Skip to content
This repository was archived by the owner on Oct 14, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions build/lib/pyzipcode/__init__.py
Original file line number Diff line number Diff line change
@@ -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]






38 changes: 38 additions & 0 deletions build/lib/pyzipcode/import.py
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 4 additions & 0 deletions build/lib/pyzipcode/settings.py
Original file line number Diff line number Diff line change
@@ -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)
51 changes: 51 additions & 0 deletions build/lib/pyzipcode/tests.py
Original file line number Diff line number Diff line change
@@ -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()
Binary file added build/lib/pyzipcode/zipcodes.db
Binary file not shown.
Binary file added dist/pyzipcode-0.4-py3.6.egg
Binary file not shown.
85 changes: 85 additions & 0 deletions pyzipcode.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions pyzipcode.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions pyzipcode.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

3 changes: 3 additions & 0 deletions pyzipcode.egg-info/entry_points.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

# -*- Entry points: -*-

1 change: 1 addition & 0 deletions pyzipcode.egg-info/not-zip-safe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions pyzipcode.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pyzipcode
4 changes: 2 additions & 2 deletions pyzipcode/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from settings import db_location
from .settings import db_location
try:
import sqlite3
except ImportError:
Expand All @@ -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)

Expand Down
Loading