Merge pull request #4 from jdejaegh/use_feature_value_belaqi

Use FeatureValue for some BelAQI functions
This commit is contained in:
Jules 2024-06-29 20:28:35 +02:00 committed by GitHub
commit 7fe28b7783
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 21 deletions

View file

@ -67,7 +67,7 @@ async def get_current_belaqi():
position=(50.85, 4.35) # (lat, lon) for Brussels 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__': if __name__ == '__main__':

View file

@ -6,7 +6,7 @@ from datetime import datetime, date
from typing import Tuple, Dict, Final from typing import Tuple, Dict, Final
from .api import IrcelineRioClient, IrcelineForecastClient from .api import IrcelineRioClient, IrcelineForecastClient
from .data import BelAqiIndex, RioFeature, ForecastFeature from .data import BelAqiIndex, RioFeature, ForecastFeature, FeatureValue
# Ratio values from Figure 2 at # Ratio values from Figure 2 at
# https://www.irceline.be/en/air-quality/measurements/air-quality-index-november-2022/info_nov2022 # 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], 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 Get current BelAQI index value for the given position using the rio_client
Raise ValueError if one or more components are not available 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: if timestamp is None:
timestamp = datetime.utcnow() timestamp = datetime.utcnow()
features = [RioFeature.PM10_HMEAN, RioFeature.PM25_HMEAN, RioFeature.O3_HMEAN, RioFeature.NO2_HMEAN]
components = await rio_client.get_data( components = await rio_client.get_data(
timestamp=timestamp, timestamp=timestamp,
features=[RioFeature.PM10_HMEAN, features=features,
RioFeature.PM25_HMEAN,
RioFeature.O3_HMEAN,
RioFeature.NO2_HMEAN],
position=position 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), pm10=components.get(RioFeature.PM10_HMEAN, {}).get('value', -1),
pm25=components.get(RioFeature.PM25_HMEAN, {}).get('value', -1), pm25=components.get(RioFeature.PM25_HMEAN, {}).get('value', -1),
o3=components.get(RioFeature.O3_HMEAN, {}).get('value', -1), o3=components.get(RioFeature.O3_HMEAN, {}).get('value', -1),
no2=components.get(RioFeature.NO2_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], 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. 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 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() 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: try:
result[day] = belaqi_index_daily( belaqi = belaqi_index_daily(
pm10=components.get((ForecastFeature.PM10_DMEAN, day), {}).get('value', -1), pm10=components.get((ForecastFeature.PM10_DMEAN, day), {}).get('value', -1),
pm25=components.get((ForecastFeature.PM25_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, 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 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): except (ValueError, TypeError):
result[day] = None result[day] = FeatureValue(timestamp=timestamp, value=None)
return result return result

View file

@ -37,12 +37,6 @@ class ForecastFeature(IrcelineFeature):
PM25_DMEAN = 'chimere_pm25_dmean' 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): class BelAqiIndex(Enum):
EXCELLENT = 1 EXCELLENT = 1
VERY_GOOD = 2 VERY_GOOD = 2
@ -54,3 +48,9 @@ class BelAqiIndex(Enum):
BAD = 8 BAD = 8
VERY_BAD = 9 VERY_BAD = 9
HORRIBLE = 10 HORRIBLE = 10
class FeatureValue(TypedDict):
# Timestamp at which the value was computed
timestamp: datetime | date
value: int | float | BelAqiIndex | None

View file

@ -231,7 +231,7 @@ async def test_belaqi_index_forecast():
assert set(result.keys()) == expected_days assert set(result.keys()) == expected_days
for v in result.values(): for v in result.values():
assert v == BelAqiIndex.MODERATE assert v.get('value') == BelAqiIndex.MODERATE
async def test_belaqi_index_forecast_missing_day(): 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)} expected_days = {date(2024, 6, 21) + timedelta(days=i) for i in range(5)}
assert set(result.keys()) == expected_days assert set(result.keys()) == expected_days
for v in result.values(): 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")) @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) pos = (50.55, 4.85)
result = await belaqi_index_rio_hourly(client, pos) 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")) @freeze_time(datetime.fromisoformat("2024-06-23T12:30:09.581Z"))