Skip to content

Commit b85bfe3

Browse files
AlexHilsonjacobtomlinson
authored andcommitted
Basic functionality for #5: Implement Text forecast (#25)
* Add capability to retrieve region details for #5 * Added way to request the text forecast for #5 * Clarified that current functionality is for getting raw response * Added example showing retrieval of a regional text forecast * Added regions as package to setup script * Cleaned text_forecast example
1 parent 04a2809 commit b85bfe3

File tree

7 files changed

+166
-1
lines changed

7 files changed

+166
-1
lines changed

datapoint/Manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from .Day import Day
1616
from .Timestep import Timestep
1717
from .Element import Element
18+
from .regions.RegionManager import RegionManager
19+
1820

1921
if (sys.version_info > (3, 0)):
2022
long = int
@@ -84,6 +86,8 @@ def __init__(self, api_key=""):
8486
self.sites_last_request = None
8587
self.sites_update_time = 3600
8688

89+
self.regions = RegionManager(self.api_key)
90+
8791
def __call_api(self, path, params=None):
8892
"""
8993
Call the datapoint api using the requests module

datapoint/regions/RegionManager.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import json
2+
from time import time
3+
4+
import requests
5+
6+
from datapoint.Site import Site
7+
from datapoint.regions.region_names import REGION_NAMES
8+
9+
REGIONS_BASE_URL = 'http://datapoint.metoffice.gov.uk/public/data/txt/wxfcs/regionalforecast/json'
10+
11+
class RegionManager(object):
12+
'''
13+
Datapoint Manager for national and regional text forecasts
14+
'''
15+
def __init__(self, api_key, base_url=None):
16+
self.api_key = api_key
17+
self.all_regions_path = '/sitelist'
18+
if not base_url:
19+
self.base_url = REGIONS_BASE_URL
20+
21+
# The list of regions changes infrequently so limit to requesting it
22+
# every hour.
23+
self.regions_last_update = 0
24+
self.regions_last_request = None
25+
self.regions_update_time = 3600
26+
27+
def call_api(self, path, **kwargs):
28+
'''
29+
Call datapoint api
30+
'''
31+
if 'key' not in kwargs:
32+
kwargs['key'] = self.api_key
33+
req = requests.get('{}{}'.format(self.base_url, path), params=kwargs)
34+
35+
if req.status_code != requests.codes.ok:
36+
req.raise_for_status()
37+
38+
return req.json()
39+
40+
def get_all_regions(self):
41+
'''
42+
Request a list of regions from Datapoint. Returns each Region
43+
as a Site object. Regions rarely change, so we cache the response
44+
for one hour to minimise requests to API.
45+
'''
46+
if (time() - self.regions_last_update) < self.regions_update_time:
47+
return self.regions_last_request
48+
49+
response = self.call_api(self.all_regions_path)
50+
regions = []
51+
for location in response['Locations']['Location']:
52+
region = Site()
53+
region.id = location['@id']
54+
region.region = location['@name']
55+
region.name = REGION_NAMES[location['@name']]
56+
regions.append(region)
57+
58+
self.regions_last_update = time()
59+
self.regions_last_request = regions
60+
return regions
61+
62+
def get_raw_forecast(self, region_id):
63+
'''
64+
Request unformatted forecast for a specific region_id.
65+
'''
66+
return self.call_api('/{}'.format(region_id))

datapoint/regions/__init__.py

Whitespace-only changes.

datapoint/regions/region_names.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
REGION_NAMES = {
2+
'os': 'Orkney & Shetland',
3+
'he': 'Highland & Eilean Siar',
4+
'gr': 'Grampian',
5+
'ta': 'Tayside',
6+
'st': 'Strathclyde',
7+
'dg': 'Dumfries, Galloway, Lothian',
8+
'ni': 'Northern Ireland',
9+
'yh': 'Yorkshire & the Humber',
10+
'ne': 'Northeast England',
11+
'em': 'East Midlands',
12+
'ee': 'East of England',
13+
'se': 'London & Southeast England',
14+
'nw': 'Northwest England',
15+
'wm': 'West Midlands',
16+
'sw': 'Southwest England',
17+
'wl': 'Wales',
18+
'uk': 'UK',
19+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python
2+
"""
3+
This example will print out the 30 day text forecast for a region of the UK.
4+
"""
5+
6+
import datapoint
7+
8+
# Create datapoint connection
9+
conn = datapoint.Manager(api_key="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")
10+
11+
# Get all regions and print out their details
12+
regions = conn.regions.get_all_regions()
13+
for region in regions:
14+
print (region.name, region.id, region.region)
15+
16+
# Get all forecasts for a specific region
17+
my_region = regions[0]
18+
forecast = conn.regions.get_raw_forecast(my_region.id)['RegionalFcst']
19+
20+
# Print the forecast details
21+
print 'Forecast for {} (issued at {}):'.format(my_region.name, forecast['issuedAt'])
22+
23+
sections = forecast['FcstPeriods']['Period']
24+
for section in forecast['FcstPeriods']['Period']:
25+
paragraph = []
26+
content = section['Paragraph']
27+
28+
# Some paragraphs have multiple sections
29+
if isinstance(content, dict):
30+
paragraph.append(content)
31+
else:
32+
paragraph = content
33+
34+
for line in paragraph:
35+
print '{}\n{}\n'.format(line['title'], line['$'])

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
maintainer_email='[email protected]',
6161
url='https://github.com/jacobtomlinson/datapoint-python',
6262
license='GPLv3',
63-
packages=['datapoint'],
63+
packages=['datapoint', 'datapoint.regions'],
6464
classifiers=[
6565
'Development Status :: 3 - Alpha',
6666
'Programming Language :: Python :: 2.6',

tests/integration/regions_test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
3+
from nose.tools import *
4+
from requests import HTTPError
5+
6+
import datapoint
7+
8+
9+
class TestRegions(object):
10+
def __init__(self):
11+
self.manager = datapoint.Manager(api_key=os.environ['API_KEY'])
12+
self.regions = self.manager.regions
13+
14+
def test_key(self):
15+
assert self.regions.api_key == os.environ['API_KEY']
16+
17+
def test_call_api(self):
18+
assert (
19+
u'RegionalFcst' in self.regions.call_api('/500'))
20+
assert_raises(
21+
HTTPError, self.regions.call_api, '/fake_path')
22+
assert_raises(
23+
HTTPError, self.regions.call_api, '/500', key='fake_key')
24+
25+
def test_get_all_regions(self):
26+
all_regions = self.regions.get_all_regions()
27+
sample_region = filter(lambda x: x.id == '515', all_regions)[0]
28+
assert (sample_region.name == 'UK')
29+
assert (sample_region.region == 'uk')
30+
31+
def test_get_raw_forecast(self):
32+
sample_region = self.regions.get_all_regions()[0]
33+
response = self.regions.get_raw_forecast(
34+
sample_region.id)['RegionalFcst']
35+
assert (response['regionId'] == sample_region.region)
36+
37+
# Based on what Datapoint serves at time of writing...
38+
forecast_periods = response['FcstPeriods']['Period']
39+
forecast_ids = [period['id'] for period in forecast_periods]
40+
expected_ids = ['day1to2', 'day3to5', 'day6to15', 'day16to30']
41+
assert (forecast_ids == expected_ids)

0 commit comments

Comments
 (0)