Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions pyairnow/historical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'''Retrieve Historical Air Quality'''
from datetime import date as date_, datetime
from typing import Callable, Coroutine, Optional, Union


class Historical:
'''
Class to retrieve historical air quality by zip code or by latitude and
longitude.
'''
def __init__(self, request: Callable[..., Coroutine]) -> None:
self._request = request

async def zipCode(
self,
zipCode: str,
*,
date: Optional[Union[date_, datetime, str]] = None,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think even though the API allows a call with no date and returns an empty set of observations, it makes more sense for a historical API to make this required and do our own check for presence in the code. This seems to work a bit differently than the forecast which defaults to today if the parameter isn't sent.

distance: Optional[int] = None
) -> list:
'''Request current observation for zip code'''
params: dict = dict(zipCode=zipCode)
'''Airnow needs T00-0000 appended to the date parameter for historical calls'''
if date and isinstance(date, str):
y, m, d = date.split('-')
params['date'] = date_(int(y), int(m), int(d)).isoformat() + "T00-0000"
elif date and isinstance(date, datetime):
params['date'] = date.date().isoformat() + "T00-0000"
elif date and isinstance(date, date_):
params['date'] = date.isoformat() + "T00-0000"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is actually an odd form of extended ISO 8601 timestamp where the T00 is the time with only hours (minutes and seconds may be omitted if they are zero) and -0000 is the UTC offset. I think a better way to do it is to get the argument into a datetime, ensure it has a time zone offset set, then use strftime to put it into the correct %Y-%m-%dT%H%z format instead of relying on isoformat and hard-coded strings.

I probably should have done something similar originally in the Forecast module, although interestingly the Forecast API endpoint does not like the T00-0000 so the EPA is using different parsers on the backend.

if distance:
params['distance'] = distance

return await self._request(
'aq/observation/zipCode/historical',
params=params
)

async def latLong(
self,
latitude: Optional[Union[float, str]] = None,
longitude: Optional[Union[float, str]] = None,
*,
date: Optional[Union[date_, datetime, str]] = None,
distance: Optional[int] = None,
) -> None:
'''Request current observation for latitude/longitude'''
params: dict = dict(
latitude=str(latitude),
longitude=str(longitude),
)
if date and isinstance(date, str):
y, m, d = date.split('-')
params['date'] = date_(int(y), int(m), int(d)).isoformat() + "T00-0000"
elif date and isinstance(date, datetime):
params['date'] = date.date().isoformat() + "T00-0000"
elif date and isinstance(date, date_):
params['date'] = date.isoformat() + "T00-0000"
if distance:
params['distance'] = distance

return await self._request(
'aq/observation/latLong/historical',
params=params
)