Skip to content

Commit 6350185

Browse files
oschwaldautarch
authored andcommitted
Updated API for new document format including subdivisions, removal of is_transparent_proxy, iso_code, and a separate postal hash
1 parent b5d0161 commit 6350185

File tree

4 files changed

+120
-54
lines changed

4 files changed

+120
-54
lines changed

geoip2/models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ def __init__(self, raw_response, languages=None):
7777
geoip2.records.City(languages, **raw_response.get('city', {}))
7878
self.location = \
7979
geoip2.records.Location(**raw_response.get('location', {}))
80-
self.region = \
81-
geoip2.records.Region(languages,
82-
**raw_response.get('region', {}))
80+
self.postal = \
81+
geoip2.records.Postal(**raw_response.get('postal', {}))
82+
self.subdivisions = \
83+
geoip2.records.Subdivisions(languages,
84+
*raw_response.get('subdivisions', []))
8385

8486

8587
class CityISPOrg(City):

geoip2/records.py

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
"""
2+
23
Records
34
=======
5+
46
"""
7+
58
from abc import ABCMeta
9+
import collections
610

711

812
class Record(object):
@@ -23,7 +27,7 @@ class PlaceRecord(Record):
2327

2428
def __init__(self, languages=None, **kwargs):
2529
if languages is None:
26-
languages = []
30+
languages = ['en']
2731
object.__setattr__(self, 'languages', languages)
2832
super(PlaceRecord, self).__init__(**kwargs)
2933

@@ -46,7 +50,7 @@ class City(PlaceRecord):
4650
confidence that the city is correct. This attribute is only available
4751
from the Omni end point.
4852
:ivar geoname_id: This returns a GeoName ID for the city. This attribute
49-
is returned by all end points.
53+
N is returned by all end points.
5054
:ivar name: Returns the name of the city based on the languages list
5155
passed to the constructor. This attribute is returned by all end points.
5256
:ivar names: This returns a dictionary where the keys are language codes
@@ -94,10 +98,7 @@ class Country(PlaceRecord):
9498
from the Omni end point.
9599
:ivar geoname_id: This returns a GeoName ID for the country. This attribute
96100
is returned by all end points.
97-
:ivar iso_3166_1_alpha_2: This returns the two-character ISO 3166-1
98-
(http://en.wikipedia.org/wiki/ISO_3166-1) alpha code for the country.
99-
This attribute is returned by all end points.
100-
:ivar iso_3166_1_alpha_3: This returns the three-character ISO 3166-1
101+
:ivar iso_code: This returns the two-character ISO 3166-1
101102
(http://en.wikipedia.org/wiki/ISO_3166-1) alpha code for the country.
102103
This attribute is returned by all end points.
103104
:ivar name: Returns the name of the country based on the languages list
@@ -106,8 +107,7 @@ class Country(PlaceRecord):
106107
and the values are names. This attribute is returned by all end points.
107108
108109
"""
109-
_valid_attributes = set(['confidence', 'geoname_id', 'iso_3166_1_alpha_2',
110-
'iso_3166_1_alpha_3', 'names'])
110+
_valid_attributes = set(['confidence', 'geoname_id', 'iso_code', 'names'])
111111

112112

113113
class Location(Record):
@@ -134,13 +134,6 @@ class Location(Record):
134134
(https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions).
135135
This attribute is returned by all end points except the Country end
136136
point.
137-
:ivar postal_code: This returns the postal code of the location. Postal
138-
codes are not available for all countries. In some countries, this will
139-
only contain part of the postal code. This attribute is returned by all
140-
end points except the Country end point.
141-
:ivar postal_confidence: This returns a value from 0-100 indicating
142-
MaxMind's confidence that the postal code is correct. This attribute is
143-
only available from the Omni end point.
144137
:ivar time_zone: This returns the time zone associated with location, as
145138
specified by the IANA Time Zone Database
146139
(http://www.iana.org/time-zones), e.g., "America/New_York". This
@@ -152,32 +145,94 @@ class Location(Record):
152145
'time_zone'])
153146

154147

155-
class Region(PlaceRecord):
156-
"""Contains data for the region record associated with an IP address
148+
class Postal(Record):
149+
"""Contains data for the postal record associated with an IP address
150+
151+
This class contains the postal data associated with an IP address.
152+
153+
This record is returned by all the end points except the Country end point.
154+
155+
Attributes:
156+
157+
:ivar code: This returns the postal code of the location. Postal
158+
codes are not available for all countries. In some countries, this will
159+
only contain part of the postal code. This attribute is returned by all
160+
end points except the Country end point.
161+
:ivar confidence: This returns a value from 0-100 indicating
162+
MaxMind's confidence that the postal code is correct. This attribute is
163+
only available from the Omni end point.
164+
165+
"""
166+
_valid_attributes = set(['code', 'confidence'])
167+
168+
169+
class Subdivision(PlaceRecord):
170+
"""Contains data a subdivisions associated with an IP address
157171
158-
This class contains the region-level data associated with an IP address.
172+
This class contains the subdivision data associated with an IP address.
159173
160174
This record is returned by all the end points except the Country end point.
161175
162176
Attributes:
163177
164178
:ivar confidence: This is a value from 0-100 indicating MaxMind's
165-
confidence that the region is correct. This attribute is only available
166-
from the Omni end point.
167-
:ivar geoname_id: This is a GeoName ID for the region. This attribute
168-
is returned by all end points.
169-
:ivar iso_3166_2: This is a string up to three characters long
170-
contain the region portion of the ISO 3166-2 code
179+
confidence that the subdivision is correct. This attribute is only
180+
available from the Omni end point.
181+
:ivar geoname_id: This is a GeoName ID for the subdivision. This
182+
attribute is returned by all end points.
183+
:ivar iso_code: This is a string up to three characters long
184+
contain the subdivision portion of the ISO 3166-2 code
171185
(http://en.wikipedia.org/wiki/ISO_3166-2). This attribute is returned
172186
by all end points.
173-
:ivar name: The name of the region based on the languages list
187+
:ivar name: The name of the subdivision based on the languages list
174188
passed to the constructor. This attribute is returned by all end points.
175189
:ivar names: A dictionary where the keys are language codes and the
176190
values are names. This attribute is returned by all end points.
177191
178192
"""
179-
_valid_attributes = set(['confidence', 'geoname_id', 'iso_3166_2',
180-
'names'])
193+
_valid_attributes = set(['confidence', 'geoname_id', 'iso_code', 'names'])
194+
195+
196+
class Subdivisions(collections.namedtuple('Subdivisions',
197+
'subdivision_1 subdivision_2 '
198+
'subdivision_3')):
199+
"""Contains data for the subdivisions associated with an IP address
200+
201+
This class contains the subdivisions of the country associated with the
202+
IP address.
203+
204+
For instance, the response for Oxford in the United Kingdom would have
205+
England as subdivision_1 and Oxfordshire as subdivision_2.
206+
207+
This record is returned by all the end points except the Country end point.
208+
209+
Attributes:
210+
211+
:ivar subdivision_1: The first-level (largest) subdivision associated with
212+
the location of the IP address. Many countries will only have this
213+
subdivision. For instance, in the United States, this would be a state.
214+
:ivar subdivision_2: The second-level subdivision associated with the
215+
location of the IP address.
216+
:ivar subdivision_3: The third-level subdivsion associated with the
217+
locatin of the IP address.
218+
219+
"""
220+
__slots__ = ()
221+
222+
def __new__(cls, languages, subdivision_1=None, subdivision_2=None,
223+
subdivision_3=None):
224+
if subdivision_1 is None:
225+
subdivision_1 = {}
226+
if subdivision_2 is None:
227+
subdivision_2 = {}
228+
if subdivision_3 is None:
229+
subdivision_3 = {}
230+
231+
subdivision_1 = Subdivision(languages, **subdivision_1)
232+
subdivision_2 = Subdivision(languages, **subdivision_2)
233+
subdivision_3 = Subdivision(languages, **subdivision_3)
234+
return super(cls, Subdivisions).__new__(cls, subdivision_1,
235+
subdivision_2, subdivision_3)
181236

182237

183238
class Traits(Record):
@@ -189,7 +244,7 @@ class Traits(Record):
189244
190245
This class has the following attributes:
191246
192-
:ivar autonomous_system_number: This returns the autonomous system
247+
:ivar autonomous_system_number: This returns the autonomous system
193248
number (http://en.wikipedia.org/wiki/Autonomous_system_(Internet))
194249
associated with the IP address. This attribute is only available from
195250
the City/ISP/Org and Omni end points.
@@ -211,8 +266,6 @@ class Traits(Record):
211266
:ivar is_anonymous_proxy: This returns true if the IP is an anonymous
212267
proxy. See http://dev.maxmind.com/faq/geoip#anonproxy for further
213268
details. This attribute is returned by all end points.
214-
:ivar is_transparent_proxy: This returns true if the IP is a transparent
215-
proxy. This attribute is returned by all end points.
216269
:ivar isp: This returns the name of the ISP associated the IP address.
217270
This attribute is only available from the City/ISP/Org and Omni end
218271
points.
@@ -246,14 +299,12 @@ class Traits(Record):
246299
'domain',
247300
'is_anonymous_proxy',
248301
'is_satellite_provider',
249-
'is_transparent_proxy',
250302
'isp',
251303
'ip_address',
252304
'organization',
253305
'user_type'])
254306

255307
def __init__(self, languages=None, **kwargs):
256-
for k in ['is_anonymous_proxy', 'is_satellite_provider',
257-
'is_transparent_proxy']:
308+
for k in ['is_anonymous_proxy', 'is_satellite_provider']:
258309
kwargs[k] = bool(kwargs.get(k, False))
259310
super(Traits, self).__init__(**kwargs)

tests/models_test.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,21 @@ def test_omni_full(self):
3838
'time_zone': 'America/Chicago',
3939
},
4040
'postal': {
41-
code: '55401',
42-
confidence: 33,
41+
'code': '55401',
42+
'confidence': 33,
4343
},
4444
'subdivisions': [{
4545
'confidence': 88,
4646
'geoname_id': 574635,
4747
'iso_code': 'MN',
4848
'names': {'en': 'Minnesota'},
49-
}],
49+
},
50+
{
51+
'geoname_id': 123,
52+
'iso_code': 'HP',
53+
'names': {'en': 'Hennepin'},
54+
}
55+
],
5056
'registered_country': {
5157
'geoname_id': 2,
5258
'iso_code': 'CA',
@@ -58,7 +64,6 @@ def test_omni_full(self):
5864
'domain': 'example.com',
5965
'ip_address': '1.2.3.4',
6066
'is_anonymous_proxy': 0,
61-
'is_transparent_proxy': 1,
6267
'is_us_military': 1,
6368
'isp': 'Comcast',
6469
'network_speed': 'cable/DSL',
@@ -81,11 +86,22 @@ def test_omni_full(self):
8186
'geoip2.records.Country object')
8287
self.assertEqual(type(model.location), geoip2.records.Location,
8388
'geoip2.records.Location object')
84-
self.assertEqual(type(model.region), geoip2.records.Region,
85-
'geoip2.records.Regin object')
89+
self.assertEqual(type(model.subdivisions.subdivision_1),
90+
geoip2.records.Subdivision,
91+
'geoip2.records.Subdivision object')
8692
self.assertEqual(type(model.traits), geoip2.records.Traits,
8793
'geoip2.records.Traits object')
8894
self.assertEqual(model.raw, raw, 'raw method returns raw input')
95+
self.assertEqual(model.subdivisions.subdivision_1.iso_code, 'MN',
96+
'div 1 has correct iso_code')
97+
self.assertEqual(model.subdivisions.subdivision_1.confidence, 88,
98+
'div 1 has correct confidence')
99+
self.assertEqual(model.subdivisions.subdivision_1.geoname_id, 574635,
100+
'div 1 has correct geoname_id')
101+
self.assertEqual(model.subdivisions.subdivision_1.names,
102+
{'en': 'Minnesota'}, 'div 1 names are correct')
103+
self.assertEqual(model.subdivisions.subdivision_2.name, 'Hennepin',
104+
'div 2 has correct name')
89105

90106
def test_omni_min(self):
91107
model = geoip2.models.Omni({'traits': {'ip_address': '5.6.7.8'}})
@@ -102,8 +118,9 @@ def test_omni_min(self):
102118
'geoip2.records.Country object')
103119
self.assertEqual(type(model.location), geoip2.records.Location,
104120
'geoip2.records.Location object')
105-
self.assertEqual(type(model.region), geoip2.records.Region,
106-
'geoip2.records.Regin object')
121+
self.assertEqual(type(model.subdivisions.subdivision_1),
122+
geoip2.records.Subdivision,
123+
'geoip2.records.Subdivision object')
107124
self.assertEqual(type(model.traits), geoip2.records.Traits,
108125
'geoip2.records.Traits object')
109126

@@ -143,8 +160,9 @@ def test_city_full(self):
143160
'geoip2.records.Country object')
144161
self.assertEqual(type(model.location), geoip2.records.Location,
145162
'geoip2.records.Location object')
146-
self.assertEqual(type(model.region), geoip2.records.Region,
147-
'geoip2.records.Regin object')
163+
self.assertEqual(type(model.subdivisions.subdivision_1),
164+
geoip2.records.Subdivision,
165+
'geoip2.records.Subdivision object')
148166
self.assertEqual(type(model.traits), geoip2.records.Traits,
149167
'geoip2.records.Traits object')
150168
self.assertEqual(model.raw, raw, 'raw method returns raw input')
@@ -177,8 +195,6 @@ def test_city_full(self):
177195
'registered_country name is correct')
178196
self.assertEqual(model.traits.is_anonymous_proxy, False,
179197
'traits is_anonymous_proxy returns False by default')
180-
self.assertEqual(model.traits.is_transparent_proxy, False,
181-
'traits is_anonymous_proxy returns False by default')
182198
self.assertEqual(model.traits.is_satellite_provider, True,
183199
'traits is_setellite_provider is True')
184200
self.assertEqual(model.raw, raw, 'raw method produces raw output')

tests/webservices_test.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ def setUp(self):
3838
},
3939
'country': {
4040
'geoname_id': 1,
41-
'iso_3166_1_alpha_2': 'US',
42-
'iso_3166_1_alpha_3': 'USA',
41+
'iso_code': 'US',
4342
'names': { 'en': 'United States of America'}
4443
},
4544
'traits': {'ip_address': '1.2.3.4',},
@@ -83,10 +82,8 @@ def test_country_ok(self, get):
8382
'continent name is North America')
8483
self.assertEqual(country.country.geoname_id, 1,
8584
'country geoname_id is 1')
86-
self.assertEqual(country.country.iso_3166_1_alpha_2, 'US',
87-
'country iso_3166_1_alpha_2 is US')
88-
self.assertEqual(country.country.iso_3166_1_alpha_3, 'USA',
89-
'country iso_3166_1_alpha_3 is USA')
85+
self.assertEqual(country.country.iso_code, 'US',
86+
'country iso_code is US')
9087
self.assertEqual(country.country.names,
9188
{ 'en': 'United States of America' },
9289
'country names' )

0 commit comments

Comments
 (0)