diff --git a/build/lib/pyzipcode/__init__.py b/build/lib/pyzipcode/__init__.py new file mode 100644 index 0000000..70ed1f7 --- /dev/null +++ b/build/lib/pyzipcode/__init__.py @@ -0,0 +1,114 @@ +from .settings import db_location +try: + import sqlite3 +except ImportError: + from pysqlite2 import dbapi2 as sqlite3 +import math +import time + +class ConnectionManager(object): + """ + Assumes a database that will work with cursor objects + """ + + def __init__(self): + # test out the connection... + conn = sqlite3.connect(db_location) + conn.close() + + def query(self, sql, args): + conn = None + retry_count = 0 + while not conn and retry_count <= 10: + # If there is trouble reading the file, retry for 10 attempts + # then just give up... + try: + conn = sqlite3.connect(db_location) + except sqlite3.OperationalError as x: + retry_count += 1 + time.sleep(0.001) + + if not conn and retry_count > 10: + raise sqlite3.OperationalError("Can't connect to sqlite database.") + + cursor = conn.cursor() + cursor.execute(sql, args) + res = cursor.fetchall() + conn.close() + return res + +ZIP_QUERY = "SELECT * FROM ZipCodes WHERE zip=?" +ZIP_RANGE_QUERY = "SELECT * FROM ZipCodes WHERE longitude >= %s and longitude <= %s AND latitude >= %s and latitude <= %s" +ZIP_FIND_QUERY = "SELECT * FROM ZipCodes WHERE city LIKE ? AND state LIKE ?" + +class ZipCode(object): + def __init__(self, data): + self.zip = data[0] + self.city = data[1] + self.state = data[2] + self.longitude = data[3] + self.latitude = data[4] + self.timezone = data[5] + self.dst = data[6] + +def format_result(zips): + if len(zips) > 0: + return [ZipCode(zip) for zip in zips] + else: + return None + +class ZipNotFoundException(Exception): + pass + +class ZipCodeDatabase(object): + + def __init__(self, conn_manager=None): + if conn_manager is None: + conn_manager = ConnectionManager() + self.conn_manager = conn_manager + + def get_zipcodes_around_radius(self, zip, radius): + zips = self.get(zip) + if zips is None: + raise ZipNotFoundException("Could not find zip code you're searching by.") + else: + zip = zips[0] + + radius = float(radius) + + long_range = (zip.longitude-(radius/69.0), zip.longitude+(radius/69.0)) + lat_range = (zip.latitude-(radius/49.0), zip.latitude+(radius/49.0)) + + return format_result(self.conn_manager.query(ZIP_RANGE_QUERY % ( + long_range[0], long_range[1], + lat_range[0], lat_range[1] + ))) + + def find_zip(self, city=None, state=None): + if city is None: + city = "%" + else: + city = city.upper() + + if state is None: + state = "%" + else: + state = state.upper() + + return format_result(self.conn_manager.query(ZIP_FIND_QUERY, [city, state])) + + def get(self, zip): + return format_result(self.conn_manager.query(ZIP_QUERY, [zip])) + + def __getitem__(self, zip): + zip = self.get(str(zip)) + if zip is None: + raise IndexError("Couldn't find zip") + else: + return zip[0] + + + + + + diff --git a/build/lib/pyzipcode/import.py b/build/lib/pyzipcode/import.py new file mode 100644 index 0000000..4dd79ff --- /dev/null +++ b/build/lib/pyzipcode/import.py @@ -0,0 +1,38 @@ + +from pysqlite2 import dbapi2 as sqlite3 +import os +import csv +try: + from .settings import db_location +except: + from pyzipcode.settings import db_location + +conn = sqlite3.connect(db_location) +c = conn.cursor() + +c.execute("DROP TABLE IF EXISTS ZipCodes;") +c.execute("CREATE TABLE ZipCodes(zip VARCHAR(5), city TEXT, state TEXT, longitude DOUBLE, latitude DOUBLE, timezone INT, dst INT);") +c.execute("CREATE INDEX zip_index ON ZipCodes(zip);") +c.execute("CREATE INDEX city_index ON ZipCodes(city);") +c.execute("CREATE INDEX state_index ON ZipCodes(state);") + +reader = csv.reader(open('zipcode.csv', "rb")) +next(reader) # prime it + +for row in reader: + zip, city, state, lat, longt, timezone, dst = row + + c.execute('INSERT INTO ZipCodes values("%s", "%s", "%s", %s, %s, %s, %s)' % ( + zip, + city, + state, + float(longt), + float(lat), + timezone, + dst + )) + +conn.commit() + +# We can also close the cursor if we are done with it +c.close() diff --git a/build/lib/pyzipcode/settings.py b/build/lib/pyzipcode/settings.py new file mode 100644 index 0000000..2ce3a4a --- /dev/null +++ b/build/lib/pyzipcode/settings.py @@ -0,0 +1,4 @@ +import os +db_filename = 'zipcodes.db' +directory = os.path.dirname(os.path.abspath(__file__)) +db_location = os.path.join(directory, db_filename) diff --git a/build/lib/pyzipcode/tests.py b/build/lib/pyzipcode/tests.py new file mode 100644 index 0000000..8f41865 --- /dev/null +++ b/build/lib/pyzipcode/tests.py @@ -0,0 +1,51 @@ + +import unittest +import pyzipcode + + +class TestSequenceFunctions(unittest.TestCase): + + def setUp(self): + self.db = pyzipcode.ZipCodeDatabase() + + def test_retrieves_zip_code_information(self): + zip = self.db['54115'] + self.assertEqual(zip.zip, '54115') + self.assertEqual(zip.city, "De Pere") + self.assertEqual(zip.state, "WI") + + def test_correct_longitude_value(self): + zip = self.db[54115] + self.assertTrue(zip.latitude > 44.42041 and zip.latitude < 44.42043) + + def test_correct_latitude_value(self): + zip = self.db[54115] + self.assertTrue(zip.longitude > -88.07897 and zip.longitude < -88.07895) + + def test_correct_timezone(self): + zip = self.db[54115] + self.assertEqual(zip.timezone, -6) + + def test_correct_dst(self): + zip = self.db[54115] + self.assertEqual(zip.dst, 1) + + def test_radius(self): + zips = self.db.get_zipcodes_around_radius('54115', 30) + self.assertTrue('54304' in [zip.zip for zip in zips]) + + def test_find_zip_by_city(self): + zip = self.db.find_zip(city="De Pere")[0] + self.assertEqual('54115', zip.zip) + + def test_find_zip_by_city_with_multiple_zips(self): + zips = self.db.find_zip(city="Green Bay") + self.assertTrue('54302' in [zip.zip for zip in zips]) + + def test_find_zips_in_state(self): + zips = self.db.find_zip(state="WI") + self.assertTrue('54304' in [zip.zip for zip in zips]) + self.assertTrue('54901' in [zip.zip for zip in zips]) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/build/lib/pyzipcode/zipcodes.db b/build/lib/pyzipcode/zipcodes.db new file mode 100644 index 0000000..98e2d18 Binary files /dev/null and b/build/lib/pyzipcode/zipcodes.db differ diff --git a/dist/pyzipcode-0.4-py3.6.egg b/dist/pyzipcode-0.4-py3.6.egg new file mode 100644 index 0000000..c32c1ca Binary files /dev/null and b/dist/pyzipcode-0.4-py3.6.egg differ diff --git a/pyzipcode.egg-info/PKG-INFO b/pyzipcode.egg-info/PKG-INFO new file mode 100644 index 0000000..6399393 --- /dev/null +++ b/pyzipcode.egg-info/PKG-INFO @@ -0,0 +1,85 @@ +Metadata-Version: 1.0 +Name: pyzipcode +Version: 0.4 +Summary: query zip codes and location data +Home-page: UNKNOWN +Author: Nathan Van Gheem +Author-email: vangheem@gmail.com +License: GPL +Description: Introduction + ============ + + This package will allow you to get zip code information. The data used in this + package is retrieved from http://pablotron.org/files/zipcodes-csv-10-Aug-2004.zip + + pyzipcode uses a local sqlite database to run. You can replace it with your own + other storage mechanism with a little effort. + + Here is some basic usage.. + + >>> from pyzipcode import ZipCodeDatabase + >>> zcdb = ZipCodeDatabase() + >>> zipcode = zcdb[54115] + >>> zipcode.zip + u'54115' + >>> zipcode.city + u'De Pere' + >>> zipcode.state + u'WI' + >>> zipcode.longitude + -88.078959999999995 + >>> zipcode.latitude + 44.42042 + >>> zipcode.timezone + -6 + + + Search zip codes... + + >>> from pyzipcode import ZipCodeDatabase + >>> zcdb = ZipCodeDatabase() + >>> len(zcdb.find_zip(city="Oshkosh")) + 7 + + + Get a list of zipcodes around a radius of a zipcode + + >>> from pyzipcode import ZipCodeDatabase + >>> zcdb = ZipCodeDatabase() + >>> [z.zip for z in zcdb.get_zipcodes_around_radius('54901', 10)] + [u'54901', u'54902', u'54903', u'54904', u'54906', u'54927', u'54952', u'54956', u'54957', u'54979', u'54985'] + + ChangeLog + ========= + + 0.4 + --- + + * updated to use maxmind database http://www.maxmind.com/app/postalcode + + * now also keeps timezone and dst values + + * longitude and latitude is now contains negative numbers + + * + + + 0.3 + --- + + * use strings instead of integer for storing zip codes since zip codes can start with a Zero. + + + 0.2 + --- + + * catch sqlite db file reading errors and keep trying in case + another process is trying to access the file at the same time. + + + 0.1 + --- + + * initial release +Keywords: zip code distance +Platform: UNKNOWN diff --git a/pyzipcode.egg-info/SOURCES.txt b/pyzipcode.egg-info/SOURCES.txt new file mode 100644 index 0000000..f603d98 --- /dev/null +++ b/pyzipcode.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +README.txt +setup.py +pyzipcode/__init__.py +pyzipcode/import.py +pyzipcode/settings.py +pyzipcode/tests.py +pyzipcode.egg-info/PKG-INFO +pyzipcode.egg-info/SOURCES.txt +pyzipcode.egg-info/dependency_links.txt +pyzipcode.egg-info/entry_points.txt +pyzipcode.egg-info/not-zip-safe +pyzipcode.egg-info/top_level.txt \ No newline at end of file diff --git a/pyzipcode.egg-info/dependency_links.txt b/pyzipcode.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyzipcode.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pyzipcode.egg-info/entry_points.txt b/pyzipcode.egg-info/entry_points.txt new file mode 100644 index 0000000..5d3e5f6 --- /dev/null +++ b/pyzipcode.egg-info/entry_points.txt @@ -0,0 +1,3 @@ + + # -*- Entry points: -*- + \ No newline at end of file diff --git a/pyzipcode.egg-info/not-zip-safe b/pyzipcode.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyzipcode.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/pyzipcode.egg-info/top_level.txt b/pyzipcode.egg-info/top_level.txt new file mode 100644 index 0000000..98c7246 --- /dev/null +++ b/pyzipcode.egg-info/top_level.txt @@ -0,0 +1 @@ +pyzipcode diff --git a/pyzipcode/__init__.py b/pyzipcode/__init__.py index da3b76b..70ed1f7 100644 --- a/pyzipcode/__init__.py +++ b/pyzipcode/__init__.py @@ -1,4 +1,4 @@ -from settings import db_location +from .settings import db_location try: import sqlite3 except ImportError: @@ -24,7 +24,7 @@ def query(self, sql, args): # then just give up... try: conn = sqlite3.connect(db_location) - except sqlite3.OperationalError, x: + except sqlite3.OperationalError as x: retry_count += 1 time.sleep(0.001) diff --git a/pyzipcode/__init__.py.bak b/pyzipcode/__init__.py.bak new file mode 100644 index 0000000..da3b76b --- /dev/null +++ b/pyzipcode/__init__.py.bak @@ -0,0 +1,114 @@ +from settings import db_location +try: + import sqlite3 +except ImportError: + from pysqlite2 import dbapi2 as sqlite3 +import math +import time + +class ConnectionManager(object): + """ + Assumes a database that will work with cursor objects + """ + + def __init__(self): + # test out the connection... + conn = sqlite3.connect(db_location) + conn.close() + + def query(self, sql, args): + conn = None + retry_count = 0 + while not conn and retry_count <= 10: + # If there is trouble reading the file, retry for 10 attempts + # then just give up... + try: + conn = sqlite3.connect(db_location) + except sqlite3.OperationalError, x: + retry_count += 1 + time.sleep(0.001) + + if not conn and retry_count > 10: + raise sqlite3.OperationalError("Can't connect to sqlite database.") + + cursor = conn.cursor() + cursor.execute(sql, args) + res = cursor.fetchall() + conn.close() + return res + +ZIP_QUERY = "SELECT * FROM ZipCodes WHERE zip=?" +ZIP_RANGE_QUERY = "SELECT * FROM ZipCodes WHERE longitude >= %s and longitude <= %s AND latitude >= %s and latitude <= %s" +ZIP_FIND_QUERY = "SELECT * FROM ZipCodes WHERE city LIKE ? AND state LIKE ?" + +class ZipCode(object): + def __init__(self, data): + self.zip = data[0] + self.city = data[1] + self.state = data[2] + self.longitude = data[3] + self.latitude = data[4] + self.timezone = data[5] + self.dst = data[6] + +def format_result(zips): + if len(zips) > 0: + return [ZipCode(zip) for zip in zips] + else: + return None + +class ZipNotFoundException(Exception): + pass + +class ZipCodeDatabase(object): + + def __init__(self, conn_manager=None): + if conn_manager is None: + conn_manager = ConnectionManager() + self.conn_manager = conn_manager + + def get_zipcodes_around_radius(self, zip, radius): + zips = self.get(zip) + if zips is None: + raise ZipNotFoundException("Could not find zip code you're searching by.") + else: + zip = zips[0] + + radius = float(radius) + + long_range = (zip.longitude-(radius/69.0), zip.longitude+(radius/69.0)) + lat_range = (zip.latitude-(radius/49.0), zip.latitude+(radius/49.0)) + + return format_result(self.conn_manager.query(ZIP_RANGE_QUERY % ( + long_range[0], long_range[1], + lat_range[0], lat_range[1] + ))) + + def find_zip(self, city=None, state=None): + if city is None: + city = "%" + else: + city = city.upper() + + if state is None: + state = "%" + else: + state = state.upper() + + return format_result(self.conn_manager.query(ZIP_FIND_QUERY, [city, state])) + + def get(self, zip): + return format_result(self.conn_manager.query(ZIP_QUERY, [zip])) + + def __getitem__(self, zip): + zip = self.get(str(zip)) + if zip is None: + raise IndexError("Couldn't find zip") + else: + return zip[0] + + + + + + diff --git a/pyzipcode/import.py b/pyzipcode/import.py index f41eb24..4dd79ff 100644 --- a/pyzipcode/import.py +++ b/pyzipcode/import.py @@ -3,7 +3,7 @@ import os import csv try: - from settings import db_location + from .settings import db_location except: from pyzipcode.settings import db_location @@ -17,7 +17,7 @@ c.execute("CREATE INDEX state_index ON ZipCodes(state);") reader = csv.reader(open('zipcode.csv', "rb")) -reader.next() # prime it +next(reader) # prime it for row in reader: zip, city, state, lat, longt, timezone, dst = row diff --git a/pyzipcode/import.py.bak b/pyzipcode/import.py.bak new file mode 100644 index 0000000..f41eb24 --- /dev/null +++ b/pyzipcode/import.py.bak @@ -0,0 +1,38 @@ + +from pysqlite2 import dbapi2 as sqlite3 +import os +import csv +try: + from settings import db_location +except: + from pyzipcode.settings import db_location + +conn = sqlite3.connect(db_location) +c = conn.cursor() + +c.execute("DROP TABLE IF EXISTS ZipCodes;") +c.execute("CREATE TABLE ZipCodes(zip VARCHAR(5), city TEXT, state TEXT, longitude DOUBLE, latitude DOUBLE, timezone INT, dst INT);") +c.execute("CREATE INDEX zip_index ON ZipCodes(zip);") +c.execute("CREATE INDEX city_index ON ZipCodes(city);") +c.execute("CREATE INDEX state_index ON ZipCodes(state);") + +reader = csv.reader(open('zipcode.csv', "rb")) +reader.next() # prime it + +for row in reader: + zip, city, state, lat, longt, timezone, dst = row + + c.execute('INSERT INTO ZipCodes values("%s", "%s", "%s", %s, %s, %s, %s)' % ( + zip, + city, + state, + float(longt), + float(lat), + timezone, + dst + )) + +conn.commit() + +# We can also close the cursor if we are done with it +c.close() diff --git a/pyzipcode/tests.py b/pyzipcode/tests.py index bc50831..8f41865 100644 --- a/pyzipcode/tests.py +++ b/pyzipcode/tests.py @@ -10,9 +10,9 @@ def setUp(self): def test_retrieves_zip_code_information(self): zip = self.db['54115'] - self.assertEquals(zip.zip, '54115') - self.assertEquals(zip.city, "De Pere") - self.assertEquals(zip.state, "WI") + self.assertEqual(zip.zip, '54115') + self.assertEqual(zip.city, "De Pere") + self.assertEqual(zip.state, "WI") def test_correct_longitude_value(self): zip = self.db[54115] @@ -24,11 +24,11 @@ def test_correct_latitude_value(self): def test_correct_timezone(self): zip = self.db[54115] - self.assertEquals(zip.timezone, -6) + self.assertEqual(zip.timezone, -6) def test_correct_dst(self): zip = self.db[54115] - self.assertEquals(zip.dst, 1) + self.assertEqual(zip.dst, 1) def test_radius(self): zips = self.db.get_zipcodes_around_radius('54115', 30) @@ -36,7 +36,7 @@ def test_radius(self): def test_find_zip_by_city(self): zip = self.db.find_zip(city="De Pere")[0] - self.assertEquals('54115', zip.zip) + self.assertEqual('54115', zip.zip) def test_find_zip_by_city_with_multiple_zips(self): zips = self.db.find_zip(city="Green Bay") diff --git a/pyzipcode/tests.py.bak b/pyzipcode/tests.py.bak new file mode 100644 index 0000000..bc50831 --- /dev/null +++ b/pyzipcode/tests.py.bak @@ -0,0 +1,51 @@ + +import unittest +import pyzipcode + + +class TestSequenceFunctions(unittest.TestCase): + + def setUp(self): + self.db = pyzipcode.ZipCodeDatabase() + + def test_retrieves_zip_code_information(self): + zip = self.db['54115'] + self.assertEquals(zip.zip, '54115') + self.assertEquals(zip.city, "De Pere") + self.assertEquals(zip.state, "WI") + + def test_correct_longitude_value(self): + zip = self.db[54115] + self.assertTrue(zip.latitude > 44.42041 and zip.latitude < 44.42043) + + def test_correct_latitude_value(self): + zip = self.db[54115] + self.assertTrue(zip.longitude > -88.07897 and zip.longitude < -88.07895) + + def test_correct_timezone(self): + zip = self.db[54115] + self.assertEquals(zip.timezone, -6) + + def test_correct_dst(self): + zip = self.db[54115] + self.assertEquals(zip.dst, 1) + + def test_radius(self): + zips = self.db.get_zipcodes_around_radius('54115', 30) + self.assertTrue('54304' in [zip.zip for zip in zips]) + + def test_find_zip_by_city(self): + zip = self.db.find_zip(city="De Pere")[0] + self.assertEquals('54115', zip.zip) + + def test_find_zip_by_city_with_multiple_zips(self): + zips = self.db.find_zip(city="Green Bay") + self.assertTrue('54302' in [zip.zip for zip in zips]) + + def test_find_zips_in_state(self): + zips = self.db.find_zip(state="WI") + self.assertTrue('54304' in [zip.zip for zip in zips]) + self.assertTrue('54901' in [zip.zip for zip in zips]) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file