Browse Source

First pass at GeoCodes and geo_data for validating/working with geo codes

pull/307/head
Ross McFarland 7 years ago
parent
commit
50ae8054c7
No known key found for this signature in database GPG Key ID: 61C10C4FC8FE4A89
5 changed files with 467 additions and 0 deletions
  1. +34
    -0
      octodns/record/geo.py
  2. +316
    -0
      octodns/record/geo_data.py
  3. +2
    -0
      requirements-dev.txt
  4. +64
    -0
      script/generate-geo-data
  5. +51
    -0
      tests/test_octodns_record_geo.py

+ 34
- 0
octodns/record/geo.py View File

@ -0,0 +1,34 @@
#
#
#
from .geo_data import geo_data
class GeoCodes(object):
__COUNTRIES = None
@classmethod
def validate(cls, code):
'''
Validates an octoDNS geo code making sure that it is a valid and
corresponding:
* continent
* continent & country
* continent, country, & province
'''
reasons = []
pieces = code.split('-')
n = len(pieces)
if n > 3:
reasons.append('Invalid geo code "{}"'.format(code))
elif n > 0 and pieces[0] not in geo_data:
reasons.append('Unknown continent code "{}"'.format(code))
elif n > 1 and pieces[1] not in geo_data[pieces[0]]:
reasons.append('Unknown country code "{}"'.format(code))
elif n > 2 and \
pieces[2] not in geo_data[pieces[0]][pieces[1]]['provinces']:
reasons.append('Unknown province code "{}"'.format(code))
return reasons

+ 316
- 0
octodns/record/geo_data.py View File

@ -0,0 +1,316 @@
#
# -*- coding: utf-8 -*-
#
# This file is generated using
# ./script/generate-geo-data > octodns/record/geo_data.py
# do not modify it directly
#
geo_data = \
{'AF': {'AO': {'name': 'Angola'},
'BF': {'name': 'Burkina Faso'},
'BI': {'name': 'Burundi'},
'BJ': {'name': 'Benin'},
'BW': {'name': 'Botswana'},
'CD': {'name': 'Congo, The Democratic Republic of the'},
'CF': {'name': 'Central African Republic'},
'CG': {'name': 'Congo'},
'CI': {'name': "Côte d'Ivoire"},
'CM': {'name': 'Cameroon'},
'CV': {'name': 'Cabo Verde'},
'DJ': {'name': 'Djibouti'},
'DZ': {'name': 'Algeria'},
'EG': {'name': 'Egypt'},
'EH': {'name': 'Western Sahara'},
'ER': {'name': 'Eritrea'},
'ET': {'name': 'Ethiopia'},
'GA': {'name': 'Gabon'},
'GH': {'name': 'Ghana'},
'GM': {'name': 'Gambia'},
'GN': {'name': 'Guinea'},
'GQ': {'name': 'Equatorial Guinea'},
'GW': {'name': 'Guinea-Bissau'},
'KE': {'name': 'Kenya'},
'KM': {'name': 'Comoros'},
'LR': {'name': 'Liberia'},
'LS': {'name': 'Lesotho'},
'LY': {'name': 'Libya'},
'MA': {'name': 'Morocco'},
'MG': {'name': 'Madagascar'},
'ML': {'name': 'Mali'},
'MR': {'name': 'Mauritania'},
'MU': {'name': 'Mauritius'},
'MW': {'name': 'Malawi'},
'MZ': {'name': 'Mozambique'},
'NA': {'name': 'Namibia'},
'NE': {'name': 'Niger'},
'NG': {'name': 'Nigeria'},
'RE': {'name': 'Réunion'},
'RW': {'name': 'Rwanda'},
'SC': {'name': 'Seychelles'},
'SD': {'name': 'Sudan'},
'SH': {'name': 'Saint Helena, Ascension and Tristan da Cunha'},
'SL': {'name': 'Sierra Leone'},
'SN': {'name': 'Senegal'},
'SO': {'name': 'Somalia'},
'SS': {'name': 'South Sudan'},
'ST': {'name': 'Sao Tome and Principe'},
'SZ': {'name': 'Swaziland'},
'TD': {'name': 'Chad'},
'TG': {'name': 'Togo'},
'TN': {'name': 'Tunisia'},
'TZ': {'name': 'Tanzania, United Republic of'},
'UG': {'name': 'Uganda'},
'YT': {'name': 'Mayotte'},
'ZA': {'name': 'South Africa'},
'ZM': {'name': 'Zambia'},
'ZW': {'name': 'Zimbabwe'}},
'AN': {'AQ': {'name': 'Antarctica'},
'BV': {'name': 'Bouvet Island'},
'HM': {'name': 'Heard Island and McDonald Islands'},
'TF': {'name': 'French Southern Territories'}},
'AS': {'AE': {'name': 'United Arab Emirates'},
'AF': {'name': 'Afghanistan'},
'AM': {'name': 'Armenia'},
'AZ': {'name': 'Azerbaijan'},
'BD': {'name': 'Bangladesh'},
'BH': {'name': 'Bahrain'},
'BN': {'name': 'Brunei Darussalam'},
'BT': {'name': 'Bhutan'},
'CC': {'name': 'Cocos (Keeling) Islands'},
'CN': {'name': 'China'},
'CX': {'name': 'Christmas Island'},
'CY': {'name': 'Cyprus'},
'GE': {'name': 'Georgia'},
'HK': {'name': 'Hong Kong'},
'ID': {'name': 'Indonesia'},
'IL': {'name': 'Israel'},
'IN': {'name': 'India'},
'IO': {'name': 'British Indian Ocean Territory'},
'IQ': {'name': 'Iraq'},
'IR': {'name': 'Iran, Islamic Republic of'},
'JO': {'name': 'Jordan'},
'JP': {'name': 'Japan'},
'KG': {'name': 'Kyrgyzstan'},
'KH': {'name': 'Cambodia'},
'KP': {'name': "Korea, Democratic People's Republic of"},
'KR': {'name': 'Korea, Republic of'},
'KW': {'name': 'Kuwait'},
'KZ': {'name': 'Kazakhstan'},
'LA': {'name': "Lao People's Democratic Republic"},
'LB': {'name': 'Lebanon'},
'LK': {'name': 'Sri Lanka'},
'MM': {'name': 'Myanmar'},
'MN': {'name': 'Mongolia'},
'MO': {'name': 'Macao'},
'MV': {'name': 'Maldives'},
'MY': {'name': 'Malaysia'},
'NP': {'name': 'Nepal'},
'OM': {'name': 'Oman'},
'PH': {'name': 'Philippines'},
'PK': {'name': 'Pakistan'},
'PS': {'name': 'Palestine, State of'},
'QA': {'name': 'Qatar'},
'SA': {'name': 'Saudi Arabia'},
'SG': {'name': 'Singapore'},
'SY': {'name': 'Syrian Arab Republic'},
'TH': {'name': 'Thailand'},
'TJ': {'name': 'Tajikistan'},
'TM': {'name': 'Turkmenistan'},
'TR': {'name': 'Turkey'},
'TW': {'name': 'Taiwan, Province of China'},
'UZ': {'name': 'Uzbekistan'},
'VN': {'name': 'Viet Nam'},
'YE': {'name': 'Yemen'}},
'EU': {'AD': {'name': 'Andorra'},
'AL': {'name': 'Albania'},
'AT': {'name': 'Austria'},
'AX': {'name': 'Åland Islands'},
'BA': {'name': 'Bosnia and Herzegovina'},
'BE': {'name': 'Belgium'},
'BG': {'name': 'Bulgaria'},
'BY': {'name': 'Belarus'},
'CH': {'name': 'Switzerland'},
'CZ': {'name': 'Czechia'},
'DE': {'name': 'Germany'},
'DK': {'name': 'Denmark'},
'EE': {'name': 'Estonia'},
'ES': {'name': 'Spain'},
'FI': {'name': 'Finland'},
'FO': {'name': 'Faroe Islands'},
'FR': {'name': 'France'},
'GB': {'name': 'United Kingdom'},
'GG': {'name': 'Guernsey'},
'GI': {'name': 'Gibraltar'},
'GR': {'name': 'Greece'},
'HR': {'name': 'Croatia'},
'HU': {'name': 'Hungary'},
'IE': {'name': 'Ireland'},
'IM': {'name': 'Isle of Man'},
'IS': {'name': 'Iceland'},
'IT': {'name': 'Italy'},
'JE': {'name': 'Jersey'},
'LI': {'name': 'Liechtenstein'},
'LT': {'name': 'Lithuania'},
'LU': {'name': 'Luxembourg'},
'LV': {'name': 'Latvia'},
'MC': {'name': 'Monaco'},
'MD': {'name': 'Moldova, Republic of'},
'ME': {'name': 'Montenegro'},
'MK': {'name': 'Macedonia, Republic of'},
'MT': {'name': 'Malta'},
'NL': {'name': 'Netherlands'},
'NO': {'name': 'Norway'},
'PL': {'name': 'Poland'},
'PT': {'name': 'Portugal'},
'RO': {'name': 'Romania'},
'RS': {'name': 'Serbia'},
'RU': {'name': 'Russian Federation'},
'SE': {'name': 'Sweden'},
'SI': {'name': 'Slovenia'},
'SJ': {'name': 'Svalbard and Jan Mayen'},
'SK': {'name': 'Slovakia'},
'SM': {'name': 'San Marino'},
'UA': {'name': 'Ukraine'},
'VA': {'name': 'Holy See (Vatican City State)'}},
'ID': {'TL': {'name': 'Timor-Leste'}},
'NA': {'AG': {'name': 'Antigua and Barbuda'},
'AI': {'name': 'Anguilla'},
'AW': {'name': 'Aruba'},
'BB': {'name': 'Barbados'},
'BL': {'name': 'Saint Barthélemy'},
'BM': {'name': 'Bermuda'},
'BQ': {'name': 'Bonaire, Sint Eustatius and Saba'},
'BS': {'name': 'Bahamas'},
'BZ': {'name': 'Belize'},
'CA': {'name': 'Canada'},
'CR': {'name': 'Costa Rica'},
'CU': {'name': 'Cuba'},
'CW': {'name': 'Curaçao'},
'DM': {'name': 'Dominica'},
'DO': {'name': 'Dominican Republic'},
'GD': {'name': 'Grenada'},
'GL': {'name': 'Greenland'},
'GP': {'name': 'Guadeloupe'},
'GT': {'name': 'Guatemala'},
'HN': {'name': 'Honduras'},
'HT': {'name': 'Haiti'},
'JM': {'name': 'Jamaica'},
'KN': {'name': 'Saint Kitts and Nevis'},
'KY': {'name': 'Cayman Islands'},
'LC': {'name': 'Saint Lucia'},
'MF': {'name': 'Saint Martin (French part)'},
'MQ': {'name': 'Martinique'},
'MS': {'name': 'Montserrat'},
'MX': {'name': 'Mexico'},
'NI': {'name': 'Nicaragua'},
'PA': {'name': 'Panama'},
'PM': {'name': 'Saint Pierre and Miquelon'},
'PR': {'name': 'Puerto Rico'},
'SV': {'name': 'El Salvador'},
'SX': {'name': 'Sint Maarten (Dutch part)'},
'TC': {'name': 'Turks and Caicos Islands'},
'TT': {'name': 'Trinidad and Tobago'},
'US': {'name': 'United States',
'provinces': {'AK': {'name': 'Alaska'},
'AL': {'name': 'Alabama'},
'AR': {'name': 'Arkansas'},
'AS': {'name': 'American Samoa'},
'AZ': {'name': 'Arizona'},
'CA': {'name': 'California'},
'CO': {'name': 'Colorado'},
'CT': {'name': 'Connecticut'},
'DC': {'name': 'District of Columbia'},
'DE': {'name': 'Delaware'},
'FL': {'name': 'Florida'},
'GA': {'name': 'Georgia'},
'GU': {'name': 'Guam'},
'HI': {'name': 'Hawaii'},
'IA': {'name': 'Iowa'},
'ID': {'name': 'Idaho'},
'IL': {'name': 'Illinois'},
'IN': {'name': 'Indiana'},
'KS': {'name': 'Kansas'},
'KY': {'name': 'Kentucky'},
'LA': {'name': 'Louisiana'},
'MA': {'name': 'Massachusetts'},
'MD': {'name': 'Maryland'},
'ME': {'name': 'Maine'},
'MI': {'name': 'Michigan'},
'MN': {'name': 'Minnesota'},
'MO': {'name': 'Missouri'},
'MP': {'name': 'Northern Mariana Islands'},
'MS': {'name': 'Mississippi'},
'MT': {'name': 'Montana'},
'NC': {'name': 'North Carolina'},
'ND': {'name': 'North Dakota'},
'NE': {'name': 'Nebraska'},
'NH': {'name': 'New Hampshire'},
'NJ': {'name': 'New Jersey'},
'NM': {'name': 'New Mexico'},
'NV': {'name': 'Nevada'},
'NY': {'name': 'New York'},
'OH': {'name': 'Ohio'},
'OK': {'name': 'Oklahoma'},
'OR': {'name': 'Oregon'},
'PA': {'name': 'Pennsylvania'},
'PR': {'name': 'Puerto Rico'},
'RI': {'name': 'Rhode Island'},
'SC': {'name': 'South Carolina'},
'SD': {'name': 'South Dakota'},
'TN': {'name': 'Tennessee'},
'TX': {'name': 'Texas'},
'UM': {'name': 'United States Minor Outlying '
'Islands'},
'UT': {'name': 'Utah'},
'VA': {'name': 'Virginia'},
'VI': {'name': 'Virgin Islands'},
'VT': {'name': 'Vermont'},
'WA': {'name': 'Washington'},
'WI': {'name': 'Wisconsin'},
'WV': {'name': 'West Virginia'},
'WY': {'name': 'Wyoming'}}},
'VC': {'name': 'Saint Vincent and the Grenadines'},
'VG': {'name': 'Virgin Islands, British'},
'VI': {'name': 'Virgin Islands, U.S.'}},
'OC': {'AS': {'name': 'American Samoa'},
'AU': {'name': 'Australia'},
'CK': {'name': 'Cook Islands'},
'FJ': {'name': 'Fiji'},
'FM': {'name': 'Micronesia, Federated States of'},
'GU': {'name': 'Guam'},
'KI': {'name': 'Kiribati'},
'MH': {'name': 'Marshall Islands'},
'MP': {'name': 'Northern Mariana Islands'},
'NC': {'name': 'New Caledonia'},
'NF': {'name': 'Norfolk Island'},
'NR': {'name': 'Nauru'},
'NU': {'name': 'Niue'},
'NZ': {'name': 'New Zealand'},
'PF': {'name': 'French Polynesia'},
'PG': {'name': 'Papua New Guinea'},
'PN': {'name': 'Pitcairn'},
'PW': {'name': 'Palau'},
'SB': {'name': 'Solomon Islands'},
'TK': {'name': 'Tokelau'},
'TO': {'name': 'Tonga'},
'TV': {'name': 'Tuvalu'},
'UM': {'name': 'United States Minor Outlying Islands'},
'VU': {'name': 'Vanuatu'},
'WF': {'name': 'Wallis and Futuna'},
'WS': {'name': 'Samoa'}},
'SA': {'AR': {'name': 'Argentina'},
'BO': {'name': 'Bolivia, Plurinational State of'},
'BR': {'name': 'Brazil'},
'CL': {'name': 'Chile'},
'CO': {'name': 'Colombia'},
'EC': {'name': 'Ecuador'},
'FK': {'name': 'Falkland Islands (Malvinas)'},
'GF': {'name': 'French Guiana'},
'GS': {'name': 'South Georgia and the South Sandwich Islands'},
'GY': {'name': 'Guyana'},
'PE': {'name': 'Peru'},
'PY': {'name': 'Paraguay'},
'SR': {'name': 'Suriname'},
'UY': {'name': 'Uruguay'},
'VE': {'name': 'Venezuela, Bolivarian Republic of'}}}

+ 2
- 0
requirements-dev.txt View File

@ -2,6 +2,8 @@ coverage
mock
nose
pycodestyle==2.4.0
pycountry>=18.12.8
pycountry_convert>=0.7.2
pyflakes==1.6.0
requests_mock
twine==1.11.0

+ 64
- 0
script/generate-geo-data View File

@ -0,0 +1,64 @@
#!/usr/bin/env python
from collections import defaultdict
from pprint import pformat
from pycountry import countries, subdivisions
from pycountry_convert import country_alpha2_to_continent_code
subs = defaultdict(dict)
for subdivision in subdivisions:
# Route53 only supports US states, Dyn supports US states and CA provinces, but for now we'll just do US
if subdivision.country_code not in ('US'):
continue
subs[subdivision.country_code][subdivision.code[3:]] = {
'name': subdivision.name
}
subs = dict(subs)
#pprint(subs)
# These are best guesses at things pycountry_convert doesn't map
continent_backups = {
'AQ': 'AN',
'EH': 'AF',
'PN': 'OC',
'SX': 'NA',
'TF': 'AN',
'TL': 'ID',
'UM': 'OC',
'VA': 'EU',
}
geos = defaultdict(dict)
for country in countries:
try:
continent_code = country_alpha2_to_continent_code(country.alpha_2)
except KeyError:
try:
continent_code = continent_backups[country.alpha_2]
except KeyError:
raise
print('{} {} {}'.format(country.alpha_2, country.name, getattr(country, 'official_name', '')))
geos[continent_code][country.alpha_2] = {
'name': country.name
}
try:
geos[continent_code][country.alpha_2]['provinces'] = subs[country.alpha_2]
except KeyError:
pass
geos = dict(geos)
data = pformat(geos).replace('\n', '\n ')
print('''#
# -*- coding: utf-8 -*-
#
# This file is generated using
# ./script/generate-geo-data > octodns/record/geo_data.py
# do not modify it directly
#
geo_data = \\
{}'''.format(data))

+ 51
- 0
tests/test_octodns_record_geo.py View File

@ -0,0 +1,51 @@
#
#
#
from __future__ import absolute_import, division, print_function, \
unicode_literals
from unittest import TestCase
from octodns.record.geo import GeoCodes
class TestRecordGeoCodes(TestCase):
def test_validate(self):
# All valid
self.assertEquals([], GeoCodes.validate('NA'))
self.assertEquals([], GeoCodes.validate('NA-US'))
self.assertEquals([], GeoCodes.validate('NA-US-OR'))
# Just plain bad
self.assertEquals(['Invalid geo code "XX-YY-ZZ-AA"'],
GeoCodes.validate('XX-YY-ZZ-AA'))
self.assertEquals(['Unknown continent code "X-Y-Z"'],
GeoCodes.validate('X-Y-Z'))
self.assertEquals(['Unknown continent code "XXX-Y-Z"'],
GeoCodes.validate('XXX-Y-Z'))
# Bad continent
self.assertEquals(['Unknown continent code "XX"'],
GeoCodes.validate('XX'))
# Bad continent good country
self.assertEquals(['Unknown continent code "XX-US"'],
GeoCodes.validate('XX-US'))
# Bad continent good country and province
self.assertEquals(['Unknown continent code "XX-US-OR"'],
GeoCodes.validate('XX-US-OR'))
# Bad country, good continent
self.assertEquals(['Unknown country code "NA-XX"'],
GeoCodes.validate('NA-XX'))
# Bad country, good continent and state
self.assertEquals(['Unknown country code "NA-XX-OR"'],
GeoCodes.validate('NA-XX-OR'))
# Good country, good continent, but bad match
self.assertEquals(['Unknown country code "NA-GB"'],
GeoCodes.validate('NA-GB'))
# Bad province code, good continent and country
self.assertEquals(['Unknown province code "NA-US-XX"'],
GeoCodes.validate('NA-US-XX'))

Loading…
Cancel
Save