Skip to content

Commit

Permalink
Merge pull request #3 from kipe/fmisid
Browse files Browse the repository at this point in the history
Allow usage of alternative location definers
  • Loading branch information
kipe committed Dec 21, 2020
2 parents c264952 + c54f8e9 commit 0accf0e
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 27 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,27 @@ After setting the environment variables, you can use the library without "any" i
```
from fmi import FMI
f = FMI()
# f.forecast() returns a list of Observation -objects (sorry, confusing, I know :P) for the next 36 hours
# f.forecast() returns a list of Forecast -objects
print(f.forecast())
```

### New in 1.1.0
`place` or `coordinates` are not longer required,
but they are respected if present.
This allows the usage of `fmisid` and `wmo` for definition of location,
allowing better transparency on what location is used.

You can list view a list of locations at:
https://www.ilmatieteenlaitos.fi/havaintoasemat

For example:
```
from fmi import FMI
f = FMI()
# Fetch and print observations from Lappeenranta Airport
print(f.observations(fmisid=101237))
```

Forecast icons
--------------

Expand Down
29 changes: 13 additions & 16 deletions fmi/fmi.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import os
import requests
import warnings
from .observation import Observation
from .observation import Observation, Forecast
from bs4 import BeautifulSoup


class FMI(object):
apikey = None
api_endpoint = 'https://opendata.fmi.fi/wfs'

def __init__(self, apikey=None, place=None, coordinates=None):
self.apikey = os.environ.get('FMI_APIKEY', apikey)
self.place = os.environ.get('FMI_PLACE', place)
self.coordinates = os.environ.get('FMI_COORDINATES', coordinates)
if self.apikey is not None:
if apikey is not None:
warnings.simplefilter('default')
warnings.warn('The use of FMI API key is deprecated.',
DeprecationWarning)
if self.place is None and self.coordinates is None:
raise AttributeError('FMI place or coordinates not set.')

def _parse_identifier(self, x):
identifier = x['gml:id'].split('-')[-1].lower()
Expand Down Expand Up @@ -56,7 +52,7 @@ def _parse_identifier(self, x):
return 'radiation_diffuse_accumulation', 1
return None, 1

def _parse_response(self, r):
def _parse_response(self, r, klass=Observation):
bs = BeautifulSoup(r.text, 'html.parser')

d = {}
Expand Down Expand Up @@ -84,24 +80,24 @@ def _parse_response(self, r):
d[timestamp][identifier] = value

return sorted(
[Observation(k, v) for k, v in d.items()],
[klass(k, v) for k, v in d.items()],
key=lambda x: x.time)

def get(self, storedquery_id, **params):
def get(self, storedquery_id, klass=Observation, **params):
query_params = {
'request': 'getFeature',
'storedquery_id': storedquery_id,
}
if self.coordinates is None:
if self.place is not None:
query_params['place'] = self.place
else:
elif self.coordinates is not None:
query_params['latlon'] = self.coordinates
query_params.update(params)

return self._parse_response(
requests.get(
self.api_endpoint,
params=query_params))
request = requests.get(self.api_endpoint, params=query_params)
request.raise_for_status()

return self._parse_response(request, klass=klass)

def observations(self, **params):
return self.get(
Expand All @@ -115,4 +111,5 @@ def forecast(self, model='hirlam', **params):
return self.get(
'fmi::forecast::%s::surface::point::timevaluepair' % (model),
maxlocations=1,
**params)
**params,
klass=Forecast)
11 changes: 9 additions & 2 deletions fmi/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ def __init__(self, timestamp, point):
'radiation_diffuse_accumulation', None)

def __repr__(self):
return '<Observation: %s - %.1f C>' % (
self.time.isoformat(), self.temperature)
return '<%s: %s - %.1f C>' % (
self.__class__.__name__,
self.time.isoformat(),
self.temperature,
)

@property
def icon(self):
Expand Down Expand Up @@ -79,3 +82,7 @@ def as_influx_measurement(self):
if key not in ['time'] and value is not None
}
}


class Forecast(Observation):
pass
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

setup(
name='fmi_weather',
version='1.0.0',
version='1.1.0',
description='FMI weather data fetcher',
author='Kimmo Huoman',
author_email='kipenroskaposti@gmail.com',
Expand Down
6 changes: 2 additions & 4 deletions tests/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@

class TestForecast(unittest.TestCase):
def test_lappeenranta(self):
now = datetime.now(tz=tzutc())

f = FMI(place='Lappeenranta')
for point in f.forecast():
assert point.time > now
assert point.time > datetime.now(tz=tzutc())
assert isinstance(point.temperature, float)

f = FMI(coordinates='%.03f,%.03f' % (61.058876, 28.186262))
for point in f.forecast():
assert point.time > now
assert point.time > datetime.now(tz=tzutc())
assert isinstance(point.temperature, float)
8 changes: 5 additions & 3 deletions tests/test_observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

class TestObservations(unittest.TestCase):
def test_lappeenranta(self):
now = datetime.now(tz=tzutc())

f = FMI(place='Lappeenranta')
for point in f.observations():
assert point.time < now
assert point.time < datetime.now(tz=tzutc())
assert isinstance(point.temperature, float)

for point in f.observations(fmisid=101237):
assert point.time < datetime.now(tz=tzutc())
assert isinstance(point.temperature, float)

0 comments on commit 0accf0e

Please sign in to comment.