From 169bcd2da359338896bcec60732fbc25a33cd8bb Mon Sep 17 00:00:00 2001 From: Jules Dejaeghere Date: Sat, 29 Jun 2024 20:27:04 +0200 Subject: [PATCH] Use FeatureValue for some BelAQI functions --- README.md | 2 +- src/open_irceline/belaqi.py | 31 ++++++++++++++++++++----------- src/open_irceline/data.py | 12 ++++++------ tests/test_belaqi.py | 6 +++--- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f4493b9..ab323bc 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ async def get_current_belaqi(): position=(50.85, 4.35) # (lat, lon) for Brussels ) - print(f"Current BelAQI index for Brussels: {result}") + print(f"Current BelAQI index for Brussels: {result.get('value')}") if __name__ == '__main__': diff --git a/src/open_irceline/belaqi.py b/src/open_irceline/belaqi.py index bb0f9a7..0069ba4 100644 --- a/src/open_irceline/belaqi.py +++ b/src/open_irceline/belaqi.py @@ -6,7 +6,7 @@ from typing import Tuple, Dict, Final from .api import IrcelineRioClient, IrcelineForecastClient -from .data import BelAqiIndex, RioFeature, ForecastFeature +from .data import BelAqiIndex, RioFeature, ForecastFeature, FeatureValue # Ratio values from Figure 2 at # https://www.irceline.be/en/air-quality/measurements/air-quality-index-november-2022/info_nov2022 @@ -117,7 +117,7 @@ def belaqi_index_hourly(pm10: float, pm25: float, o3: float, no2: float) -> BelA async def belaqi_index_rio_hourly(rio_client: IrcelineRioClient, position: Tuple[float, float], - timestamp: datetime | None = None) -> BelAqiIndex: + timestamp: datetime | None = None) -> FeatureValue: """ Get current BelAQI index value for the given position using the rio_client Raise ValueError if one or more components are not available @@ -128,25 +128,30 @@ async def belaqi_index_rio_hourly(rio_client: IrcelineRioClient, position: Tuple """ if timestamp is None: timestamp = datetime.utcnow() + + features = [RioFeature.PM10_HMEAN, RioFeature.PM25_HMEAN, RioFeature.O3_HMEAN, RioFeature.NO2_HMEAN] + components = await rio_client.get_data( timestamp=timestamp, - features=[RioFeature.PM10_HMEAN, - RioFeature.PM25_HMEAN, - RioFeature.O3_HMEAN, - RioFeature.NO2_HMEAN], + features=features, position=position ) - return belaqi_index_hourly( + ts = min([components.get(f, {}).get('timestamp') for f in features + if components.get(f, {}).get('timestamp') is not None]) + + belaqi = belaqi_index_hourly( pm10=components.get(RioFeature.PM10_HMEAN, {}).get('value', -1), pm25=components.get(RioFeature.PM25_HMEAN, {}).get('value', -1), o3=components.get(RioFeature.O3_HMEAN, {}).get('value', -1), no2=components.get(RioFeature.NO2_HMEAN, {}).get('value', -1) ) + return FeatureValue(timestamp=ts, value=belaqi) + async def belaqi_index_forecast_daily(forecast_client: IrcelineForecastClient, position: Tuple[float, float], - timestamp: date | None = None) -> Dict[date, BelAqiIndex | None]: + timestamp: date | None = None) -> Dict[date, FeatureValue]: """ Get forecasted BelAQI index value for the given position using the forecast_client. Data is downloaded for the given day and the four next days @@ -169,15 +174,19 @@ async def belaqi_index_forecast_daily(forecast_client: IrcelineForecastClient, p result = dict() - for _, day in components.keys(): + days = {day for _, day in components.keys()} + timestamps = {v.get('timestamp') for v in components.values() if v.get('timestamp') is not None} + timestamp = min(timestamps) + for day in days: try: - result[day] = belaqi_index_daily( + belaqi = belaqi_index_daily( pm10=components.get((ForecastFeature.PM10_DMEAN, day), {}).get('value', -1), pm25=components.get((ForecastFeature.PM25_DMEAN, day), {}).get('value', -1), o3=components.get((ForecastFeature.O3_MAXHMEAN, day), {}).get('value', -1) * O3_MAX_HMEAN_TO_MAX8HMEAN, no2=components.get((ForecastFeature.NO2_MAXHMEAN, day), {}).get('value', -1) * NO2_MAX_HMEAN_TO_DMEAN ) + result[day] = FeatureValue(timestamp=timestamp, value=belaqi) except (ValueError, TypeError): - result[day] = None + result[day] = FeatureValue(timestamp=timestamp, value=None) return result diff --git a/src/open_irceline/data.py b/src/open_irceline/data.py index 48f6238..94c2a10 100644 --- a/src/open_irceline/data.py +++ b/src/open_irceline/data.py @@ -37,12 +37,6 @@ class ForecastFeature(IrcelineFeature): PM25_DMEAN = 'chimere_pm25_dmean' -class FeatureValue(TypedDict): - # Timestamp at which the value was computed - timestamp: datetime | date - value: int | float | None - - class BelAqiIndex(Enum): EXCELLENT = 1 VERY_GOOD = 2 @@ -54,3 +48,9 @@ class BelAqiIndex(Enum): BAD = 8 VERY_BAD = 9 HORRIBLE = 10 + + +class FeatureValue(TypedDict): + # Timestamp at which the value was computed + timestamp: datetime | date + value: int | float | BelAqiIndex | None diff --git a/tests/test_belaqi.py b/tests/test_belaqi.py index ec7f375..cc3383c 100644 --- a/tests/test_belaqi.py +++ b/tests/test_belaqi.py @@ -231,7 +231,7 @@ async def test_belaqi_index_forecast(): assert set(result.keys()) == expected_days for v in result.values(): - assert v == BelAqiIndex.MODERATE + assert v.get('value') == BelAqiIndex.MODERATE async def test_belaqi_index_forecast_missing_day(): @@ -244,7 +244,7 @@ async def test_belaqi_index_forecast_missing_day(): expected_days = {date(2024, 6, 21) + timedelta(days=i) for i in range(5)} assert set(result.keys()) == expected_days for v in result.values(): - assert v is None + assert v.get('value') is None @freeze_time(datetime.fromisoformat("2024-06-23T12:30:09.581Z")) @@ -254,7 +254,7 @@ async def test_belaqi_index_actual(): pos = (50.55, 4.85) result = await belaqi_index_rio_hourly(client, pos) - assert result == BelAqiIndex.GOOD + assert result.get('value') == BelAqiIndex.GOOD @freeze_time(datetime.fromisoformat("2024-06-23T12:30:09.581Z"))