Skip to content

Commit ef1e42c

Browse files
committed
Merge pull request tweepy#220 from inactivist/geo-models
Place model support and geo API mods to support Places
2 parents 517031b + 5f4c71f commit ef1e42c

File tree

3 files changed

+95
-7
lines changed

3 files changed

+95
-7
lines changed

tests.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,19 @@ def testtrends(self):
294294
self.api.trends_weekly()
295295

296296
def testgeoapis(self):
297-
self.api.geo_id(id='c3f37afa9efcf94b') # Austin, TX, USA
298-
self.api.nearby_places(lat=30.267370168467806, long=-97.74261474609375) # Austin, TX, USA
299-
self.api.reverse_geocode(lat=30.267370168467806, long=-97.74261474609375) # Austin, TX, USA
297+
def place_name_in_list(place_name, place_list):
298+
"""Return True if a given place_name is in place_list."""
299+
return any([x.full_name.lower() == place_name.lower() for x in place_list])
300+
301+
twitter_hq = self.api.geo_similar_places(lat=37, long= -122, name='Twitter HQ')
302+
# Assumes that twitter_hq is first Place returned...
303+
self.assertEqual(twitter_hq[0].id, '3bdf30ed8b201f31')
304+
# Test various API functions using Austin, TX, USA
305+
self.assertEqual(self.api.geo_id(id='c3f37afa9efcf94b').full_name, 'Austin, TX')
306+
self.assertTrue(place_name_in_list('Austin, TX',
307+
self.api.nearby_places(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA
308+
self.assertTrue(place_name_in_list('Austin, TX',
309+
self.api.reverse_geocode(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA
300310

301311
class TweepyCursorTests(unittest.TestCase):
302312

tweepy/api.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,32 +702,39 @@ def is_subscribed_list(self, owner, slug, user_id):
702702
""" geo/reverse_geocode """
703703
reverse_geocode = bind_api(
704704
path = '/geo/reverse_geocode.json',
705-
payload_type = 'json',
705+
payload_type = 'place', payload_list = True,
706706
allowed_param = ['lat', 'long', 'accuracy', 'granularity', 'max_results']
707707
)
708708

709709
""" geo/nearby_places """
710710
# listed as deprecated on twitter's API documents
711711
nearby_places = bind_api(
712712
path = '/geo/nearby_places.json',
713-
payload_type = 'json',
713+
payload_type = 'place', payload_list = True,
714714
allowed_param = ['lat', 'long', 'ip', 'accuracy', 'granularity', 'max_results']
715715
)
716716

717717
""" geo/id """
718718
geo_id = bind_api(
719719
path = '/geo/id/{id}.json',
720-
payload_type = 'json',
720+
payload_type = 'place',
721721
allowed_param = ['id']
722722
)
723723

724724
""" geo/search """
725725
geo_search = bind_api(
726726
path = '/geo/search.json',
727-
payload_type = 'json',
727+
payload_type = 'place', payload_list = True,
728728
allowed_param = ['lat', 'long', 'query', 'ip', 'granularity', 'accuracy', 'max_results', 'contained_within']
729729
)
730730

731+
""" geo/similar_places """
732+
geo_similar_places = bind_api(
733+
path = '/geo/similar_places.json',
734+
payload_type = 'place', payload_list = True,
735+
allowed_param = ['lat', 'long', 'name', 'contained_within']
736+
)
737+
731738
""" Internal use only """
732739
@staticmethod
733740
def _pack_image(filename, max_size):

tweepy/models.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ def parse(cls, api, json):
6262
setattr(status, 'source_url', None)
6363
elif k == 'retweeted_status':
6464
setattr(status, k, Status.parse(api, v))
65+
elif k == 'place':
66+
if v is not None:
67+
setattr(status, k, Place.parse(api, v))
68+
else:
69+
setattr(status, k, None)
6570
else:
6671
setattr(status, k, v)
6772
return status
@@ -321,6 +326,70 @@ def parse(cls, api, json):
321326
return json['ids']
322327

323328

329+
class BoundingBox(Model):
330+
331+
@classmethod
332+
def parse(cls, api, json):
333+
result = cls(api)
334+
if json is not None:
335+
for k, v in json.items():
336+
setattr(result, k, v)
337+
return result
338+
339+
def origin(self):
340+
"""
341+
Return longitude, latitude of southwest (bottom, left) corner of
342+
bounding box, as a tuple.
343+
344+
This assumes that bounding box is always a rectangle, which
345+
appears to be the case at present.
346+
"""
347+
return tuple(self.coordinates[0][0])
348+
349+
def corner(self):
350+
"""
351+
Return longitude, latitude of northeast (top, right) corner of
352+
bounding box, as a tuple.
353+
354+
This assumes that bounding box is always a rectangle, which
355+
appears to be the case at present.
356+
"""
357+
return tuple(self.coordinates[0][2])
358+
359+
360+
class Place(Model):
361+
362+
@classmethod
363+
def parse(cls, api, json):
364+
place = cls(api)
365+
for k, v in json.items():
366+
if k == 'bounding_box':
367+
# bounding_box value may be null (None.)
368+
# Example: "United States" (id=96683cc9126741d1)
369+
if v is not None:
370+
t = BoundingBox.parse(api, v)
371+
else:
372+
t = v
373+
setattr(place, k, t)
374+
elif k == 'contained_within':
375+
# contained_within is a list of Places.
376+
setattr(place, k, Place.parse_list(api, v))
377+
else:
378+
setattr(place, k, v)
379+
return place
380+
381+
@classmethod
382+
def parse_list(cls, api, json_list):
383+
if isinstance(json_list, list):
384+
item_list = json_list
385+
else:
386+
item_list = json_list['result']['places']
387+
388+
results = ResultSet()
389+
for obj in item_list:
390+
results.append(cls.parse(api, obj))
391+
return results
392+
324393
class ModelFactory(object):
325394
"""
326395
Used by parsers for creating instances
@@ -340,4 +409,6 @@ class ModelFactory(object):
340409

341410
json = JSONModel
342411
ids = IDModel
412+
place = Place
413+
bounding_box = BoundingBox
343414

0 commit comments

Comments
 (0)