Switch to using ipstack.com's free API for geoip lookup. Requires setting IPSTACK_ACCESS_KEY in settings.py now. Fixed #90
This commit is contained in:
parent
316a047f14
commit
501918da77
5 changed files with 131 additions and 34 deletions
115
events/ipstack.py
Normal file
115
events/ipstack.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
from collections import OrderedDict
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from geocoder.base import OneResult, MultipleResultsQuery
|
||||
|
||||
IPSTACK_URL = 'http://api.ipstack.com/{0}?access_key={1}&format=json&legacy=1'
|
||||
RESULT_CACHE = OrderedDict()
|
||||
CACHE_SIZE = getattr(settings, 'IPSTACK_CACHE_SIZE', 1000)
|
||||
|
||||
|
||||
class IPStackResult(OneResult):
|
||||
|
||||
@property
|
||||
def lat(self):
|
||||
return self.raw.get('latitude')
|
||||
|
||||
@property
|
||||
def lng(self):
|
||||
return self.raw.get('longitude')
|
||||
|
||||
@property
|
||||
def latlng(self):
|
||||
if self.ok:
|
||||
return [self.lat, self.lng]
|
||||
return None
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
if self.city:
|
||||
return u'{0}, {1} {2}'.format(self.city, self.state, self.country)
|
||||
elif self.state:
|
||||
return u'{0}, {1}'.format(self.state, self.country)
|
||||
elif self.country:
|
||||
return u'{0}'.format(self.country)
|
||||
return u''
|
||||
|
||||
@property
|
||||
def postal(self):
|
||||
zip_code = self.raw.get('zip_code')
|
||||
postal_code = self.raw.get('postal_code')
|
||||
if zip_code:
|
||||
return zip_code
|
||||
if postal_code:
|
||||
return postal_code
|
||||
|
||||
@property
|
||||
def city(self):
|
||||
return self.raw.get('city')
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self.raw.get('region')
|
||||
|
||||
@property
|
||||
def region_code(self):
|
||||
return self.raw.get('region_code')
|
||||
|
||||
@property
|
||||
def country(self):
|
||||
return self.raw.get('country_name')
|
||||
|
||||
@property
|
||||
def country_code3(self):
|
||||
return self.raw.get('country_code3')
|
||||
|
||||
@property
|
||||
def continent(self):
|
||||
return self.raw.get('continent')
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
return self.raw.get('timezone')
|
||||
|
||||
@property
|
||||
def area_code(self):
|
||||
return self.raw.get('area_code')
|
||||
|
||||
@property
|
||||
def dma_code(self):
|
||||
return self.raw.get('dma_code')
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
return self.raw.get('offset')
|
||||
|
||||
@property
|
||||
def organization(self):
|
||||
return self.raw.get('organization')
|
||||
|
||||
@property
|
||||
def ip(self):
|
||||
return self.raw.get('ip')
|
||||
|
||||
@property
|
||||
def time_zone(self):
|
||||
return self.raw.get('time_zone')
|
||||
|
||||
|
||||
def get_ipstack_geocoder(ip):
|
||||
if ip in RESULT_CACHE:
|
||||
return RESULT_CACHE[ip]
|
||||
ipstack_key = getattr(settings, 'IPSTACK_ACCESS_KEY', None)
|
||||
if ipstack_key is None:
|
||||
raise Exception("You must define IPSTACK_ACCESS_KEY in your setting to use ipstack.py geocoding")
|
||||
call_url = IPSTACK_URL.format(ip, ipstack_key)
|
||||
print("Calling ipstack: {0}".format(call_url))
|
||||
session = requests.Session()
|
||||
response = session.get(call_url)
|
||||
if response.status_code != 200:
|
||||
raise Exception("Call to ipstack.com returned status code {0}".format(response.status_code))
|
||||
result = IPStackResult(response.json())
|
||||
RESULT_CACHE[ip] = result
|
||||
if len(RESULT_CACHE) > CACHE_SIZE:
|
||||
RESULT_CACHE.popitem(last=False) # Discard the oldest entry
|
||||
return result
|
|
@ -2,14 +2,15 @@ from django.utils import timezone
|
|||
from django.conf import settings
|
||||
|
||||
from .models.locale import City
|
||||
from .ipstack import get_ipstack_geocoder
|
||||
|
||||
import math
|
||||
import pytz
|
||||
import datetime
|
||||
import geocoder
|
||||
|
||||
KM_PER_DEGREE_LAT = 110.574
|
||||
KM_PER_DEGREE_LNG = 111.320 # At the equator
|
||||
DEFAULT_NEAR_DISTANCE = 100 # kilometeres
|
||||
|
||||
class TimezoneChoices():
|
||||
|
||||
|
@ -29,12 +30,12 @@ def get_geoip(request):
|
|||
client_ip = get_client_ip(request)
|
||||
if client_ip == '127.0.0.1' or client_ip == 'localhost':
|
||||
if settings.DEBUG:
|
||||
client_ip = getattr(settings, 'DEBUG_IP', '8.8.8.8') # Try Google's server
|
||||
client_ip = getattr(settings, 'DEBUG_IP', '8.8.8.8') # Try Debug ip
|
||||
print("Client is localhost, using %s for geoip instead" % client_ip)
|
||||
else:
|
||||
raise Exception("Client is localhost")
|
||||
|
||||
g = geocoder.freegeoip(client_ip)
|
||||
g = get_ipstack_geocoder(client_ip)
|
||||
return g
|
||||
|
||||
def get_bounding_box(center, radius):
|
||||
|
@ -101,3 +102,5 @@ def get_nearest_city(ll, max_distance=100):
|
|||
else:
|
||||
return sorted(nearby_cities, key=lambda city: city_distance_from(ll, city))[0]
|
||||
return city
|
||||
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ STATIC_URL = '/static/'
|
|||
MEDIA_ROOT = './media/'
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
IPSTACK_ACCESS_KEY=None
|
||||
GOOGLE_ANALYTICS_ID=None
|
||||
GOOGLE_MAPS_API_KEY=None
|
||||
SOCIAL_AUTH_GITHUB_KEY=None
|
||||
|
@ -197,6 +198,7 @@ SETTINGS_EXPORT = [
|
|||
'SOCIAL_AUTH_FACEBOOK_KEY',
|
||||
'SOCIAL_AUTH_TWITTER_KEY',
|
||||
'SOCIAL_AUTH_LINKEDIN_KEY',
|
||||
'IPSTACK_ACCESS_KEY',
|
||||
]
|
||||
|
||||
# Make django messages framework use Bootstrap's alert style classes
|
||||
|
|
|
@ -4,17 +4,18 @@ from django.utils import timezone
|
|||
|
||||
from model_mommy import mommy
|
||||
import mock
|
||||
import geocoder
|
||||
import datetime
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from events.ipstack import get_ipstack_geocoder
|
||||
from events.models import Event, Place, Attendee, UserProfile
|
||||
# Create your tests here.
|
||||
|
||||
def mock_get_geoip(latlng=(0.0, 0.0)):
|
||||
def mock_get_geoip(latitude=0.0, longitude=0.0):
|
||||
def get_geoip(request):
|
||||
g = geocoder.ip('8.8.8.8')
|
||||
g.latlng = latlng
|
||||
g = get_ipstack_geocoder('8.8.8.8')
|
||||
g.raw['latitude'] = latitude
|
||||
g.raw['longitude'] = longitude
|
||||
return g
|
||||
return get_geoip
|
||||
|
||||
|
@ -26,7 +27,7 @@ class EventDisplayTests(TestCase):
|
|||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
||||
@mock.patch("events.location.get_geoip", mock_get_geoip((0.0, 0.0)))
|
||||
@mock.patch("events.location.get_geoip", mock_get_geoip(latitude=0.0, longitude=0.0))
|
||||
def test_events_list(self):
|
||||
place = mommy.make(Place, name="Test Place", latitude=0.0, longitude=0.0)
|
||||
event = mommy.make(Event, name="Test Event", place=place, start_time=timezone.now() + datetime.timedelta(days=1), end_time=timezone.now() + datetime.timedelta(days=2))
|
||||
|
@ -36,7 +37,7 @@ class EventDisplayTests(TestCase):
|
|||
response = c.get(resolve_url('all-events'))
|
||||
assert(response.status_code == 200)
|
||||
|
||||
@mock.patch("events.location.get_geoip", mock_get_geoip(None))
|
||||
@mock.patch("events.location.get_geoip", mock_get_geoip(latitude=None, longitude=None))
|
||||
def test_events_list_no_geoip(self):
|
||||
place = mommy.make(Place, name="Test Place", latitude=0.0, longitude=0.0)
|
||||
event = mommy.make(Event, name="Test Event", place=place, start_time=timezone.now() + datetime.timedelta(days=1), end_time=timezone.now() + datetime.timedelta(days=2))
|
||||
|
|
|
@ -2,12 +2,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
import datetime
|
||||
import simplejson
|
||||
import geocoder
|
||||
import math
|
||||
import traceback
|
||||
|
||||
from events.location import get_geoip, get_client_ip
|
||||
from .new_user import *
|
||||
|
||||
KM_PER_DEGREE_LAT = 110.574
|
||||
|
@ -15,19 +12,6 @@ KM_PER_DEGREE_LNG = 111.320 # At the equator
|
|||
DEFAULT_NEAR_DISTANCE = 100 # kilometeres
|
||||
|
||||
|
||||
def get_geoip(request):
|
||||
client_ip = get_client_ip(request)
|
||||
if client_ip == '127.0.0.1' or client_ip == 'localhost':
|
||||
if settings.DEBUG:
|
||||
client_ip = '8.8.8.8' # Try Google's server
|
||||
print("Client is localhost, using 8.8.8.8 for geoip instead")
|
||||
else:
|
||||
raise Exception("Client is localhost")
|
||||
|
||||
g = geocoder.ip(client_ip)
|
||||
return g
|
||||
|
||||
|
||||
def get_nearby_teams(request, near_distance=DEFAULT_NEAR_DISTANCE):
|
||||
g = get_geoip(request)
|
||||
if g.latlng is None or g.latlng[0] is None or g.latlng[1] is None:
|
||||
|
@ -46,13 +30,5 @@ def get_nearby_teams(request, near_distance=DEFAULT_NEAR_DISTANCE):
|
|||
return Team.objects.none()
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue