SteveMarshall / fire-eagle-python-binding
- Source
- Commits
- Network (2)
- Issues (0)
- Downloads (0)
- Wiki (1)
- Graphs
-
Tree:
ebace6d
commit ebace6d8cdf1a5908550732e32f7a5b911c7de72
tree 9c3b0cae53310bd9e093bb9a9d3ca7cf1bd05c26
parent 7254b8af50a61f9bb5b5e6b19fdc3ddc8f16705f
tree 9c3b0cae53310bd9e093bb9a9d3ca7cf1bd05c26
parent 7254b8af50a61f9bb5b5e6b19fdc3ddc8f16705f
fire-eagle-python-binding / fireeagle_api.py
| 2b32f51e » | Steven Marshall | 2008-03-04 | 1 | """ | |
| b3336f3b » | Steven Marshall | 2008-03-25 | 2 | Fire Eagle API Python module v0.6.1 | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 3 | by Steve Marshall <steve@nascentguruism.com> | |
| 4 | <http://nascentguruism.com/> | ||||
| 5 | |||||
| b3336f3b » | Steven Marshall | 2008-03-25 | 6 | Source repo at <http://github.com/SteveMarshall/fire-eagle-python-binding/> | |
| 7 | |||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 8 | Example usage: | |
| 9 | |||||
| 10 | >>> from fireeagle_api import FireEagle | ||||
| d19a70b6 » | myelin | 2008-08-07 | 11 | >>> from pprint import pprint | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 12 | >>> fe = FireEagle( YOUR_CONSUMER_KEY, YOUR_CONSUMER_SECRET ) | |
| 13 | >>> application_token = fe.request_token() | ||||
| 14 | >>> auth_url = fe.authorize( application_token ) | ||||
| 15 | >>> print auth_url | ||||
| 16 | >>> pause( 'Please authorize the app at that URL!' ) | ||||
| 17 | >>> user_token = fe.access_token( application_token ) | ||||
| d19a70b6 » | myelin | 2008-08-07 | 18 | >>> pprint( fe.lookup( user_token, q='London, England' ) ) | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 19 | [{'name': 'London, England', 'place_id': '.2P4je.dBZgMyQ'}] | |
| d19a70b6 » | myelin | 2008-08-07 | 20 | >>> pprint( fe.user( user_token ) ) | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 21 | [ { 'best_guess': True, | |
| 22 | 'georss:box': [ u'51.2613182068', | ||||
| 23 | u'-0.5090100169', | ||||
| 24 | u'51.6860313416', | ||||
| 25 | u'0.2803600132'], | ||||
| 26 | 'level': 3, | ||||
| 27 | 'level_name': 'city', | ||||
| 28 | 'located_at': datetime.datetime(2008, 2, 29, 13, 22, 2), | ||||
| 29 | 'name': 'London, England', | ||||
| 30 | 'place_id': '.2P4je.dBZgMyQ'}, | ||||
| 31 | { 'best_guess': True, | ||||
| 32 | 'georss:box': [ u'49.8662185669', | ||||
| 33 | u'-6.4506998062', | ||||
| 34 | u'55.8111686707', | ||||
| 35 | u'1.7633299828'], | ||||
| 36 | 'level': 5, | ||||
| 37 | 'level_name': 'state', | ||||
| 38 | 'located_at': datetime.datetime(2008, 2, 29, 13, 22, 2), | ||||
| 39 | 'name': 'England, United Kingdom', | ||||
| 40 | 'place_id': 'pn4MsiGbBZlXeplyXg'}, | ||||
| 41 | { 'best_guess': True, | ||||
| 42 | 'georss:box': [ u'49.1620903015', | ||||
| 43 | u'-8.6495599747', | ||||
| 44 | u'60.8606987', | ||||
| 45 | u'1.7633399963'], | ||||
| 46 | 'level': 6, | ||||
| 47 | 'level_name': 'country', | ||||
| 48 | 'located_at': datetime.datetime(2008, 2, 29, 13, 22, 2), | ||||
| 49 | 'name': 'United Kingdom', | ||||
| 50 | 'place_id': 'DevLebebApj4RVbtaQ'}] | ||||
| 51 | |||||
| 52 | Copyright (c) 2008, Steve Marshall | ||||
| 53 | All rights reserved. | ||||
| 54 | |||||
| 55 | Unless otherwise specified, redistribution and use of this software in | ||||
| 56 | source and binary forms, with or without modification, are permitted | ||||
| 57 | provided that the following conditions are met: | ||||
| 58 | |||||
| 59 | * Redistributions of source code must retain the above copyright | ||||
| 60 | notice, this list of conditions and the following disclaimer. | ||||
| 61 | * Redistributions in binary form must reproduce the above copyright | ||||
| 62 | notice, this list of conditions and the following disclaimer in the | ||||
| 63 | documentation and/or other materials provided with the distribution. | ||||
| 64 | * The name of the author nor the names of any contributors may be | ||||
| 65 | used to endorse or promote products derived from this software without | ||||
| 66 | specific prior written permission. | ||||
| 67 | |||||
| 68 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | ||||
| 69 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | ||||
| 70 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||||
| 71 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER | ||||
| 72 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||||
| 73 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||||
| 74 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||||
| 75 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||
| 76 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||||
| 77 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| 78 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| 79 | """ | ||||
| d19a70b6 » | myelin | 2008-08-07 | 80 | import datetime, httplib, os.path, re, string | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 81 | from xml.dom import minidom | |
| 82 | |||||
| 83 | import oauth | ||||
| 84 | |||||
| 85 | # General API setup | ||||
| 86 | API_PROTOCOL = 'https' | ||||
| 87 | API_SERVER = 'fireeagle.yahooapis.com' | ||||
| 88 | API_VERSION = '0.1' | ||||
| 81f4c59b » | Steven Marshall | 2008-04-30 | 89 | FE_PROTOCOL = 'https' | |
| d19a70b6 » | myelin | 2008-08-07 | 90 | FE_SERVER = 'fireeagle.yahoo.net' | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 91 | ||
| 92 | # Calling templates | ||||
| 93 | API_URL_TEMPLATE = string.Template( | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 94 | '${server}/api/' + API_VERSION + '/${method}' | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 95 | ) | |
| 96 | OAUTH_URL_TEMPLATE = string.Template( | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 97 | '${server}/oauth/${method}' | |
| d19a70b6 » | myelin | 2008-08-07 | 98 | ) | |
| 99 | AUTHORIZE_URL_TEMPLATE = string.Template( | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 100 | '${server}/oauth/${method}?oauth_token=${token}' | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 101 | ) | |
| 102 | POST_HEADERS = { | ||||
| 103 | 'Content-type': 'application/x-www-form-urlencoded', | ||||
| 104 | 'Accept' : 'text/plain' | ||||
| 105 | } | ||||
| 106 | LOCATION_PARAMETERS = [ | ||||
| 107 | 'address', 'cid', 'city', 'country', 'geom', 'lac', 'lat', | ||||
| d19a70b6 » | myelin | 2008-08-07 | 108 | 'lon', 'mcc', 'mnc', 'place_id', 'postal', 'q', 'state', 'woeid' | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 109 | ] | |
| 110 | |||||
| 111 | # Error templates | ||||
| 112 | NULL_ARGUMENT_EXCEPTION = string.Template( | ||||
| 113 | 'Too few arguments were supplied for the method ${method}; required arguments are: ${args}' | ||||
| 114 | ) | ||||
| 115 | # TODO: Allow specification of method name and call-stack? | ||||
| 116 | SPECIFIED_ERROR_EXCEPTION = string.Template( | ||||
| 117 | '${message} (Code ${code})' | ||||
| 118 | ) | ||||
| 119 | UNSPECIFIED_ERROR_EXCEPTION = string.Template( | ||||
| 120 | 'An error occurred whilst trying to execute the requested method, and the server responded with status ${status}.' | ||||
| 121 | ) | ||||
| 122 | |||||
| 123 | # Attribute conversion functions | ||||
| 124 | string = lambda s: s.encode('utf8') | ||||
| ee8b2202 » | Steven Marshall | 2008-03-25 | 125 | boolean = lambda s: 'true' == s.lower() | |
| 126 | |||||
| 127 | def geo_str(s): | ||||
| 128 | if 0 == len(s): | ||||
| 129 | return None | ||||
| 130 | # TODO: Would this be better served returning an array of floats? | ||||
| 81f4c59b » | Steven Marshall | 2008-04-30 | 131 | return [float(bit) for bit in s.split(' ')] | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 132 | ||
| 133 | def date(s): | ||||
| 134 | # 2008-02-08T10:49:03-08:00 | ||||
| 135 | bits = re.match(r""" | ||||
| 136 | ^(\d{4}) # Year ($1) | ||||
| 137 | -(\d{2}) # Month ($2) | ||||
| 138 | -(\d{2}) # Day ($3) | ||||
| 139 | T(\d{2}) # Hour ($4) | ||||
| 140 | :(\d{2}) # Minute ($5) | ||||
| 141 | :(\d{2}) # Second ($6) | ||||
| 142 | [+-] # TODO: TZ offset dir ($7) | ||||
| 143 | \d{2} # TODO: Offset hour ($8) | ||||
| 144 | :\d{2} # TODO: Offset min ($9) | ||||
| 145 | """, s, re.VERBOSE | ||||
| 146 | ).groups() | ||||
| 147 | bits = [bit for bit in bits if bit is not None] | ||||
| 148 | |||||
| 149 | # TODO: Generate fixed-offset tzinfo | ||||
| 150 | return datetime.datetime(*map(int, bits)) | ||||
| 151 | |||||
| 152 | # Return types | ||||
| 153 | LOCATION = 'location', { | ||||
| 154 | 'name' : string, | ||||
| 155 | 'place_id': string, | ||||
| d19a70b6 » | myelin | 2008-08-07 | 156 | 'woeid' : string, | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 157 | } | |
| 158 | |||||
| 159 | USER_LOCATION = 'location', { | ||||
| ee8b2202 » | Steven Marshall | 2008-03-25 | 160 | 'best_guess' : boolean, | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 161 | # HACK: I'm not entirely happy using 'georss:box' as the key here | |
| 162 | 'georss:box' : geo_str, | ||||
| ee8b2202 » | Steven Marshall | 2008-03-25 | 163 | 'georss:point' : geo_str, | |
| 164 | 'level' : int, | ||||
| 165 | 'level_name' : string, | ||||
| 166 | 'located_at' : date, | ||||
| 167 | 'name' : string, | ||||
| 168 | 'place_id' : string, | ||||
| d19a70b6 » | myelin | 2008-08-07 | 169 | 'woeid' : string, | |
| 170 | 'query' : string, | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 171 | } | |
| 172 | |||||
| 270cce1a » | Steven Marshall | 2008-03-06 | 173 | USER = 'user', { | |
| 174 | 'token' : string, | ||||
| e304fa5e » | Steven Marshall | 2008-03-06 | 175 | 'location': USER_LOCATION, | |
| 270cce1a » | Steven Marshall | 2008-03-06 | 176 | } | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 177 | FIREEAGLE_METHODS = { | |
| 178 | # OAuth methods | ||||
| 179 | 'access_token': { | ||||
| 180 | 'http_headers': None, | ||||
| 181 | 'http_method' : 'GET', | ||||
| 182 | 'optional' : [], | ||||
| ebace6d8 » | mojodna | 2009-05-26 | 183 | 'required' : ['oauth_verifier', 'token'], | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 184 | 'returns' : 'oauth_token', | |
| 185 | 'url_template': OAUTH_URL_TEMPLATE, | ||||
| 186 | }, | ||||
| 187 | 'authorize': { | ||||
| 188 | 'http_headers': None, | ||||
| 189 | 'http_method' : 'GET', | ||||
| 190 | 'optional' : [], | ||||
| 191 | 'required' : ['token'], | ||||
| 192 | 'returns' : 'request_url', | ||||
| d19a70b6 » | myelin | 2008-08-07 | 193 | 'url_template': AUTHORIZE_URL_TEMPLATE, | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 194 | }, | |
| 195 | 'request_token': { | ||||
| 196 | 'http_headers': None, | ||||
| 197 | 'http_method' : 'GET', | ||||
| ebace6d8 » | mojodna | 2009-05-26 | 198 | 'optional' : ['oauth_callback'], | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 199 | 'required' : [], | |
| 200 | 'returns' : 'oauth_token', | ||||
| 201 | 'url_template': OAUTH_URL_TEMPLATE, | ||||
| 202 | }, | ||||
| 203 | # Fire Eagle methods | ||||
| 204 | 'lookup': { | ||||
| 205 | 'http_headers': None, | ||||
| 206 | 'http_method' : 'GET', | ||||
| 207 | 'optional' : LOCATION_PARAMETERS, | ||||
| 208 | 'required' : ['token'], | ||||
| 209 | 'returns' : LOCATION, | ||||
| 210 | 'url_template': API_URL_TEMPLATE, | ||||
| 211 | }, | ||||
| 270cce1a » | Steven Marshall | 2008-03-06 | 212 | 'recent': { | |
| 213 | 'http_headers': None, | ||||
| 214 | 'http_method' : 'GET', | ||||
| e304fa5e » | Steven Marshall | 2008-03-06 | 215 | 'optional' : ['per_page', 'page', 'time'], | |
| 270cce1a » | Steven Marshall | 2008-03-06 | 216 | 'required' : ['token'], | |
| 217 | 'returns' : USER, | ||||
| 218 | 'url_template': API_URL_TEMPLATE, | ||||
| 219 | }, | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 220 | 'update': { | |
| 221 | 'http_headers': POST_HEADERS, | ||||
| 222 | 'http_method' : 'POST', | ||||
| 223 | 'optional' : LOCATION_PARAMETERS, | ||||
| 224 | 'required' : ['token'], | ||||
| 225 | # We don't care about returns from update: HTTP 200 is success | ||||
| 226 | 'returns' : None, | ||||
| 227 | 'url_template': API_URL_TEMPLATE, | ||||
| 228 | }, | ||||
| 229 | 'user': { | ||||
| 230 | 'http_headers': None, | ||||
| 231 | 'http_method' : 'GET', | ||||
| 232 | 'optional' : [], | ||||
| 233 | 'required' : ['token'], | ||||
| 65c08d86 » | Steven Marshall | 2008-03-06 | 234 | 'returns' : USER, | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 235 | 'url_template': API_URL_TEMPLATE, | |
| 236 | }, | ||||
| e304fa5e » | Steven Marshall | 2008-03-06 | 237 | 'within': { | |
| 238 | 'http_headers': None, | ||||
| 239 | 'http_method' : 'GET', | ||||
| 240 | # HACK: woe_id is ignored if place_id is present, so neither is | ||||
| 241 | # strictly 'required'. Unfortunately, calling with neither | ||||
| 242 | # returns an empty list | ||||
| 243 | 'optional' : ['place_id', 'woe_id'], | ||||
| 244 | 'required' : ['token'], | ||||
| 245 | 'returns' : USER, | ||||
| 246 | 'url_template': API_URL_TEMPLATE, | ||||
| 247 | } | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 248 | } | |
| 249 | |||||
| 250 | class FireEagleException( Exception ): | ||||
| 251 | pass | ||||
| 252 | |||||
| e304fa5e » | Steven Marshall | 2008-03-06 | 253 | # Used as a proxy for methods of the FireEagle class; when methods are called, | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 254 | # __call__ in FireEagleAccumulator is called, ultimately calling the | |
| 255 | # fireeagle_obj's callMethod() | ||||
| 256 | class FireEagleAccumulator: | ||||
| 257 | def __init__( self, fireeagle_obj, name ): | ||||
| 258 | self.fireeagle_obj = fireeagle_obj | ||||
| 259 | self.name = name | ||||
| 260 | |||||
| 261 | def __repr__( self ): | ||||
| 262 | return self.name | ||||
| 263 | |||||
| 264 | def __call__( self, *args, **kw ): | ||||
| 265 | return self.fireeagle_obj.call_method( self.name, *args, **kw ) | ||||
| 266 | |||||
| 267 | |||||
| 268 | class FireEagle: | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 269 | def __init__( self, rc_or_consumer_key, consumer_secret=None ): | |
| 270 | """ | ||||
| 271 | syntax: FireEagle( os.path.expanduser( "~/.fireeaglerc" ) ) | ||||
| 272 | or FireEagle( CONSUMER_KEY, CONSUMER_SECRET ) | ||||
| 273 | """ | ||||
| 274 | |||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 275 | # Prepare object lifetime variables | |
| 9f40e6d4 » | myelin | 2008-08-08 | 276 | self.read_config( rc_or_consumer_key, consumer_secret ) | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 277 | self.oauth_consumer = oauth.OAuthConsumer( | |
| 278 | self.consumer_key, | ||||
| 279 | self.consumer_secret | ||||
| 280 | ) | ||||
| 281 | self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 282 | proto, host, port = re.search(r"^(https?)://([a-z\.0-9]+)(?:\:(\d+))?$", self.api_server).groups() | |
| 283 | self.http_connection = (proto == 'https' and httplib.HTTPSConnection or httplib.HTTPConnection)( host, port ) | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 284 | ||
| 285 | # Prepare the accumulators for each method | ||||
| 286 | for method, _ in FIREEAGLE_METHODS.items(): | ||||
| 287 | if not hasattr( self, method ): | ||||
| 288 | setattr( self, method, FireEagleAccumulator( self, method )) | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 289 | ||
| 290 | def read_config( self, rc_or_consumer_key, consumer_secret ): | ||||
| 291 | if consumer_secret is None: | ||||
| 292 | info = {} | ||||
| 293 | for line in open( rc_or_consumer_key ).readlines(): | ||||
| 294 | p = line.find( "#" ) | ||||
| 295 | if p != -1: line = line[:p] | ||||
| 296 | line = line.strip() | ||||
| 297 | if not line: continue | ||||
| 298 | k, v = line.split("=", 1) | ||||
| 299 | info[ k.strip() ] = v.strip() | ||||
| 300 | else: | ||||
| 301 | info = { | ||||
| 302 | 'consumer_key': rc_or_consumer_key, | ||||
| 303 | 'consumer_secret': consumer_secret, | ||||
| 304 | } | ||||
| 305 | |||||
| 306 | info.setdefault("api_server", API_SERVER) | ||||
| 307 | info.setdefault("api_protocol", API_PROTOCOL) | ||||
| 308 | self.api_server = self._build_server_url(info, 'api') | ||||
| 309 | |||||
| 310 | info.setdefault("auth_server", FE_SERVER) | ||||
| 311 | info.setdefault("auth_protocol", FE_PROTOCOL) | ||||
| 312 | self.auth_server = self._build_server_url(info, 'auth') | ||||
| 313 | |||||
| 314 | self.consumer_key, self.consumer_secret = info['consumer_key'], info['consumer_secret'] | ||||
| 315 | |||||
| 316 | def _build_server_url( self, info, role ): | ||||
| 317 | proto = info['%s_protocol' % role] | ||||
| 318 | default_port = (proto == 'https') and 443 or 80 | ||||
| 319 | port = int(info.get('%s_port' % role, default_port)) | ||||
| 320 | url = '%s://%s%s' % ( | ||||
| 321 | proto, | ||||
| 322 | info['%s_server' % role], | ||||
| 323 | (port != default_port) and (':%d' % port) or '', | ||||
| 324 | ) | ||||
| 325 | return url | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 326 | ||
| 327 | def fetch_response( self, http_method, url, \ | ||||
| 328 | body = None, headers = None ): | ||||
| 329 | """Pass a request to the server and return the response as a string""" | ||||
| 330 | |||||
| 331 | # Prepare the request | ||||
| 332 | if ( body is not None ) or ( headers is not None ): | ||||
| 333 | self.http_connection.request( http_method, url, body, headers ) | ||||
| 334 | else: | ||||
| 335 | self.http_connection.request( http_method, url ) | ||||
| 336 | |||||
| 337 | # Get the response | ||||
| 338 | response = self.http_connection.getresponse() | ||||
| 339 | response_body = response.read() | ||||
| 340 | |||||
| 341 | # If we've been informed of an error, raise it | ||||
| 342 | if ( 200 != response.status ): | ||||
| 343 | # Try to get the error message | ||||
| 344 | try: | ||||
| 345 | error_dom = minidom.parseString( response_body ) | ||||
| 346 | response_errors = error_dom.getElementsByTagName( 'err' ) | ||||
| 347 | except: # TODO: Naked except: make this explicit! | ||||
| 348 | response_errors = None | ||||
| 349 | |||||
| 350 | # If we can't get the error message, just raise a generic one | ||||
| 351 | if response_errors: | ||||
| 352 | msg = SPECIFIED_ERROR_EXCEPTION.substitute( \ | ||||
| 353 | message = response_errors[0].getAttribute( 'msg' ), | ||||
| 354 | code = response_errors[0].getAttribute( 'code' ) | ||||
| 355 | ) | ||||
| 356 | else: | ||||
| 357 | msg = UNSPECIFIED_ERROR_EXCEPTION.substitute( \ | ||||
| 358 | status = response.status ) | ||||
| 359 | |||||
| 360 | raise FireEagleException, msg | ||||
| 361 | |||||
| 362 | # Return the body of the response | ||||
| 363 | return response_body | ||||
| 364 | |||||
| 270cce1a » | Steven Marshall | 2008-03-06 | 365 | def build_return( self, dom_element, target_element_name, conversions): | |
| 366 | results = [] | ||||
| 367 | for node in dom_element.getElementsByTagName( target_element_name ): | ||||
| 368 | data = {} | ||||
| 369 | |||||
| 370 | for key, conversion in conversions.items(): | ||||
| 371 | node_key = key.replace( '_', '-' ) | ||||
| ee8b2202 » | Steven Marshall | 2008-03-25 | 372 | key = key.replace( ':', '_' ) | |
| 270cce1a » | Steven Marshall | 2008-03-06 | 373 | data_elements = node.getElementsByTagName( node_key ) | |
| 374 | |||||
| 65c08d86 » | Steven Marshall | 2008-03-06 | 375 | # If conversion is a tuple, call build_return again | |
| 376 | if isinstance( conversion, tuple ): | ||||
| 377 | child_element, child_conversions = conversion | ||||
| 378 | data[key] = self.build_return( \ | ||||
| 379 | node, child_element, child_conversions \ | ||||
| 380 | ) | ||||
| 270cce1a » | Steven Marshall | 2008-03-06 | 381 | else: | |
| 65c08d86 » | Steven Marshall | 2008-03-06 | 382 | # If we've got multiple elements, build a | |
| 383 | # list of conversions | ||||
| 384 | if data_elements and ( len( data_elements ) > 1 ): | ||||
| 385 | data_item = [] | ||||
| 386 | for data_element in data_elements: | ||||
| 387 | data_item.append( conversion( | ||||
| 388 | data_element.firstChild.data | ||||
| 389 | ) ) | ||||
| 390 | # If we only have one element, assume text node | ||||
| 391 | elif data_elements: | ||||
| 392 | data_item = conversion( \ | ||||
| 393 | data_elements[0].firstChild.data | ||||
| 394 | ) | ||||
| 395 | # If no elements are matched, convert the attribute | ||||
| 396 | else: | ||||
| 397 | data_item = conversion( \ | ||||
| ee8b2202 » | Steven Marshall | 2008-03-25 | 398 | node.getAttribute( node_key ) \ | |
| 65c08d86 » | Steven Marshall | 2008-03-06 | 399 | ) | |
| ee8b2202 » | Steven Marshall | 2008-03-25 | 400 | ||
| 401 | if data_item is not None: | ||||
| 402 | data[key] = data_item | ||||
| 270cce1a » | Steven Marshall | 2008-03-06 | 403 | ||
| 404 | results.append( data ) | ||||
| 405 | |||||
| 406 | return results | ||||
| 407 | |||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 408 | def call_method( self, method, *args, **kw ): | |
| 409 | |||||
| 410 | # Theoretically, we might want to do 'does this method exits?' checks | ||||
| 411 | # here, but as all the aggregators are being built in __init__(), | ||||
| 412 | # we actually don't need to: Python handles it for us. | ||||
| 413 | meta = FIREEAGLE_METHODS[method] | ||||
| 414 | |||||
| 415 | if args: | ||||
| 416 | # Positional arguments are mapped to meta['required'] | ||||
| 417 | # and meta['optional'] in order of specification of those | ||||
| 418 | # (with required first, obviously) | ||||
| 419 | names = meta['required'] + meta['optional'] | ||||
| 420 | for i in range( len( args ) ): | ||||
| 421 | kw[names[i]] = args[i] | ||||
| 422 | |||||
| 423 | # Check we have all required arguments | ||||
| 424 | if len( set( meta['required'] ) - set( kw.keys() ) ) > 0: | ||||
| 425 | raise FireEagleException, \ | ||||
| 426 | NULL_ARGUMENT_EXCEPTION.substitute( \ | ||||
| 427 | method = method, \ | ||||
| 428 | args = ', '.join( meta['required'] ) | ||||
| 429 | ) | ||||
| 430 | |||||
| 431 | # Token shouldn't be handled as a normal arg, so strip it out | ||||
| 432 | # (but make sure we have it, even if it's None) | ||||
| 433 | if 'token' in kw: | ||||
| 434 | token = kw['token'] | ||||
| 435 | del kw['token'] | ||||
| 436 | else: | ||||
| 437 | token = None | ||||
| d19a70b6 » | myelin | 2008-08-07 | 438 | ||
| 439 | # If the return type is the request_url, simply build the URL | ||||
| 440 | # (without a signature) and return it witout executing | ||||
| 441 | # anything. | ||||
| 442 | if 'request_url' == meta['returns']: | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 443 | return meta['url_template'].substitute( method=method, server=self.auth_server, token=token.key ) | |
| ebace6d8 » | mojodna | 2009-05-26 | 444 | ||
| 445 | if 'oauth_callback' in meta['optional'] and 'oauth_callback' not in kw: | ||||
| 446 | kw['oauth_callback'] = "oob" | ||||
| 447 | |||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 448 | # Build and sign the oauth_request | |
| d19a70b6 » | myelin | 2008-08-07 | 449 | # NOTE: If ( token == None ), it's handled silently | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 450 | # when building/signing | |
| 451 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( | ||||
| 452 | self.oauth_consumer, | ||||
| 453 | token = token, | ||||
| 454 | http_method = meta['http_method'], | ||||
| 9f40e6d4 » | myelin | 2008-08-08 | 455 | http_url = meta['url_template'].substitute( method=method, server=self.api_server ), | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 456 | parameters = kw | |
| 457 | ) | ||||
| 458 | oauth_request.sign_request( | ||||
| 459 | self.signature_method, | ||||
| 460 | self.oauth_consumer, | ||||
| 461 | token | ||||
| 462 | ) | ||||
| 463 | |||||
| 464 | if 'POST' == meta['http_method']: | ||||
| 465 | response = self.fetch_response( oauth_request.http_method, \ | ||||
| 9c235b7f » | SteveMarshall | 2009-05-21 | 466 | oauth_request.get_normalized_http_url(), \ | |
| 467 | oauth_request.to_postdata(), \ | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 468 | meta['http_headers'] ) | |
| 469 | else: | ||||
| 470 | response = self.fetch_response( oauth_request.http_method, \ | ||||
| 471 | oauth_request.to_url() ) | ||||
| 472 | |||||
| 473 | # Method returns nothing, but finished fine | ||||
| 474 | if not meta['returns']: | ||||
| 475 | return True | ||||
| 476 | # Return the oauth token | ||||
| 477 | elif 'oauth_token' == meta['returns']: | ||||
| 478 | return oauth.OAuthToken.from_string( response ) | ||||
| 479 | |||||
| 480 | element, conversions = meta['returns'] | ||||
| 481 | response_dom = minidom.parseString( response ) | ||||
| d6e1d9bd » | Steven Marshall | 2008-03-25 | 482 | ||
| a9a96f0b » | Steven Marshall | 2008-03-06 | 483 | results = self.build_return( \ | |
| 484 | response_dom, element, conversions ) | ||||
| 2b32f51e » | Steven Marshall | 2008-03-04 | 485 | ||
| a9a96f0b » | Steven Marshall | 2008-03-06 | 486 | return results | |
| 2b32f51e » | Steven Marshall | 2008-03-04 | 487 | ||
| 488 | |||||
| d19a70b6 » | myelin | 2008-08-07 | 489 | # TODO: Cached version | |

