Ticket #16553: 16553.2.diff
File 16553.2.diff, 46.5 KB (added by , 13 years ago) |
---|
-
new file django/contrib/gis/geoip/__init__.py
diff -r f22df4c44377 django/contrib/gis/geoip/__init__.py
- + 1 """ 2 This module houses the GeoIP object, a ctypes wrapper for the MaxMind GeoIP(R) 3 C API (http://www.maxmind.com/app/c). This is an alternative to the GPL 4 licensed Python GeoIP interface provided by MaxMind. 5 6 GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts. 7 8 For IP-based geolocation, this module requires the GeoLite Country and City 9 datasets, in binary format (CSV will not work!). The datasets may be 10 downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/. 11 Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory 12 corresponding to settings.GEOIP_PATH. 13 """ 14 try: 15 from django.contrib.gis.geoip.base import GeoIP, GeoIPException 16 HAS_GEOIP = True 17 except: 18 HAS_GEOIP = False -
new file django/contrib/gis/geoip/base.py
diff -r f22df4c44377 django/contrib/gis/geoip/base.py
- + 1 import os 2 import re 3 from ctypes import c_char_p 4 5 from django.core.validators import ipv4_re 6 from django.contrib.gis.geoip.libgeoip import GEOIP_SETTINGS 7 from django.contrib.gis.geoip.prototypes import ( 8 GeoIPRecord, GeoIPTag, GeoIP_open, GeoIP_delete, GeoIP_database_info, 9 GeoIP_lib_version, GeoIP_record_by_addr, GeoIP_record_by_name, 10 GeoIP_country_code_by_addr, GeoIP_country_code_by_name, 11 GeoIP_country_name_by_addr, GeoIP_country_name_by_name) 12 13 # Regular expressions for recognizing the GeoIP free database editions. 14 free_regex = re.compile(r'^GEO-\d{3}FREE') 15 lite_regex = re.compile(r'^GEO-\d{3}LITE') 16 17 #### GeoIP classes #### 18 class GeoIPException(Exception): pass 19 20 class GeoIP(object): 21 # The flags for GeoIP memory caching. 22 # GEOIP_STANDARD - read database from filesystem, uses least memory. 23 # 24 # GEOIP_MEMORY_CACHE - load database into memory, faster performance 25 # but uses more memory 26 # 27 # GEOIP_CHECK_CACHE - check for updated database. If database has been 28 # updated, reload filehandle and/or memory cache. This option 29 # is not thread safe. 30 # 31 # GEOIP_INDEX_CACHE - just cache the most frequently accessed index 32 # portion of the database, resulting in faster lookups than 33 # GEOIP_STANDARD, but less memory usage than GEOIP_MEMORY_CACHE - 34 # useful for larger databases such as GeoIP Organization and 35 # GeoIP City. Note, for GeoIP Country, Region and Netspeed 36 # databases, GEOIP_INDEX_CACHE is equivalent to GEOIP_MEMORY_CACHE 37 # 38 # GEOIP_MMAP_CACHE - load database into mmap shared memory ( not available 39 # on Windows). 40 GEOIP_STANDARD = 0 41 GEOIP_MEMORY_CACHE = 1 42 GEOIP_CHECK_CACHE = 2 43 GEOIP_INDEX_CACHE = 4 44 GEOIP_MMAP_CACHE = 8 45 cache_options = dict((opt, None) for opt in (0, 1, 2, 4, 8)) 46 47 # Paths to the city & country binary databases. 48 _city_file = '' 49 _country_file = '' 50 51 # Initially, pointers to GeoIP file references are NULL. 52 _city = None 53 _country = None 54 55 def __init__(self, path=None, cache=0, country=None, city=None): 56 """ 57 Initializes the GeoIP object, no parameters are required to use default 58 settings. Keyword arguments may be passed in to customize the locations 59 of the GeoIP data sets. 60 61 * path: Base directory to where GeoIP data is located or the full path 62 to where the city or country data files (*.dat) are located. 63 Assumes that both the city and country data sets are located in 64 this directory; overrides the GEOIP_PATH settings attribute. 65 66 * cache: The cache settings when opening up the GeoIP datasets, 67 and may be an integer in (0, 1, 2, 4, 8) corresponding to 68 the GEOIP_STANDARD, GEOIP_MEMORY_CACHE, GEOIP_CHECK_CACHE, 69 GEOIP_INDEX_CACHE, and GEOIP_MMAP_CACHE, `GeoIPOptions` C API 70 settings, respectively. Defaults to 0, meaning that the data is read 71 from the disk. 72 73 * country: The name of the GeoIP country data file. Defaults to 74 'GeoIP.dat'; overrides the GEOIP_COUNTRY settings attribute. 75 76 * city: The name of the GeoIP city data file. Defaults to 77 'GeoLiteCity.dat'; overrides the GEOIP_CITY settings attribute. 78 """ 79 # Checking the given cache option. 80 if cache in self.cache_options: 81 self._cache = cache 82 else: 83 raise GeoIPException('Invalid GeoIP caching option: %s' % cache) 84 85 # Getting the GeoIP data path. 86 if not path: 87 path = GEOIP_SETTINGS.get('GEOIP_PATH', None) 88 if not path: raise GeoIPException('GeoIP path must be provided via parameter or the GEOIP_PATH setting.') 89 if not isinstance(path, basestring): 90 raise TypeError('Invalid path type: %s' % type(path).__name__) 91 92 if os.path.isdir(path): 93 # Constructing the GeoIP database filenames using the settings 94 # dictionary. If the database files for the GeoLite country 95 # and/or city datasets exist, then try and open them. 96 country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat')) 97 if os.path.isfile(country_db): 98 self._country = GeoIP_open(country_db, cache) 99 self._country_file = country_db 100 101 city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat')) 102 if os.path.isfile(city_db): 103 self._city = GeoIP_open(city_db, cache) 104 self._city_file = city_db 105 elif os.path.isfile(path): 106 # Otherwise, some detective work will be needed to figure 107 # out whether the given database path is for the GeoIP country 108 # or city databases. 109 ptr = GeoIP_open(path, cache) 110 info = GeoIP_database_info(ptr) 111 if lite_regex.match(info): 112 # GeoLite City database detected. 113 self._city = ptr 114 self._city_file = path 115 elif free_regex.match(info): 116 # GeoIP Country database detected. 117 self._country = ptr 118 self._country_file = path 119 else: 120 raise GeoIPException('Unable to recognize database edition: %s' % info) 121 else: 122 raise GeoIPException('GeoIP path must be a valid file or directory.') 123 124 def __del__(self): 125 # Cleaning any GeoIP file handles lying around. 126 if self._country: GeoIP_delete(self._country) 127 if self._city: GeoIP_delete(self._city) 128 129 def _check_query(self, query, country=False, city=False, city_or_country=False): 130 "Helper routine for checking the query and database availability." 131 # Making sure a string was passed in for the query. 132 if not isinstance(query, basestring): 133 raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__) 134 135 # Extra checks for the existence of country and city databases. 136 if city_or_country and not (self._country or self._city): 137 raise GeoIPException('Invalid GeoIP country and city data files.') 138 elif country and not self._country: 139 raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file) 140 elif city and not self._city: 141 raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file) 142 143 def city(self, query): 144 """ 145 Returns a dictionary of city information for the given IP address or 146 Fully Qualified Domain Name (FQDN). Some information in the dictionary 147 may be undefined (None). 148 """ 149 self._check_query(query, city=True) 150 if ipv4_re.match(query): 151 # If an IP address was passed in 152 return GeoIP_record_by_addr(self._city, c_char_p(query)) 153 else: 154 # If a FQDN was passed in. 155 return GeoIP_record_by_name(self._city, c_char_p(query)) 156 157 def country_code(self, query): 158 "Returns the country code for the given IP Address or FQDN." 159 self._check_query(query, city_or_country=True) 160 if self._country: 161 if ipv4_re.match(query): 162 return GeoIP_country_code_by_addr(self._country, query) 163 else: 164 return GeoIP_country_code_by_name(self._country, query) 165 else: 166 return self.city(query)['country_code'] 167 168 def country_name(self, query): 169 "Returns the country name for the given IP Address or FQDN." 170 self._check_query(query, city_or_country=True) 171 if self._country: 172 if ipv4_re.match(query): 173 return GeoIP_country_name_by_addr(self._country, query) 174 else: 175 return GeoIP_country_name_by_name(self._country, query) 176 else: 177 return self.city(query)['country_name'] 178 179 def country(self, query): 180 """ 181 Returns a dictonary with with the country code and name when given an 182 IP address or a Fully Qualified Domain Name (FQDN). For example, both 183 '24.124.1.80' and 'djangoproject.com' are valid parameters. 184 """ 185 # Returning the country code and name 186 return {'country_code' : self.country_code(query), 187 'country_name' : self.country_name(query), 188 } 189 190 #### Coordinate retrieval routines #### 191 def coords(self, query, ordering=('longitude', 'latitude')): 192 cdict = self.city(query) 193 if cdict is None: return None 194 else: return tuple(cdict[o] for o in ordering) 195 196 def lon_lat(self, query): 197 "Returns a tuple of the (longitude, latitude) for the given query." 198 return self.coords(query) 199 200 def lat_lon(self, query): 201 "Returns a tuple of the (latitude, longitude) for the given query." 202 return self.coords(query, ('latitude', 'longitude')) 203 204 def geos(self, query): 205 "Returns a GEOS Point object for the given query." 206 ll = self.lon_lat(query) 207 if ll: 208 from django.contrib.gis.geos import Point 209 return Point(ll, srid=4326) 210 else: 211 return None 212 213 #### GeoIP Database Information Routines #### 214 @property 215 def country_info(self): 216 "Returns information about the GeoIP country database." 217 if self._country is None: 218 ci = 'No GeoIP Country data in "%s"' % self._country_file 219 else: 220 ci = GeoIP_database_info(self._country) 221 return ci 222 223 @property 224 def city_info(self): 225 "Retuns information about the GeoIP city database." 226 if self._city is None: 227 ci = 'No GeoIP City data in "%s"' % self._city_file 228 else: 229 ci = GeoIP_database_info(self._city) 230 return ci 231 232 @property 233 def info(self): 234 "Returns information about the GeoIP library and databases in use." 235 info = '' 236 if GeoIP_lib_version: 237 info += 'GeoIP Library:\n\t%s\n' % GeoIP_lib_version() 238 return info + 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info) 239 240 #### Methods for compatibility w/the GeoIP-Python API. #### 241 @classmethod 242 def open(cls, full_path, cache): 243 return GeoIP(full_path, cache) 244 245 def _rec_by_arg(self, arg): 246 if self._city: 247 return self.city(arg) 248 else: 249 return self.country(arg) 250 region_by_addr = city 251 region_by_name = city 252 record_by_addr = _rec_by_arg 253 record_by_name = _rec_by_arg 254 country_code_by_addr = country_code 255 country_code_by_name = country_code 256 country_name_by_addr = country_name 257 country_name_by_name = country_name -
new file django/contrib/gis/geoip/libgeoip.py
diff -r f22df4c44377 django/contrib/gis/geoip/libgeoip.py
- + 1 import os 2 from ctypes import CDLL 3 from ctypes.util import find_library 4 from django.conf import settings 5 6 # Creating the settings dictionary with any settings, if needed. 7 GEOIP_SETTINGS = dict((key, getattr(settings, key)) 8 for key in ('GEOIP_PATH', 'GEOIP_LIBRARY_PATH', 'GEOIP_COUNTRY', 'GEOIP_CITY') 9 if hasattr(settings, key)) 10 lib_path = GEOIP_SETTINGS.get('GEOIP_LIBRARY_PATH', None) 11 12 # The shared library for the GeoIP C API. May be downloaded 13 # from http://www.maxmind.com/download/geoip/api/c/ 14 if lib_path: 15 lib_name = None 16 else: 17 # TODO: Is this really the library name for Windows? 18 lib_name = 'GeoIP' 19 20 # Getting the path to the GeoIP library. 21 if lib_name: lib_path = find_library(lib_name) 22 if lib_path is None: raise GeoIPException('Could not find the GeoIP library (tried "%s"). ' 23 'Try setting GEOIP_LIBRARY_PATH in your settings.' % lib_name) 24 lgeoip = CDLL(lib_path) 25 26 # Getting the C `free` for the platform. 27 if os.name == 'nt': 28 libc = CDLL('msvcrt') 29 else: 30 libc = CDLL(None) 31 free = libc.free -
new file django/contrib/gis/geoip/prototypes.py
diff -r f22df4c44377 django/contrib/gis/geoip/prototypes.py
- + 1 from ctypes import c_char_p, c_float, c_int, string_at, Structure, POINTER 2 from django.contrib.gis.geoip.libgeoip import lgeoip, free 3 4 #### GeoIP C Structure definitions #### 5 6 class GeoIPRecord(Structure): 7 _fields_ = [('country_code', c_char_p), 8 ('country_code3', c_char_p), 9 ('country_name', c_char_p), 10 ('region', c_char_p), 11 ('city', c_char_p), 12 ('postal_code', c_char_p), 13 ('latitude', c_float), 14 ('longitude', c_float), 15 # TODO: In 1.4.6 this changed from `int dma_code;` to 16 # `union {int metro_code; int dma_code;};`. Change 17 # to a `ctypes.Union` in to accomodate in future when 18 # pre-1.4.6 versions are no longer distributed. 19 ('dma_code', c_int), 20 ('area_code', c_int), 21 ('charset', c_int), 22 ('continent_code', c_char_p), 23 ] 24 geoip_char_fields = [name for name, ctype in GeoIPRecord._fields_ if ctype is c_char_p] 25 geoip_encodings = { 0: 'iso-8859-1', 26 1: 'utf8', 27 } 28 29 class GeoIPTag(Structure): pass 30 31 RECTYPE = POINTER(GeoIPRecord) 32 DBTYPE = POINTER(GeoIPTag) 33 34 #### ctypes function prototypes #### 35 36 # GeoIP_lib_version appeared in version 1.4.7. 37 if hasattr(lgeoip, 'GeoIP_lib_version'): 38 GeoIP_lib_version = lgeoip.GeoIP_lib_version 39 GeoIP_lib_version.argtypes = None 40 GeoIP_lib_version.restype = c_char_p 41 else: 42 GeoIP_lib_version = None 43 44 # For freeing memory allocated within a record 45 GeoIPRecord_delete = lgeoip.GeoIPRecord_delete 46 GeoIPRecord_delete.argtypes = [RECTYPE] 47 GeoIPRecord_delete.restype = None 48 49 # For retrieving records by name or address. 50 def check_record(result, func, cargs): 51 if bool(result): 52 # Checking the pointer to the C structure, if valid pull out elements 53 # into a dicionary. 54 rec = result.contents 55 record = dict((fld, getattr(rec, fld)) for fld, ctype in rec._fields_) 56 57 # Now converting the strings to unicode using the proper encoding. 58 encoding = geoip_encodings[record['charset']] 59 for char_field in geoip_char_fields: 60 if record[char_field]: 61 record[char_field] = record[char_field].decode(encoding) 62 63 # Free the memory allocated for the struct & return. 64 GeoIPRecord_delete(result) 65 return record 66 else: 67 return None 68 69 def record_output(func): 70 func.argtypes = [DBTYPE, c_char_p] 71 func.restype = RECTYPE 72 func.errcheck = check_record 73 return func 74 GeoIP_record_by_addr = record_output(lgeoip.GeoIP_record_by_addr) 75 GeoIP_record_by_name = record_output(lgeoip.GeoIP_record_by_name) 76 77 78 # For opening & closing GeoIP database files. 79 GeoIP_open = lgeoip.GeoIP_open 80 GeoIP_open.restype = DBTYPE 81 GeoIP_delete = lgeoip.GeoIP_delete 82 GeoIP_delete.argtypes = [DBTYPE] 83 GeoIP_delete.restype = None 84 85 # This is so the string pointer can be freed within Python. 86 class geoip_char_p(c_char_p): 87 pass 88 89 def check_string(result, func, cargs): 90 if result: 91 s = string_at(result) 92 free(result) 93 else: 94 s = '' 95 return s 96 97 GeoIP_database_info = lgeoip.GeoIP_database_info 98 GeoIP_database_info.restype = geoip_char_p 99 GeoIP_database_info.errcheck = check_string 100 101 # String output routines. 102 def string_output(func): 103 func.restype = c_char_p 104 return func 105 106 GeoIP_country_code_by_addr = string_output(lgeoip.GeoIP_country_code_by_addr) 107 GeoIP_country_code_by_name = string_output(lgeoip.GeoIP_country_code_by_name) 108 GeoIP_country_name_by_addr = string_output(lgeoip.GeoIP_country_name_by_addr) 109 GeoIP_country_name_by_name = string_output(lgeoip.GeoIP_country_name_by_name) -
new file django/contrib/gis/geoip/tests.py
diff -r f22df4c44377 django/contrib/gis/geoip/tests.py
- + 1 import os 2 from django.conf import settings 3 from django.contrib.gis.geos import GEOSGeometry 4 from django.contrib.gis.geoip import GeoIP, GeoIPException 5 from django.utils import unittest 6 7 # Note: Requires use of both the GeoIP country and city datasets. 8 # The GEOIP_DATA path should be the only setting set (the directory 9 # should contain links or the actual database files 'GeoIP.dat' and 10 # 'GeoLiteCity.dat'. 11 class GeoIPTest(unittest.TestCase): 12 13 def test01_init(self): 14 "Testing GeoIP initialization." 15 g1 = GeoIP() # Everything inferred from GeoIP path 16 path = settings.GEOIP_PATH 17 g2 = GeoIP(path, 0) # Passing in data path explicitly. 18 g3 = GeoIP.open(path, 0) # MaxMind Python API syntax. 19 20 for g in (g1, g2, g3): 21 self.assertEqual(True, bool(g._country)) 22 self.assertEqual(True, bool(g._city)) 23 24 # Only passing in the location of one database. 25 city = os.path.join(path, 'GeoLiteCity.dat') 26 cntry = os.path.join(path, 'GeoIP.dat') 27 g4 = GeoIP(city, country='') 28 self.assertEqual(None, g4._country) 29 g5 = GeoIP(cntry, city='') 30 self.assertEqual(None, g5._city) 31 32 # Improper parameters. 33 bad_params = (23, 'foo', 15.23) 34 for bad in bad_params: 35 self.assertRaises(GeoIPException, GeoIP, cache=bad) 36 if isinstance(bad, basestring): 37 e = GeoIPException 38 else: 39 e = TypeError 40 self.assertRaises(e, GeoIP, bad, 0) 41 42 def test02_bad_query(self): 43 "Testing GeoIP query parameter checking." 44 cntry_g = GeoIP(city='<foo>') 45 # No city database available, these calls should fail. 46 self.assertRaises(GeoIPException, cntry_g.city, 'google.com') 47 self.assertRaises(GeoIPException, cntry_g.coords, 'yahoo.com') 48 49 # Non-string query should raise TypeError 50 self.assertRaises(TypeError, cntry_g.country_code, 17) 51 self.assertRaises(TypeError, cntry_g.country_name, GeoIP) 52 53 def test03_country(self): 54 "Testing GeoIP country querying methods." 55 g = GeoIP(city='<foo>') 56 57 fqdn = 'www.google.com' 58 addr = '12.215.42.19' 59 60 for query in (fqdn, addr): 61 for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name): 62 self.assertEqual('US', func(query)) 63 for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name): 64 self.assertEqual('United States', func(query)) 65 self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'}, 66 g.country(query)) 67 68 def test04_city(self): 69 "Testing GeoIP city querying methods." 70 g = GeoIP(country='<foo>') 71 72 addr = '128.249.1.1' 73 fqdn = 'tmc.edu' 74 for query in (fqdn, addr): 75 # Country queries should still work. 76 for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name): 77 self.assertEqual('US', func(query)) 78 for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name): 79 self.assertEqual('United States', func(query)) 80 self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'}, 81 g.country(query)) 82 83 # City information dictionary. 84 d = g.city(query) 85 self.assertEqual('USA', d['country_code3']) 86 self.assertEqual('Houston', d['city']) 87 self.assertEqual('TX', d['region']) 88 self.assertEqual(713, d['area_code']) 89 geom = g.geos(query) 90 self.failIf(not isinstance(geom, GEOSGeometry)) 91 lon, lat = (-95.4010, 29.7079) 92 lat_lon = g.lat_lon(query) 93 lat_lon = (lat_lon[1], lat_lon[0]) 94 for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon): 95 self.assertAlmostEqual(lon, tup[0], 4) 96 self.assertAlmostEqual(lat, tup[1], 4) 97 98 def test05_unicode(self): 99 "Testing that GeoIP strings are properly encoded, see #16553." 100 g = GeoIP() 101 d = g.city('62.224.93.23') 102 self.assertEqual(u'Sch\xf6mberg', d['city']) 103 104 105 def suite(): 106 s = unittest.TestSuite() 107 s.addTest(unittest.makeSuite(GeoIPTest)) 108 return s 109 110 def run(verbosity=1): 111 unittest.TextTestRunner(verbosity=verbosity).run(suite()) -
django/contrib/gis/tests/__init__.py
diff -r f22df4c44377 django/contrib/gis/tests/__init__.py
a b 78 78 sys.stderr.write('GDAL not available - no tests requiring GDAL will be run.\n') 79 79 80 80 # Add GeoIP tests to the suite, if the library and data is available. 81 from django.contrib.gis. utilsimport HAS_GEOIP81 from django.contrib.gis.geoip import HAS_GEOIP 82 82 if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'): 83 from django.contrib.gis. tests import test_geoip84 suite.addTest( test_geoip.suite())83 from django.contrib.gis.geoip import tests as geoip_tests 84 suite.addTest(geoip_tests.suite()) 85 85 86 86 # Finally, adding the suites for each of the GeoDjango test apps. 87 87 if apps: -
deleted file django/contrib/gis/tests/test_geoip.py
diff -r f22df4c44377 django/contrib/gis/tests/test_geoip.py
+ - 1 import os2 import unittest3 from django.db import settings4 from django.contrib.gis.geos import GEOSGeometry5 from django.contrib.gis.utils import GeoIP, GeoIPException6 7 # Note: Requires use of both the GeoIP country and city datasets.8 # The GEOIP_DATA path should be the only setting set (the directory9 # should contain links or the actual database files 'GeoIP.dat' and10 # 'GeoLiteCity.dat'.11 class GeoIPTest(unittest.TestCase):12 13 def test01_init(self):14 "Testing GeoIP initialization."15 g1 = GeoIP() # Everything inferred from GeoIP path16 path = settings.GEOIP_PATH17 g2 = GeoIP(path, 0) # Passing in data path explicitly.18 g3 = GeoIP.open(path, 0) # MaxMind Python API syntax.19 20 for g in (g1, g2, g3):21 self.assertEqual(True, bool(g._country))22 self.assertEqual(True, bool(g._city))23 24 # Only passing in the location of one database.25 city = os.path.join(path, 'GeoLiteCity.dat')26 cntry = os.path.join(path, 'GeoIP.dat')27 g4 = GeoIP(city, country='')28 self.assertEqual(None, g4._country)29 g5 = GeoIP(cntry, city='')30 self.assertEqual(None, g5._city)31 32 # Improper parameters.33 bad_params = (23, 'foo', 15.23)34 for bad in bad_params:35 self.assertRaises(GeoIPException, GeoIP, cache=bad)36 if isinstance(bad, basestring):37 e = GeoIPException38 else:39 e = TypeError40 self.assertRaises(e, GeoIP, bad, 0)41 42 def test02_bad_query(self):43 "Testing GeoIP query parameter checking."44 cntry_g = GeoIP(city='<foo>')45 # No city database available, these calls should fail.46 self.assertRaises(GeoIPException, cntry_g.city, 'google.com')47 self.assertRaises(GeoIPException, cntry_g.coords, 'yahoo.com')48 49 # Non-string query should raise TypeError50 self.assertRaises(TypeError, cntry_g.country_code, 17)51 self.assertRaises(TypeError, cntry_g.country_name, GeoIP)52 53 def test03_country(self):54 "Testing GeoIP country querying methods."55 g = GeoIP(city='<foo>')56 57 fqdn = 'www.google.com'58 addr = '12.215.42.19'59 60 for query in (fqdn, addr):61 for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name):62 self.assertEqual('US', func(query))63 for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name):64 self.assertEqual('United States', func(query))65 self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'},66 g.country(query))67 68 def test04_city(self):69 "Testing GeoIP city querying methods."70 g = GeoIP(country='<foo>')71 72 addr = '130.80.29.3'73 fqdn = 'chron.com'74 for query in (fqdn, addr):75 # Country queries should still work.76 for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name):77 self.assertEqual('US', func(query))78 for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name):79 self.assertEqual('United States', func(query))80 self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'},81 g.country(query))82 83 # City information dictionary.84 d = g.city(query)85 self.assertEqual('USA', d['country_code3'])86 self.assertEqual('Houston', d['city'])87 self.assertEqual('TX', d['region'])88 self.assertEqual(713, d['area_code'])89 geom = g.geos(query)90 self.failIf(not isinstance(geom, GEOSGeometry))91 lon, lat = (-95.3670, 29.7523)92 lat_lon = g.lat_lon(query)93 lat_lon = (lat_lon[1], lat_lon[0])94 for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):95 self.assertAlmostEqual(lon, tup[0], 4)96 self.assertAlmostEqual(lat, tup[1], 4)97 98 def suite():99 s = unittest.TestSuite()100 s.addTest(unittest.makeSuite(GeoIPTest))101 return s102 103 def run(verbosity=2):104 unittest.TextTestRunner(verbosity=verbosity).run(suite()) -
django/contrib/gis/utils/__init__.py
diff -r f22df4c44377 django/contrib/gis/utils/__init__.py
a b 14 14 except: 15 15 pass 16 16 17 # Attempting to import the GeoIP class. 18 try: 19 from django.contrib.gis.utils.geoip import GeoIP, GeoIPException 20 HAS_GEOIP = True 21 except: 22 HAS_GEOIP = False 17 # GeoIP now lives in `django.contrib.gis.geoip`; this shortcut will be deprecated 18 # in Django 1.5. 19 from django.contrib.gis.utils import geoip 20 HAS_GEOIP = geoip.HAS_GEOIP 21 if HAS_GEOIP: 22 GeoIP = geoip.GeoIP 23 GeoIPException = geoip.GeoIPException 23 24 24 25 from django.contrib.gis.utils.wkt import precision_wkt 25 26 -
django/contrib/gis/utils/geoip.py
diff -r f22df4c44377 django/contrib/gis/utils/geoip.py
a b 1 """ 2 This module houses the GeoIP object, a ctypes wrapper for the MaxMind GeoIP(R) 3 C API (http://www.maxmind.com/app/c). This is an alternative to the GPL 4 licensed Python GeoIP interface provided by MaxMind. 1 import warnings 5 2 6 GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts. 3 from django.contrib.gis import geoip 4 HAS_GEOIP = geoip.HAS_GEOIP 5 if HAS_GEOIP: 6 BaseGeoIP = geoip.GeoIP 7 GeoIPException = geoip.GeoIPException 7 8 8 For IP-based geolocation, this module requires the GeoLite Country and City 9 datasets, in binary format (CSV will not work!). The datasets may be 10 downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/. 11 Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory 12 corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples 13 below for more details. 14 15 TODO: Verify compatibility with Windows. 16 17 Example: 18 19 >>> from django.contrib.gis.utils import GeoIP 20 >>> g = GeoIP() 21 >>> g.country('google.com') 22 {'country_code': 'US', 'country_name': 'United States'} 23 >>> g.city('72.14.207.99') 24 {'area_code': 650, 25 'city': 'Mountain View', 26 'country_code': 'US', 27 'country_code3': 'USA', 28 'country_name': 'United States', 29 'dma_code': 807, 30 'latitude': 37.419200897216797, 31 'longitude': -122.05740356445312, 32 'postal_code': '94043', 33 'region': 'CA'} 34 >>> g.lat_lon('salon.com') 35 (37.789798736572266, -122.39420318603516) 36 >>> g.lon_lat('uh.edu') 37 (-95.415199279785156, 29.77549934387207) 38 >>> g.geos('24.124.1.80').wkt 39 'POINT (-95.2087020874023438 39.0392990112304688)' 40 """ 41 import os 42 import re 43 from ctypes import c_char_p, c_float, c_int, Structure, CDLL, POINTER 44 from ctypes.util import find_library 45 from django.conf import settings 46 if not settings.configured: settings.configure() 47 48 # Creating the settings dictionary with any settings, if needed. 49 GEOIP_SETTINGS = dict((key, getattr(settings, key)) 50 for key in ('GEOIP_PATH', 'GEOIP_LIBRARY_PATH', 'GEOIP_COUNTRY', 'GEOIP_CITY') 51 if hasattr(settings, key)) 52 lib_path = GEOIP_SETTINGS.get('GEOIP_LIBRARY_PATH', None) 53 54 # GeoIP Exception class. 55 class GeoIPException(Exception): pass 56 57 # The shared library for the GeoIP C API. May be downloaded 58 # from http://www.maxmind.com/download/geoip/api/c/ 59 if lib_path: 60 lib_name = None 61 else: 62 # TODO: Is this really the library name for Windows? 63 lib_name = 'GeoIP' 64 65 # Getting the path to the GeoIP library. 66 if lib_name: lib_path = find_library(lib_name) 67 if lib_path is None: raise GeoIPException('Could not find the GeoIP library (tried "%s"). ' 68 'Try setting GEOIP_LIBRARY_PATH in your settings.' % lib_name) 69 lgeoip = CDLL(lib_path) 70 71 # Regular expressions for recognizing IP addresses and the GeoIP 72 # free database editions. 73 ipregex = re.compile(r'^(?P<w>\d\d?\d?)\.(?P<x>\d\d?\d?)\.(?P<y>\d\d?\d?)\.(?P<z>\d\d?\d?)$') 74 free_regex = re.compile(r'^GEO-\d{3}FREE') 75 lite_regex = re.compile(r'^GEO-\d{3}LITE') 76 77 #### GeoIP C Structure definitions #### 78 class GeoIPRecord(Structure): 79 _fields_ = [('country_code', c_char_p), 80 ('country_code3', c_char_p), 81 ('country_name', c_char_p), 82 ('region', c_char_p), 83 ('city', c_char_p), 84 ('postal_code', c_char_p), 85 ('latitude', c_float), 86 ('longitude', c_float), 87 # TODO: In 1.4.6 this changed from `int dma_code;` to 88 # `union {int metro_code; int dma_code;};`. Change 89 # to a `ctypes.Union` in to accomodate in future when 90 # pre-1.4.6 versions are no longer distributed. 91 ('dma_code', c_int), 92 ('area_code', c_int), 93 # TODO: The following structure fields were added in 1.4.3 -- 94 # uncomment these fields when sure previous versions are no 95 # longer distributed by package maintainers. 96 #('charset', c_int), 97 #('continent_code', c_char_p), 98 ] 99 class GeoIPTag(Structure): pass 100 101 #### ctypes function prototypes #### 102 RECTYPE = POINTER(GeoIPRecord) 103 DBTYPE = POINTER(GeoIPTag) 104 105 # For retrieving records by name or address. 106 def record_output(func): 107 func.restype = RECTYPE 108 return func 109 rec_by_addr = record_output(lgeoip.GeoIP_record_by_addr) 110 rec_by_name = record_output(lgeoip.GeoIP_record_by_name) 111 112 # For opening & closing GeoIP database files. 113 geoip_open = lgeoip.GeoIP_open 114 geoip_open.restype = DBTYPE 115 geoip_close = lgeoip.GeoIP_delete 116 geoip_close.argtypes = [DBTYPE] 117 geoip_close.restype = None 118 119 # String output routines. 120 def string_output(func): 121 func.restype = c_char_p 122 return func 123 geoip_dbinfo = string_output(lgeoip.GeoIP_database_info) 124 cntry_code_by_addr = string_output(lgeoip.GeoIP_country_code_by_addr) 125 cntry_code_by_name = string_output(lgeoip.GeoIP_country_code_by_name) 126 cntry_name_by_addr = string_output(lgeoip.GeoIP_country_name_by_addr) 127 cntry_name_by_name = string_output(lgeoip.GeoIP_country_name_by_name) 128 129 #### GeoIP class #### 130 class GeoIP(object): 131 # The flags for GeoIP memory caching. 132 # GEOIP_STANDARD - read database from filesystem, uses least memory. 133 # 134 # GEOIP_MEMORY_CACHE - load database into memory, faster performance 135 # but uses more memory 136 # 137 # GEOIP_CHECK_CACHE - check for updated database. If database has been updated, 138 # reload filehandle and/or memory cache. 139 # 140 # GEOIP_INDEX_CACHE - just cache 141 # the most frequently accessed index portion of the database, resulting 142 # in faster lookups than GEOIP_STANDARD, but less memory usage than 143 # GEOIP_MEMORY_CACHE - useful for larger databases such as 144 # GeoIP Organization and GeoIP City. Note, for GeoIP Country, Region 145 # and Netspeed databases, GEOIP_INDEX_CACHE is equivalent to GEOIP_MEMORY_CACHE 146 # 147 GEOIP_STANDARD = 0 148 GEOIP_MEMORY_CACHE = 1 149 GEOIP_CHECK_CACHE = 2 150 GEOIP_INDEX_CACHE = 4 151 cache_options = dict((opt, None) for opt in (0, 1, 2, 4)) 152 _city_file = '' 153 _country_file = '' 154 155 # Initially, pointers to GeoIP file references are NULL. 156 _city = None 157 _country = None 158 159 def __init__(self, path=None, cache=0, country=None, city=None): 160 """ 161 Initializes the GeoIP object, no parameters are required to use default 162 settings. Keyword arguments may be passed in to customize the locations 163 of the GeoIP data sets. 164 165 * path: Base directory to where GeoIP data is located or the full path 166 to where the city or country data files (*.dat) are located. 167 Assumes that both the city and country data sets are located in 168 this directory; overrides the GEOIP_PATH settings attribute. 169 170 * cache: The cache settings when opening up the GeoIP datasets, 171 and may be an integer in (0, 1, 2, 4) corresponding to 172 the GEOIP_STANDARD, GEOIP_MEMORY_CACHE, GEOIP_CHECK_CACHE, 173 and GEOIP_INDEX_CACHE `GeoIPOptions` C API settings, 174 respectively. Defaults to 0, meaning that the data is read 175 from the disk. 176 177 * country: The name of the GeoIP country data file. Defaults to 178 'GeoIP.dat'; overrides the GEOIP_COUNTRY settings attribute. 179 180 * city: The name of the GeoIP city data file. Defaults to 181 'GeoLiteCity.dat'; overrides the GEOIP_CITY settings attribute. 182 """ 183 # Checking the given cache option. 184 if cache in self.cache_options: 185 self._cache = self.cache_options[cache] 186 else: 187 raise GeoIPException('Invalid caching option: %s' % cache) 188 189 # Getting the GeoIP data path. 190 if not path: 191 path = GEOIP_SETTINGS.get('GEOIP_PATH', None) 192 if not path: raise GeoIPException('GeoIP path must be provided via parameter or the GEOIP_PATH setting.') 193 if not isinstance(path, basestring): 194 raise TypeError('Invalid path type: %s' % type(path).__name__) 195 196 if os.path.isdir(path): 197 # Constructing the GeoIP database filenames using the settings 198 # dictionary. If the database files for the GeoLite country 199 # and/or city datasets exist, then try and open them. 200 country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat')) 201 if os.path.isfile(country_db): 202 self._country = geoip_open(country_db, cache) 203 self._country_file = country_db 204 205 city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat')) 206 if os.path.isfile(city_db): 207 self._city = geoip_open(city_db, cache) 208 self._city_file = city_db 209 elif os.path.isfile(path): 210 # Otherwise, some detective work will be needed to figure 211 # out whether the given database path is for the GeoIP country 212 # or city databases. 213 ptr = geoip_open(path, cache) 214 info = geoip_dbinfo(ptr) 215 if lite_regex.match(info): 216 # GeoLite City database detected. 217 self._city = ptr 218 self._city_file = path 219 elif free_regex.match(info): 220 # GeoIP Country database detected. 221 self._country = ptr 222 self._country_file = path 223 else: 224 raise GeoIPException('Unable to recognize database edition: %s' % info) 225 else: 226 raise GeoIPException('GeoIP path must be a valid file or directory.') 227 228 def __del__(self): 229 # Cleaning any GeoIP file handles lying around. 230 if self._country: geoip_close(self._country) 231 if self._city: geoip_close(self._city) 232 233 def _check_query(self, query, country=False, city=False, city_or_country=False): 234 "Helper routine for checking the query and database availability." 235 # Making sure a string was passed in for the query. 236 if not isinstance(query, basestring): 237 raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__) 238 239 # Extra checks for the existence of country and city databases. 240 if city_or_country and not (self._country or self._city): 241 raise GeoIPException('Invalid GeoIP country and city data files.') 242 elif country and not self._country: 243 raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file) 244 elif city and not self._city: 245 raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file) 246 247 def city(self, query): 248 """ 249 Returns a dictionary of city information for the given IP address or 250 Fully Qualified Domain Name (FQDN). Some information in the dictionary 251 may be undefined (None). 252 """ 253 self._check_query(query, city=True) 254 if ipregex.match(query): 255 # If an IP address was passed in 256 ptr = rec_by_addr(self._city, c_char_p(query)) 257 else: 258 # If a FQDN was passed in. 259 ptr = rec_by_name(self._city, c_char_p(query)) 260 261 # Checking the pointer to the C structure, if valid pull out elements 262 # into a dicionary and return. 263 if bool(ptr): 264 record = ptr.contents 265 return dict((tup[0], getattr(record, tup[0])) for tup in record._fields_) 266 else: 267 return None 268 269 def country_code(self, query): 270 "Returns the country code for the given IP Address or FQDN." 271 self._check_query(query, city_or_country=True) 272 if self._country: 273 if ipregex.match(query): return cntry_code_by_addr(self._country, query) 274 else: return cntry_code_by_name(self._country, query) 275 else: 276 return self.city(query)['country_code'] 277 278 def country_name(self, query): 279 "Returns the country name for the given IP Address or FQDN." 280 self._check_query(query, city_or_country=True) 281 if self._country: 282 if ipregex.match(query): return cntry_name_by_addr(self._country, query) 283 else: return cntry_name_by_name(self._country, query) 284 else: 285 return self.city(query)['country_name'] 286 287 def country(self, query): 288 """ 289 Returns a dictonary with with the country code and name when given an 290 IP address or a Fully Qualified Domain Name (FQDN). For example, both 291 '24.124.1.80' and 'djangoproject.com' are valid parameters. 292 """ 293 # Returning the country code and name 294 return {'country_code' : self.country_code(query), 295 'country_name' : self.country_name(query), 296 } 297 298 #### Coordinate retrieval routines #### 299 def coords(self, query, ordering=('longitude', 'latitude')): 300 cdict = self.city(query) 301 if cdict is None: return None 302 else: return tuple(cdict[o] for o in ordering) 303 304 def lon_lat(self, query): 305 "Returns a tuple of the (longitude, latitude) for the given query." 306 return self.coords(query) 307 308 def lat_lon(self, query): 309 "Returns a tuple of the (latitude, longitude) for the given query." 310 return self.coords(query, ('latitude', 'longitude')) 311 312 def geos(self, query): 313 "Returns a GEOS Point object for the given query." 314 ll = self.lon_lat(query) 315 if ll: 316 from django.contrib.gis.geos import Point 317 return Point(ll, srid=4326) 318 else: 319 return None 320 321 #### GeoIP Database Information Routines #### 322 def country_info(self): 323 "Returns information about the GeoIP country database." 324 if self._country is None: 325 ci = 'No GeoIP Country data in "%s"' % self._country_file 326 else: 327 ci = geoip_dbinfo(self._country) 328 return ci 329 country_info = property(country_info) 330 331 def city_info(self): 332 "Retuns information about the GeoIP city database." 333 if self._city is None: 334 ci = 'No GeoIP City data in "%s"' % self._city_file 335 else: 336 ci = geoip_dbinfo(self._city) 337 return ci 338 city_info = property(city_info) 339 340 def info(self): 341 "Returns information about all GeoIP databases in use." 342 return 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info) 343 info = property(info) 344 345 #### Methods for compatibility w/the GeoIP-Python API. #### 346 @classmethod 347 def open(cls, full_path, cache): 348 return GeoIP(full_path, cache) 349 350 def _rec_by_arg(self, arg): 351 if self._city: 352 return self.city(arg) 353 else: 354 return self.country(arg) 355 region_by_addr = city 356 region_by_name = city 357 record_by_addr = _rec_by_arg 358 record_by_name = _rec_by_arg 359 country_code_by_addr = country_code 360 country_code_by_name = country_code 361 country_name_by_addr = country_name 362 country_name_by_name = country_name 9 class GeoIP(BaseGeoIP): 10 def __init__(self, *args, **kwargs): 11 warnings.warn('GeoIP class has been moved to `django.contrib.gis.geoip`, and ' 12 'this shortcut will disappear in Django v1.6.', 13 PendingDeprecationWarning, stacklevel=2) 14 super(GeoIP, self).__init__(*args, **kwargs) -
django/contrib/gis/utils/srs.py
diff -r f22df4c44377 django/contrib/gis/utils/srs.py
a b 1 1 from django.contrib.gis.gdal import SpatialReference 2 from django.db import connections, DEFAULT_DB_ALIAS3 2 4 3 def add_srs_entry(srs, auth_name='EPSG', auth_srid=None, ref_sys_name=None, 5 database= DEFAULT_DB_ALIAS):4 database='default'): 6 5 """ 7 6 This function takes a GDAL SpatialReference system and adds its information 8 7 to the `spatial_ref_sys` table of the spatial backend. Doing this enables … … 33 32 of `django.db.DEFAULT_DB_ALIAS` (at the time of this writing, it's value 34 33 is 'default'). 35 34 """ 35 from django.db import connections 36 36 connection = connections[database] 37 37 if not hasattr(connection.ops, 'spatial_version'): 38 38 raise Exception('The `add_srs_entry` utility only works ' -
docs/internals/deprecation.txt
diff -r f22df4c44377 docs/internals/deprecation.txt
a b 239 239 were deprecated since Django 1.4 and will be removed in favor 240 240 of the ``django.utils.text.Truncator`` class. 241 241 242 * The :class:`~django.contrib.gis.geoip.GeoIP` was moved to 243 :mod:`django.contrib.gis.geoip` in 1.4 -- the shortcut in 244 :mod:`django.contrib.gis.utils` will be removed. 245 242 246 2.0 243 247 --- 244 248 -
docs/ref/contrib/gis/geoip.txt
diff -r f22df4c44377 docs/ref/contrib/gis/geoip.txt
a b 4 4 Geolocation with GeoIP 5 5 ====================== 6 6 7 .. module:: django.contrib.gis. utils.geoip7 .. module:: django.contrib.gis.geoip 8 8 :synopsis: High-level Python interface for MaxMind's GeoIP C library. 9 9 10 .. currentmodule:: django.contrib.gis.utils 10 .. versionchanged:: 1.4 11 12 .. note:: 13 14 In Django 1.4, the :class:`GeoIP` object was moved out of 15 :mod:`django.contrib.gis.utils` and into :mod:`django.contrib.gis.geoip`. 16 The shortcut in ``utils`` is deprecated, and will be removed in Django 1.6. 11 17 12 18 The :class:`GeoIP` object is a ctypes wrapper for the 13 19 `MaxMind GeoIP C API`__. [#]_ This interface is a BSD-licensed alternative … … 136 142 137 143 All the following querying routines may take either a string IP address 138 144 or a fully qualified domain name (FQDN). For example, both 139 ``'2 4.124.1.80'`` and ``'djangoproject.com'`` would be valid query145 ``'205.186.163.125'`` and ``'djangoproject.com'`` would be valid query 140 146 parameters. 141 147 142 148 .. method:: GeoIP.city(query) … … 144 150 Returns a dictionary of city information for the given query. Some 145 151 of the values in the dictionary may be undefined (``None``). 146 152 147 .. method:: GeoIP country(query)153 .. method:: GeoIP.country(query) 148 154 149 155 Returns a dictionary with the country code and country for the given 150 156 query. … … 190 196 .. attribute:: GeoIP.info 191 197 192 198 This property returns information about all GeoIP databases (both city 193 and country) .199 and country), and the version of the GeoIP C library (if supported). 194 200 195 201 GeoIP-Python API compatibility methods 196 202 ----------------------------------------