import the yelpapi module (a final dependency)
[yelp-api-cdsw] / yelpapi / yelpapi.py
1 """
2     Copyright (c) 2013, Los Alamos National Security, LLC
3     All rights reserved.
4
5     Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
6     following conditions are met:
7
8     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
9       disclaimer.
10     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
11       following disclaimer in the documentation and/or other materials provided with the distribution.
12     * Neither the name of Los Alamos National Security, LLC nor the names of its contributors may be used to endorse or
13       promote products derived from this software without specific prior written permission.
14
15     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 """
23
24 from requests_oauthlib import OAuth1Session
25
26 SEARCH_API_URL = 'http://api.yelp.com/v2/search'
27 BUSINESS_API_URL = 'http://api.yelp.com/v2/business/%s'
28
29
30 class YelpAPI(object):
31
32     """
33         This class implements the complete Yelp 2.0 API. It offers access to both the Search API and
34         Business API. It is simple and completely extensible since it dynamically takes arguments. This will
35         allow it to continue working even if Yelp changes the spec. The only thing that should cause this to break
36         is if Yelp changes the URL scheme.
37     """
38
39     class YelpError(Exception):
40
41         """
42             This class is used for all non-API errors. For example, this exception will be raised if a non-JSON-parseable
43             response from Yelp is received.
44         """
45         pass
46
47     class YelpAPIError(Exception):
48
49         """
50             This class is used for all API errors. For a list of all possible Yelp API errors, see
51             http://www.yelp.com/developers/documentation/v2/errors.
52         """
53         pass
54
55     def __init__(self, consumer_key, consumer_secret, token, token_secret):
56         self._yelp_session = OAuth1Session(consumer_key, consumer_secret, token, token_secret)
57
58     @staticmethod
59     def _get_clean_parameters(kwargs):
60         """
61             Clean the parameters by filtering out any parameters that have a None value.
62         """
63         return dict((k, v) for k, v in kwargs.items() if v is not None)
64
65     def search_query(self, **kwargs):
66         """
67             This function implements the Yelp Search API (http://www.yelp.com/developers/documentation/v2/search_api).
68             Arbitrary keywords can be passed in, and a dynamically-generated dict of businesses will be returned.
69         """
70         parameters = YelpAPI._get_clean_parameters(kwargs)
71         response = self._yelp_session.get(SEARCH_API_URL, params=parameters)
72
73         # raise YelpError if Yelp returns invalid JSON or something other than JSON
74         try:
75             response_json = response.json()
76         except ValueError as e:
77             raise self.YelpError(e)
78
79         # Yelp can return one of many different API errors, so check for one of them
80         # possible errors: http://www.yelp.com/developers/documentation/v2/errors
81         if 'error' in response_json:
82             if 'field' in response_json['error']:
83                 raise self.YelpAPIError(response_json['error']['id'], '%s [field=%s]' % (response_json['error']['text'], response_json['error']['field']))
84             else:
85                 raise self.YelpAPIError(response_json['error']['id'], response_json['error']['text'])
86
87         # we got a good response, so return
88         return response_json
89
90     def business_query(self, id, **kwargs):
91         """
92             Similar to search_query, this function implements the Yelp Business API (http://www.yelp.com/developers/documentation/v2/business).
93             A mandatory business ID must be passed in, as well as any arbitrary keywords allowed by Yelp. A single dict will be returned for the
94             business.
95         """
96         if not id:
97             raise ValueError('A valid business ID must be given.')
98
99         parameters = YelpAPI._get_clean_parameters(kwargs)
100         response = self._yelp_session.get(BUSINESS_API_URL % id, params=parameters)
101
102         # Yelp currently returns a 404 HTML page if an invalid business ID is provided, so check for that
103         try:
104             response_json = response.json()
105         except ValueError:
106             raise self.YelpError('Unable to parse JSON from Yelp response. This is likely caused by an invalid business ID [id=%s].' % id)
107
108         # we got a good response, so return
109         return response_json

Benjamin Mako Hill || Want to submit a patch?