""" Python library for interacting with the Sunlight Labs API.
The Sunlight Labs API (http://services.sunlightlabs.com/api/) provides basic
legislator data, conversion between ids, and zipcode-district lookups.
"""
__author__ = "James Turk (jturk@sunlightfoundation.com)"
__version__ = "0.5.0"
__copyright__ = "Copyright (c) 2009 Sunlight Labs"
__license__ = "BSD"
import urllib, urllib2
import warnings
try:
import json
except ImportError:
import simplejson as json
class SunlightApiError(Exception):
""" Exception for Sunlight API errors """
# results #
class SunlightApiObject(object):
def __init__(self, d):
self.__dict__ = d
class Legislator(SunlightApiObject):
def __str__(self):
if self.nickname:
fname = self.nickname
else:
fname = self.firstname
return '%s. %s %s (%s-%s)' % (self.title, fname, self.lastname,
self.party, self.state)
class LegislatorSearchResult(SunlightApiObject):
def __init__(self, d):
self.legislator = Legislator(d['legislator'])
self.score = d['score']
def __str__(self):
return '%s %s' % (self.score, self.legislator)
class Committee(SunlightApiObject):
def __init__(self, d):
self.__dict__ = d
self.subcommittees = [Committee(sc['committee']) for sc in getattr(self, 'subcommittees', [])]
self.members = [Legislator(m['legislator']) for m in getattr(self, 'members', [])]
def __str__(self):
return '%s' % (self.name)
class District(SunlightApiObject):
def __str__(self):
return '%s-%s' % (self.state, self.number)
class Lobbyist(SunlightApiObject):
def __str__(self):
return '%s %s' % (self.firstname, self.lastname)
class Issue(SunlightApiObject):
def __str__(self):
return '%s (%s)' % (self.code, self.specific_issue)
class Filing(SunlightApiObject):
def __init__(self, d):
self.__dict__ = d
self.lobbyists = [Lobbyist(l['lobbyist']) for l in self.lobbyists]
self.issues = [Issue(i['issue']) for i in self.issues]
def __str__(self):
return '%s - %s for %s' % (self.filing_id, self.registrant_name,
self.client_name)
class FilingSearchResult(SunlightApiObject):
def __init__(self, d):
self.__dict__ = d['lobbyist']
self.score = d['score']
def __str__(self):
return '%s %s %s (%s)' % (self.score, self.firstname, self.lastname,
self.registrant_name)
# namespaces #
class sunlight(object):
apikey = None
@staticmethod
def _apicall(func, params):
if sunlight.apikey is None:
raise SunlightApiError('Missing sunlight apikey')
url = 'http://services.sunlightlabs.com/api/%s.json?apikey=%s&%s' % \
(func, sunlight.apikey, urllib.urlencode(params))
try:
response = urllib2.urlopen(url).read()
return json.loads(response)['response']
except urllib2.HTTPError, e:
raise SunlightApiError(e.read())
except (ValueError, KeyError), e:
raise SunlightApiError('Invalid Response')
class legislators(object):
@staticmethod
def get(**kwargs):
result = sunlight._apicall('legislators.get', kwargs)['legislator']
return Legislator(result)
@staticmethod
def getList(**kwargs):
results = sunlight._apicall('legislators.getList', kwargs)
return [Legislator(l['legislator']) for l in results['legislators']]
@staticmethod
def search(name, threshold=0.9, all_legislators=False):
params = {'name':name, 'threshold': threshold}
if all_legislators:
params['all_legislators'] = 1
results = sunlight._apicall('legislators.search', params)['results']
return [LegislatorSearchResult(r['result']) for r in results]
@staticmethod
def allForZip(zipcode):
results = sunlight._apicall('legislators.allForZip', {'zip':zipcode})
return [Legislator(l['legislator']) for l in results['legislators']]
class committees(object):
@staticmethod
def get(committee_id):
results = sunlight._apicall('committees.get', {'id':committee_id})
return Committee(results['committee'])
@staticmethod
def getList(chamber):
results = sunlight._apicall('committees.getList', {'chamber':chamber})
return [Committee(c['committee']) for c in results['committees']]
@staticmethod
def allForLegislator(bioguide_id):
results = sunlight._apicall('committees.allForLegislator',
{'bioguide_id': bioguide_id})
return [Committee(c['committee']) for c in results['committees']]
class districts(object):
@staticmethod
def getDistrictsFromZip(zipcode):
results = sunlight._apicall('districts.getDistrictsFromZip', {'zip':zipcode})
return [District(r['district']) for r in results['districts']]
@staticmethod
def getZipsFromDistrict(state, district):
params = {'state':state, 'district':district}
results = sunlight._apicall('districts.getZipsFromDistrict', params)
return results['zips']
@staticmethod
def getDistrictFromLatLong(latitude, longitude):
params = {'latitude':latitude, 'longitude':longitude}
result = sunlight._apicall('districts.getDistrictFromLatLong', params)
return District(result['districts'][0]['district'])
class lobbyists(object):
@staticmethod
def getFiling(id):
warnings.warn('lobbyist methods deprecated as of September 2009', DeprecationWarning)
result = sunlight._apicall('lobbyists.getFiling', {'id':id})
return Filing(result['filing'])
@staticmethod
def getFilingList(**kwargs):
warnings.warn('lobbyist methods deprecated as of September 2009', DeprecationWarning)
results = sunlight._apicall('lobbyists.getFilingList', kwargs)
return [Filing(f['filing']) for f in results['filings']]
@staticmethod
def search(name, year=None, threshold=0.9):
warnings.warn('lobbyist methods deprecated as of September 2009', DeprecationWarning)
if year == None:
import time
year = time.strftime('%Y')
params = {'name':name, 'year':year, 'threshold': threshold}
results = sunlight._apicall('lobbyists.search', params)['results']
return [FilingSearchResult(f['result']) for f in results]
class wordlist(object):
@staticmethod
def _apicall(call, body=None):
warnings.warn('wordlist methods deprecated as of September 2009', DeprecationWarning)
base_url = 'http://services.sunlightlabs.com/api/wordlist'
url = '%s/%s/?apikey=%s' % (base_url, call, sunlight.apikey)
try:
if body:
data = urllib.urlencode(body)
else:
data = None
return urllib2.urlopen(url, data).read()
except urllib2.HTTPError, e:
raise SunlightApiError(e.read())
@staticmethod
def get(list_name):
warnings.warn('wordlist methods deprecated as of September 2009', DeprecationWarning)
words = sunlight.wordlist._apicall(list_name)
return words.split('~')
@staticmethod
def update(list_name, words):
warnings.warn('wordlist methods deprecated as of September 2009', DeprecationWarning)
sunlight.wordlist._apicall(list_name, {'words': '\n'.join(words)})
@staticmethod
def filter_stopwords(list_name, text):
warnings.warn('wordlist methods deprecated as of September 2009', DeprecationWarning)
call = '%s/filter_stopwords' % list_name
return sunlight.wordlist._apicall(call, {'text': text})