-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathservice.py
More file actions
124 lines (100 loc) · 3.62 KB
/
service.py
File metadata and controls
124 lines (100 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# Copyright (c) Sunlight Labs, 2012 under the terms and conditions
# of the LICENSE file.
"""
.. module:: sunlight.service
:synopsis: Sunlight API Superclass
Base service class. All API classes (such as say -
:class:`sunlight.services.openstates.OpenStates`) inherit from this.
"""
import sys
import datetime
import sunlight.config
import sunlight.errors
from sunlight.cache import response_cache
if sys.version_info[0] >= 3:
from urllib.parse import urlencode, quote
from urllib.request import urlopen
from urllib.error import HTTPError
_str_type = str
else:
from urllib import urlencode, quote
from urllib2 import urlopen
from urllib2 import HTTPError
_str_type = basestring
def safe_encode(kwargs):
kwargs = kwargs.copy()
for k, v in kwargs.items():
if isinstance(v, _str_type):
kwargs[k] = v.encode('utf8')
return urlencode(kwargs)
class Service(object):
"""
Base class for all the API implementations, as well as a bunch of common
code on how to actually fetch text over the network.
"""
is_pageable = False
@response_cache
def get(self, top_level_object, **kwargs):
"""
Get some data from the network - this is where we actually fetch
something and make a request.
.. warning:: Be sure that API_KEY was set before calling this method.
This will throw a :class:`sunlight.errors.NoAPIKeyException` if
the API_KEY is not set.
args:
``top_level_object`` (str): Thing to query for (such as say,
"bills" for OpenStates )
kwargs:
These arguments will be passed to the underlying API implementation
to help create a query. Validation will happen down below, and
on a per-API level.
"""
if not sunlight.config.API_KEY:
raise sunlight.errors.NoAPIKeyException(
"Warning: Missing API Key. please visit " +
sunlight.config.API_SIGNUP_PAGE +
" to register for a key."
)
top_level_object = list(map(quote, top_level_object))
url = self._get_url(top_level_object, sunlight.config.API_KEY,
**kwargs)
try:
r = urlopen(url)
return_data = r.read().decode('utf8')
return self._decode_response(return_data)
except HTTPError as e:
message = e.read()
code = e.getcode()
ex = sunlight.errors.BadRequestException("Error (%s) -- %s" % (
code, message
))
ex.url = e.geturl()
ex.message = message
ex.code = code
raise ex
class EntityList(list):
"""
EntityList provides an iterable of API entities along with a _meta
dictionary that contains information about the query results. This could
include result count and pagination details.
"""
def __init__(self, data=[], meta=None):
list.__init__(self, data)
self._meta = meta
class EntityDict(dict):
"""
EntityDict provides a dictionary representation of an API entity along
with a _meta dictionary that contains information about the query results.
This could include result count and pagination details.
"""
def __init__(self, data={}, meta=None):
dict.__init__(self, data)
self._meta = meta
def datetime_parser(dct):
for k, v in dct.items():
for dte_fmt in ("%Y-%m-%d", "%Y-%m-%dT%H:%M:%SZ"):
try:
dct[k] = datetime.datetime.strptime(v, dte_fmt)
except Exception:
pass
return dct