From 5979ab4b57a3f8738bcb9f4dbac54812be5700b7 Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Sun, 26 Sep 2021 09:08:34 -0400 Subject: [PATCH 1/7] add observation constants --- pynws/const.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pynws/const.py b/pynws/const.py index affd347..fed80f8 100644 --- a/pynws/const.py +++ b/pynws/const.py @@ -59,3 +59,25 @@ "blizzard": "Blizzard", "fog": "Fog/mist", } + +OBS_TEMPERATURE = "temperature" +OBS_BARO_PRESSURE = "barometricPressure" +OBS_SEA_PRESSURE = "seaLevelPressure" +OBS_REL_HUMIDITY = "relativeHumidity" +OBS_WIND_SPEED = "windSpeed" +OBS_WIND_DIRECTION = "windDirection" +OBS_VISIBILITY = "visibility" +OBS_ELEVATION = "elevation" +OBS_DESCRIPTION = "textDescription" +OBS_DEWPOINT = "dewpoint" +OBS_WIND_GUST = "windGust" +OBS_STATION = "station" +OBS_TIMESTAMP = "timestamp" +OBS_ICON = "icon" +OBS_MAX_TEMP_24H = "maxTemperatureLast24Hours" +OBS_MIN_TEMP_24H = "minTemperatureLast24Hours" +OBS_PRECIPITATION_1H = "precipitationLastHour" +OBS_PRECIPITATION_3H = "precipitationLast3Hours" +OBS_PRECIPITATION_6H = "precipitationLast6Hours" +OBS_WIND_CHILL = "windChill" +OBS_HEAT_INDEX = "heatIndex" From da6bdbb0499c0a90ced7bd8ea5b25a4583b660ea Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Fri, 1 Oct 2021 19:58:42 -0400 Subject: [PATCH 2/7] more constants, use them --- pynws/const.py | 3 ++ pynws/simple_nws.py | 81 ++++++++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/pynws/const.py b/pynws/const.py index fed80f8..478db05 100644 --- a/pynws/const.py +++ b/pynws/const.py @@ -81,3 +81,6 @@ OBS_PRECIPITATION_6H = "precipitationLast6Hours" OBS_WIND_CHILL = "windChill" OBS_HEAT_INDEX = "heatIndex" +# derived observations +OBS_ICON_TIME = "iconTime" +OBS_ICON_WEATHER = "iconWeather" diff --git a/pynws/simple_nws.py b/pynws/simple_nws.py index 29d29d5..bfaecb7 100644 --- a/pynws/simple_nws.py +++ b/pynws/simple_nws.py @@ -4,7 +4,29 @@ from metar import Metar -from .const import ALERT_ID, API_WEATHER_CODE +from .const import (ALERT_ID, API_WEATHER_CODE, OBS_TEMPERATURE, +OBS_BARO_PRESSURE, +OBS_SEA_PRESSURE, +OBS_REL_HUMIDITY, +OBS_WIND_SPEED, +OBS_WIND_DIRECTION, +OBS_VISIBILITY, +OBS_ELEVATION, +OBS_DESCRIPTION, +OBS_DEWPOINT, +OBS_WIND_GUST, +OBS_STATION, +OBS_TIMESTAMP, +OBS_ICON, +OBS_ICON_TIME, +OBS_ICON_WEATHER, +OBS_MAX_TEMP_24H, +OBS_MIN_TEMP_24H, +OBS_PRECIPITATION_1H, +OBS_PRECIPITATION_3H, +OBS_PRECIPITATION_6H, +OBS_WIND_CHILL, +OBS_HEAT_INDEX,) from .nws import Nws WIND_DIRECTIONS = [ @@ -56,27 +78,27 @@ def m_p_s_to_km_p_hr(m_p_s): WIND = {name: idx * 360 / 16 for idx, name in enumerate(WIND_DIRECTIONS)} OBSERVATIONS = { - "temperature": ["temp", "C", None], - "barometricPressure": None, - "seaLevelPressure": ["press", "HPA", 100], - "relativeHumidity": None, - "windSpeed": ["wind_speed", "MPS", 3.6], - "windDirection": ["wind_dir", None, None], - "visibility": ["vis", "M", None], - "elevation": None, - "textDescription": None, - "dewpoint": None, - "windGust": None, - "station": None, - "timestamp": None, - "icon": None, - "maxTemperatureLast24Hours": None, - "minTemperatureLast24Hours": None, - "precipitationLastHour": None, - "precipitationLast3Hours": None, - "precipitationLast6Hours": None, - "windChill": None, - "heatIndex": None, + OBS_TEMPERATURE: ["temp", "C", None], + OBS_BARO_PRESSURE: None, + OBS_SEA_PRESSURE: ["press", "HPA", 100], + OBS_REL_HUMIDITY: None, + OBS_WIND_SPEED: ["wind_speed", "MPS", 3.6], + OBS_WIND_DIRECTION: ["wind_dir", None, None], + OBS_VISIBILITY: ["vis", "M", None], + OBS_ELEVATION: None, + OBS_DESCRIPTION: None, + OBS_DEWPOINT: None, + OBS_WIND_GUST: None, + OBS_STATION: None, + OBS_TIMESTAMP: None, + OBS_ICON: None, + OBS_MAX_TEMP_24H: None, + OBS_MIN_TEMP_24H: None, + OBS_PRECIPITATION_1H: None, + OBS_PRECIPITATION_3H: None, + OBS_PRECIPITATION_6H: None, + OBS_WIND_CHILL: None, + OBS_HEAT_INDEX: None, } @@ -85,8 +107,7 @@ def convert_weather(weather): return [(API_WEATHER_CODE.get(w[0], w[0]), w[1]) for w in weather] -def parse_icon(icon): - """ +def parse_icon(icon): """ Parse icon url to NWS weather codes. Example: @@ -273,13 +294,13 @@ def observation(self): data[obs] = met_prop.value() if met[2] is not None: data[obs] = data[obs] * met[2] - if data.get("icon"): - time, weather = parse_icon(data["icon"]) - data["iconTime"] = time - data["iconWeather"] = convert_weather(weather) + if data.get(OBS_ICON): + time, weather = parse_icon(data[OBS_ICON]) + data[OBS_ICON_TIME] = time + data[OBS_ICON_WEATHER] = convert_weather(weather) else: - data["iconTime"] = None - data["iconWeather"] = None + data[OBS_ICON_TIME] = None + data[OBS_ICON_WEATHER] = None return data @property From d6462a095622b82f0e0a337441b49de76189a715 Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Sun, 3 Oct 2021 09:58:00 -0400 Subject: [PATCH 3/7] fix docstring --- pynws/simple_nws.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynws/simple_nws.py b/pynws/simple_nws.py index bfaecb7..6859c60 100644 --- a/pynws/simple_nws.py +++ b/pynws/simple_nws.py @@ -107,7 +107,8 @@ def convert_weather(weather): return [(API_WEATHER_CODE.get(w[0], w[0]), w[1]) for w in weather] -def parse_icon(icon): """ +def parse_icon(icon): + """ Parse icon url to NWS weather codes. Example: From 79ca4a470116cb985f4147c996e527a29448258d Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Mon, 22 Nov 2021 20:40:35 -0500 Subject: [PATCH 4/7] more observation constants --- pynws/const.py | 5 +++++ pynws/simple_nws.py | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pynws/const.py b/pynws/const.py index 478db05..5d86582 100644 --- a/pynws/const.py +++ b/pynws/const.py @@ -81,6 +81,11 @@ OBS_PRECIPITATION_6H = "precipitationLast6Hours" OBS_WIND_CHILL = "windChill" OBS_HEAT_INDEX = "heatIndex" +OBS_RAW_MESSAGE = "rawMessage" + +OBS_ITEM_VALUE = "value" +OBS_ITEM_UNIT_CODE = "unitCode" + # derived observations OBS_ICON_TIME = "iconTime" OBS_ICON_WEATHER = "iconWeather" diff --git a/pynws/simple_nws.py b/pynws/simple_nws.py index 034d402..f92e291 100644 --- a/pynws/simple_nws.py +++ b/pynws/simple_nws.py @@ -26,7 +26,12 @@ OBS_PRECIPITATION_3H, OBS_PRECIPITATION_6H, OBS_WIND_CHILL, -OBS_HEAT_INDEX,) +OBS_HEAT_INDEX, +OBS_RAW_MESSAGE, +OBS_ITEM_VALUE, +OBS_ITEM_UNIT_CODE, + +) from .nws import Nws WIND_DIRECTIONS = [ @@ -175,7 +180,7 @@ async def set_station(self, station=None): @staticmethod def extract_metar(obs): """Return parsed metar if available.""" - metar_msg = obs.get("rawMessage") + metar_msg = obs.get(OBS_RAW_MESSAGE) if metar_msg: try: metar_obs = Metar.Metar(metar_msg) @@ -192,7 +197,7 @@ async def update_observation(self, limit=0, start_time=None): return None self._observation = sorted( obs, - key=lambda item: self.extract_observation_value(item, "timestamp"), + key=lambda item: self.extract_observation_value(item, OBS_TIMESTAMP), reverse=True, ) self._metar_obs = [self.extract_metar(iobs) for iobs in self._observation] @@ -272,10 +277,10 @@ def extract_observation_value(observation, value): if obs_value is None: return None if isinstance(observation[value], dict): - obs_sub_value = observation[value].get("value") + obs_sub_value = observation[value].get(OBS_ITEM_VALUE) if obs_sub_value is None: return None - return float(obs_sub_value), observation[value].get("unitCode") + return float(obs_sub_value), observation[value].get(OBS_ITEM_UNIT_CODE) return observation[value] @property From 73d4d7df2eae85699fb6f40ef0f238f862df623a Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Mon, 22 Nov 2021 20:41:27 -0500 Subject: [PATCH 5/7] black --- pynws/simple_nws.py | 56 +++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/pynws/simple_nws.py b/pynws/simple_nws.py index f92e291..d275945 100644 --- a/pynws/simple_nws.py +++ b/pynws/simple_nws.py @@ -4,33 +4,35 @@ from metar import Metar -from .const import (ALERT_ID, API_WEATHER_CODE, OBS_TEMPERATURE, -OBS_BARO_PRESSURE, -OBS_SEA_PRESSURE, -OBS_REL_HUMIDITY, -OBS_WIND_SPEED, -OBS_WIND_DIRECTION, -OBS_VISIBILITY, -OBS_ELEVATION, -OBS_DESCRIPTION, -OBS_DEWPOINT, -OBS_WIND_GUST, -OBS_STATION, -OBS_TIMESTAMP, -OBS_ICON, -OBS_ICON_TIME, -OBS_ICON_WEATHER, -OBS_MAX_TEMP_24H, -OBS_MIN_TEMP_24H, -OBS_PRECIPITATION_1H, -OBS_PRECIPITATION_3H, -OBS_PRECIPITATION_6H, -OBS_WIND_CHILL, -OBS_HEAT_INDEX, -OBS_RAW_MESSAGE, -OBS_ITEM_VALUE, -OBS_ITEM_UNIT_CODE, - +from .const import ( + ALERT_ID, + API_WEATHER_CODE, + OBS_BARO_PRESSURE, + OBS_DESCRIPTION, + OBS_DEWPOINT, + OBS_ELEVATION, + OBS_HEAT_INDEX, + OBS_ICON, + OBS_ICON_TIME, + OBS_ICON_WEATHER, + OBS_ITEM_UNIT_CODE, + OBS_ITEM_VALUE, + OBS_MAX_TEMP_24H, + OBS_MIN_TEMP_24H, + OBS_PRECIPITATION_1H, + OBS_PRECIPITATION_3H, + OBS_PRECIPITATION_6H, + OBS_RAW_MESSAGE, + OBS_REL_HUMIDITY, + OBS_SEA_PRESSURE, + OBS_STATION, + OBS_TEMPERATURE, + OBS_TIMESTAMP, + OBS_VISIBILITY, + OBS_WIND_CHILL, + OBS_WIND_DIRECTION, + OBS_WIND_GUST, + OBS_WIND_SPEED, ) from .nws import Nws From 1d033ac91518ecd2acbb115c0f30959d48c9fb40 Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Mon, 22 Nov 2021 21:00:43 -0500 Subject: [PATCH 6/7] add to tests --- tests/test_simple_nws.py | 122 ++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/tests/test_simple_nws.py b/tests/test_simple_nws.py index 9f9d3b6..c444dd6 100644 --- a/tests/test_simple_nws.py +++ b/tests/test_simple_nws.py @@ -2,6 +2,20 @@ import pytest from pynws import NwsError, SimpleNWS +from pynws.const import ( + OBS_DEWPOINT, + + OBS_REL_HUMIDITY, + OBS_SEA_PRESSURE, + OBS_TEMPERATURE, + OBS_VISIBILITY, + OBS_WIND_DIRECTION, + OBS_WIND_GUST, + OBS_WIND_SPEED, + OBS_ICON_WEATHER, + OBS_ICON_TIME, +) + from tests.helpers import data_return_function, setup_app LATLON = (0, 0) @@ -48,17 +62,17 @@ async def test_nws_observation(aiohttp_client, loop, mock_urls, observation_json await nws.update_observation() observation = nws.observation assert observation - assert observation["temperature"] == 10 - assert observation["dewpoint"] == 10 - assert observation["relativeHumidity"] == 10 - assert observation["windDirection"] == 10 - assert observation["visibility"] == 10000 - assert observation["seaLevelPressure"] == 100000 - assert observation["windSpeed"] == 36 # converted to km_gr - assert observation["iconTime"] == "day" - assert observation["windGust"] == 36 # same - assert observation["iconWeather"][0][0] == "A few clouds" - assert observation["iconWeather"][0][1] is None + assert observation[OBS_TEMPERATURE] == 10 + assert observation[OBS_DEWPOINT] == 10 + assert observation[OBS_REL_HUMIDITY] == 10 + assert observation[OBS_WIND_DIRECTION] == 10 + assert observation[OBS_VISIBILITY] == 10000 + assert observation[OBS_SEA_PRESSURE] == 100000 + assert observation[OBS_WIND_SPEED] == 36 # converted to km_gr + assert observation[OBS_ICON_TIME] == "day" + assert observation[OBS_WIND_GUST] == 36 # same + assert observation[OBS_ICON_WEATHER][0][0] == "A few clouds" + assert observation[OBS_ICON_WEATHER][0][1] is None async def test_nws_observation_units(aiohttp_client, loop, mock_urls): @@ -69,9 +83,9 @@ async def test_nws_observation_units(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation assert observation - assert round(observation["temperature"], 1) == -12.2 - assert observation["windSpeed"] == 10 # converted to km_gr - assert observation["windGust"] == 10 + assert round(observation[OBS_TEMPERATURE], 1) == -12.2 + assert observation[OBS_WIND_SPEED] == 10 # converted to km_gr + assert observation[OBS_WIND_GUST] == 10 async def test_nws_observation_metar(aiohttp_client, loop, mock_urls): @@ -82,14 +96,14 @@ async def test_nws_observation_metar(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] == 25.6 - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] == 350.0 - assert observation["visibility"] == 16093.44 - assert round(observation["seaLevelPressure"]) == 101761 - assert round(observation["windSpeed"], 2) == 9.26 - assert observation["windGust"] is None + assert observation[OBS_TEMPERATURE] == 25.6 + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] == 350.0 + assert observation[OBS_VISIBILITY] == 16093.44 + assert round(observation[OBS_SEA_PRESSURE]) == 101761 + assert round(observation[OBS_WIND_SPEED], 2) == 9.26 + assert observation[OBS_WIND_GUST] is None async def test_nws_observation_metar_noparse(aiohttp_client, loop, mock_urls): @@ -99,7 +113,7 @@ async def test_nws_observation_metar_noparse(aiohttp_client, loop, mock_urls): await nws.set_station(STATION) await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None + assert observation[OBS_TEMPERATURE] is None async def test_nws_observation_empty(aiohttp_client, loop, mock_urls): @@ -110,16 +124,16 @@ async def test_nws_observation_empty(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] is None - assert observation["visibility"] is None - assert observation["seaLevelPressure"] is None - assert observation["windSpeed"] is None - assert observation["windGust"] is None - assert observation["iconTime"] is None - assert observation["iconWeather"] is None + assert observation[OBS_TEMPERATURE] is None + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] is None + assert observation[OBS_VISIBILITY] is None + assert observation[OBS_SEA_PRESSURE] is None + assert observation[OBS_WIND_SPEED] is None + assert observation[OBS_WIND_GUST] is None + assert observation[OBS_ICON_TIME] is None + assert observation[OBS_ICON_WEATHER] is None async def test_nws_observation_noprop(aiohttp_client, loop, mock_urls): @@ -141,16 +155,16 @@ async def test_nws_observation_missing_value(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] is None - assert observation["visibility"] is None - assert observation["seaLevelPressure"] is None - assert observation["windSpeed"] is None - assert observation["windGust"] is None - assert observation["iconTime"] is None - assert observation["iconWeather"] is None + assert observation[OBS_TEMPERATURE] is None + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] is None + assert observation[OBS_VISIBILITY] is None + assert observation[OBS_SEA_PRESSURE] is None + assert observation[OBS_WIND_SPEED] is None + assert observation[OBS_WIND_GUST] is None + assert observation[OBS_ICON_TIME] is None + assert observation[OBS_ICON_WEATHER] is None @freeze_time("2019-10-13T14:30:00-04:00") @@ -161,10 +175,10 @@ async def test_nws_forecast(aiohttp_client, loop, mock_urls): await nws.update_forecast() forecast = nws.forecast - assert forecast[0]["iconWeather"][0][0] == "Thunderstorm (high cloud cover)" - assert forecast[0]["iconWeather"][0][1] == 40 - assert forecast[0]["iconWeather"][1][0] == "Overcast" - assert forecast[0]["iconWeather"][1][1] is None + assert forecast[0][OBS_ICON_WEATHER][0][0] == "Thunderstorm (high cloud cover)" + assert forecast[0][OBS_ICON_WEATHER][0][1] == 40 + assert forecast[0][OBS_ICON_WEATHER][1][0] == "Overcast" + assert forecast[0][OBS_ICON_WEATHER][1][1] is None assert forecast[0]["windSpeedAvg"] == 10 assert forecast[0]["windBearing"] == 180 @@ -176,13 +190,13 @@ async def test_nws_forecast_discard_stale(aiohttp_client, loop, mock_urls): nws = SimpleNWS(*LATLON, USERID, client, filter_forecast=True) await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 77 + assert forecast[0][OBS_TEMPERATURE] == 77 nws = SimpleNWS(*LATLON, USERID, client, filter_forecast=False) await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 78 + assert forecast[0][OBS_TEMPERATURE] == 78 @freeze_time("2019-10-14T20:30:00-04:00") @@ -193,7 +207,7 @@ async def test_nws_forecast_hourly(aiohttp_client, loop, mock_urls): await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 78 + assert forecast[0][OBS_TEMPERATURE] == 78 @freeze_time("2019-10-13T14:30:00-04:00") @@ -204,10 +218,10 @@ async def test_nws_forecast_strings(aiohttp_client, loop, mock_urls): await nws.update_forecast() forecast = nws.forecast - assert forecast[0]["iconWeather"][0][0] == "Thunderstorm (high cloud cover)" - assert forecast[0]["iconWeather"][0][1] == 40 - assert forecast[0]["iconWeather"][1][0] == "Overcast" - assert forecast[0]["iconWeather"][1][1] is None + assert forecast[0][OBS_ICON_WEATHER][0][0] == "Thunderstorm (high cloud cover)" + assert forecast[0][OBS_ICON_WEATHER][0][1] == 40 + assert forecast[0][OBS_ICON_WEATHER][1][0] == "Overcast" + assert forecast[0][OBS_ICON_WEATHER][1][1] is None assert forecast[0]["windSpeedAvg"] == 10 assert forecast[0]["windBearing"] == 180 From e83f19fceb107f7160c29a8769386ff247a53755 Mon Sep 17 00:00:00 2001 From: Matthew Flamm Date: Mon, 22 Nov 2021 21:00:58 -0500 Subject: [PATCH 7/7] black --- tests/test_simple_nws.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_simple_nws.py b/tests/test_simple_nws.py index c444dd6..ac70348 100644 --- a/tests/test_simple_nws.py +++ b/tests/test_simple_nws.py @@ -4,7 +4,6 @@ from pynws import NwsError, SimpleNWS from pynws.const import ( OBS_DEWPOINT, - OBS_REL_HUMIDITY, OBS_SEA_PRESSURE, OBS_TEMPERATURE,