From 83c2c6aa3ad6dfd2218ff2aeb35287ac996514fe Mon Sep 17 00:00:00 2001 From: Conor Brady Date: Tue, 17 Mar 2026 11:36:00 -0700 Subject: [PATCH 1/3] Filter for specific station types --- .../packages/wps-wf1/src/wps_wf1/parsers.py | 35 ++++++++++++------- .../wps-wf1/src/wps_wf1/tests/test_parsers.py | 28 ++++++++++++--- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/backend/packages/wps-wf1/src/wps_wf1/parsers.py b/backend/packages/wps-wf1/src/wps_wf1/parsers.py index ecb7a8ef1..fea8b9ba6 100644 --- a/backend/packages/wps-wf1/src/wps_wf1/parsers.py +++ b/backend/packages/wps-wf1/src/wps_wf1/parsers.py @@ -4,26 +4,16 @@ from datetime import datetime, timezone from typing import Generator, List -from wps_shared.schemas.sfms import SFMSDailyActual -from wps_wf1.ecodivisions.ecodivision_seasons import EcodivisionSeasons - -from wps_wf1.util import ( - compute_dewpoint, - get_zone_code_prefix, - is_station_fire_zone_valid, - is_station_valid, -) -from wps_wf1.validation import get_valid_flags - -from wps_shared.schemas.fba import FireCenterStation, FireCentre from wps_shared.db.models.forecasts import NoonForecast +from wps_shared.db.models.observations import HourlyActual +from wps_shared.schemas.fba import FireCenterStation, FireCentre from wps_shared.schemas.morecast_v2 import ( StationDailyFromWF1, WeatherDeterminate, WeatherIndeterminate, ) -from wps_shared.db.models.observations import HourlyActual from wps_shared.schemas.observations import WeatherReading +from wps_shared.schemas.sfms import SFMSDailyActual from wps_shared.schemas.stations import ( FireZone, StationFireCentre, @@ -33,6 +23,14 @@ WFWXWeatherStation, ) +from wps_wf1.ecodivisions.ecodivision_seasons import EcodivisionSeasons +from wps_wf1.util import ( + compute_dewpoint, + get_zone_code_prefix, + is_station_fire_zone_valid, + is_station_valid, +) +from wps_wf1.validation import get_valid_flags logger = logging.getLogger(__name__) @@ -349,8 +347,19 @@ def sfms_daily_actuals_mapper( sfms_daily_actuals: List[SFMSDailyActual] = [] for raw_daily in raw_dailies: station_data = raw_daily.get("stationData") + site_type_id = (station_data or {}).get("siteType", {}).get("id") if ( is_station_valid(station_data) + and site_type_id + in ( + "HUB_STN", + "WXSTN_GOES", + "WXSTN_MB", + "WXSTN_TEL", + "WXSTN_CELL", + "WXSTN_UHF", + "EXTERNAL", + ) and raw_daily.get("recordType").get("id") == WF1RecordTypeEnum.ACTUAL.value ): station_code = station_data.get("stationCode") diff --git a/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py b/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py index e1de4080e..e93ea493b 100644 --- a/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py +++ b/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py @@ -1,11 +1,11 @@ -from datetime import datetime, timezone import math -import pytest -from wps_wf1.parsers import parse_hourly_actual, sfms_daily_actuals_mapper +from datetime import datetime, timezone +import pytest from wps_shared.db.models.observations import HourlyActual from wps_shared.schemas.sfms import SFMSDailyActual from wps_shared.schemas.stations import WFWXWeatherStation +from wps_wf1.parsers import parse_hourly_actual, sfms_daily_actuals_mapper def _make_station(code, lat=49.0, lon=-123.0, elevation=100): @@ -21,7 +21,13 @@ def _make_station(code, lat=49.0, lon=-123.0, elevation=100): def _make_raw_daily( - station_code, record_type="ACTUAL", status="ACTIVE", lat=49.0, lon=-123.0, **weather_fields + station_code, + record_type="ACTUAL", + status="ACTIVE", + lat=49.0, + lon=-123.0, + site_type="WXSTN_TEL", + **weather_fields, ): defaults = { "temperature": None, @@ -35,6 +41,7 @@ def _make_raw_daily( "stationData": { "stationCode": station_code, "stationStatus": {"id": status}, + "siteType": {"id": site_type}, "latitude": lat, "longitude": lon, }, @@ -177,6 +184,19 @@ def test_filters_inactive_station(self): assert sfms_daily_actuals_mapper([raw], [station]) == [] + def test_filters_invalid_site_type(self): + station = _make_station(100) + raw = _make_raw_daily(100, site_type="UNKNOWN_TYPE", temperature=20.0) + + assert sfms_daily_actuals_mapper([raw], [station]) == [] + + def test_filters_missing_site_type(self): + station = _make_station(100) + raw = _make_raw_daily(100, temperature=20.0) + del raw["stationData"]["siteType"] + + assert sfms_daily_actuals_mapper([raw], [station]) == [] + def test_filters_station_with_null_coordinates(self): station = _make_station(100) raw = _make_raw_daily(100, lat=None, lon=None, temperature=20.0) From 0836af1414fde7aaf511ef705d084f7ed433591c Mon Sep 17 00:00:00 2001 From: Conor Brady Date: Tue, 17 Mar 2026 11:45:40 -0700 Subject: [PATCH 2/3] Only active stations --- backend/packages/wps-wf1/src/wps_wf1/parsers.py | 5 ++++- backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/packages/wps-wf1/src/wps_wf1/parsers.py b/backend/packages/wps-wf1/src/wps_wf1/parsers.py index fea8b9ba6..2f1228bea 100644 --- a/backend/packages/wps-wf1/src/wps_wf1/parsers.py +++ b/backend/packages/wps-wf1/src/wps_wf1/parsers.py @@ -347,9 +347,12 @@ def sfms_daily_actuals_mapper( sfms_daily_actuals: List[SFMSDailyActual] = [] for raw_daily in raw_dailies: station_data = raw_daily.get("stationData") - site_type_id = (station_data or {}).get("siteType", {}).get("id") + station_data_or_empty = station_data or {} + site_type_id = station_data_or_empty.get("siteType", {}).get("id") + station_status_id = station_data_or_empty.get("stationStatus", {}).get("id") if ( is_station_valid(station_data) + and station_status_id == "ACTIVE" and site_type_id in ( "HUB_STN", diff --git a/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py b/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py index e93ea493b..a96310769 100644 --- a/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py +++ b/backend/packages/wps-wf1/src/wps_wf1/tests/test_parsers.py @@ -184,6 +184,13 @@ def test_filters_inactive_station(self): assert sfms_daily_actuals_mapper([raw], [station]) == [] + @pytest.mark.parametrize("status", ["TEST", "PROJECT"]) + def test_filters_non_active_station(self, status): + station = _make_station(100) + raw = _make_raw_daily(100, status=status, temperature=20.0) + + assert sfms_daily_actuals_mapper([raw], [station]) == [] + def test_filters_invalid_site_type(self): station = _make_station(100) raw = _make_raw_daily(100, site_type="UNKNOWN_TYPE", temperature=20.0) From 6c5b16ac2c01f9221ebf0551ee8e4e0ef0345c52 Mon Sep 17 00:00:00 2001 From: Conor Brady Date: Tue, 17 Mar 2026 13:17:32 -0700 Subject: [PATCH 3/3] add explanatory comment --- backend/packages/wps-wf1/src/wps_wf1/parsers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/packages/wps-wf1/src/wps_wf1/parsers.py b/backend/packages/wps-wf1/src/wps_wf1/parsers.py index 2f1228bea..eb62f26e7 100644 --- a/backend/packages/wps-wf1/src/wps_wf1/parsers.py +++ b/backend/packages/wps-wf1/src/wps_wf1/parsers.py @@ -353,6 +353,8 @@ def sfms_daily_actuals_mapper( if ( is_station_valid(station_data) and station_status_id == "ACTIVE" + # Site types match those used to define APP_WF1_WEATHER.STATION_BC_ACTIVE_REPORTING_VW, + # the station source for legacy SFMS. and site_type_id in ( "HUB_STN",