Skip to content
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Packages
*.egg
*.egg-info
venv
dist
build
eggs
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Low level API example
Canada
>>> v.year
2006
>>> v.manufacturer
Honada Canda
>>> v.is_pre_2010
True
>>> v.wmi
Expand All @@ -48,7 +50,6 @@ Low level API example
False



Methods
-------

Expand Down
1 change: 0 additions & 1 deletion libvin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from conversion import convert
from decoding import decode
from verification import is_valid
Empty file removed libvin/check.py
Empty file.
119 changes: 77 additions & 42 deletions libvin/decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,88 @@
"""

from libvin.static import *
from libvin import wmi_map


VIN_SIZE = 17
"""For model years 1981 to present, the VIN is composed of 17
alphanumeric values"""


PROHIBITED_LETTERS = 'IOQ'
r"""The letters I,O, Q are prohibited from any VIN position"""


PROHIBITED_MODEL_LETTERS = 'UZ0'
r"""The tenth position of the VIN represents the Model Year and
does not permit the use of the characters U and Z, as well
as the numeric zero (0)
"""


r"""The ninth position of the VIN is a calculated value based on
the other 16 alphanumeric values, it's called the
"Check Digit". The result of the check digit can ONLY be a
numeric 0-9 or letter "X".
"""
ALLOWED_CHECK_DIGIT_LETTERS = 'X0123456789'


class BadVin(Exception):
r"""If VIN is incorrect according to specification, exception is raised."""
pass

class Vin(object):

def __init__(self, vin):
if not vin:
raise BadVin('Vin is empty')

self.vin = vin.upper()

if not self.is_valid():
raise BadVin('Vin is not valid')

@property
def country(self):
"""
Returns the World Manufacturer's Country.
"""
if not self.vin[0] in WORLD_MANUFACTURER_MAP:
return None

countries = WORLD_MANUFACTURER_MAP[self.vin[0]]['countries']

for codes in countries:
if self.vin[0] in codes:
return countries[codes]

return 'Unknown'
return None

def decode(self):
return self.vin

def is_valid(self):
r"""Returns True if a VIN is valid, otherwise returns False."""
if hasattr(self, '_is_valid'):
return getattr(self, '_is_valid')

_is_valid = False

if len(self.vin) != VIN_SIZE:
_is_valid = False
elif any(x in PROHIBITED_LETTERS for x in self.vin):
_is_valid = False
elif self.vin[9] in PROHIBITED_MODEL_LETTERS:
_is_valid = False
elif not self.vin[8] in ALLOWED_CHECK_DIGIT_LETTERS:
_is_valid = False
else:
_is_valid = True

setattr(self, '_is_valid', _is_valid)
return _is_valid

@property
def is_pre_2010(self):
"""
Expand All @@ -42,44 +102,6 @@ def is_pre_2010(self):
"""
return self.vin[6].isdigit()

@property
def is_valid(self):
"""
Returns True if a VIN is valid, otherwise returns False.
"""
if len(self.vin) != 17:
"""
For model years 1981 to present, the VIN is composed of 17
alphanumeric values
"""
return False

elif any(x in 'IOQ' for x in self.vin):
"""
The letters I,O, Q are prohibited from any VIN position
"""
return False

elif self.vin[9] in 'UZ0':
"""
The tenth position of the VIN represents the Model Year and
does not permit the use of the characters U and Z, as well
as the numeric zero (0)
"""
return False

elif self.vin[8] not in 'X0123456789':
"""
The ninth position of the VIN is a calculated value based on
the other 16 alphanumeric values, it's called the
"Check Digit". The result of the check digit can ONLY be a
numeric 0-9 or letter "X".
"""
return False

else:
return True

@property
def less_than_500_built_per_year(self):
"""
Expand All @@ -96,6 +118,8 @@ def region(self):
"""
Returns the World Manufacturer's Region. Possible results:
"""
if not self.vin[0] in WORLD_MANUFACTURER_MAP:
return None
return WORLD_MANUFACTURER_MAP[self.vin[0]]['region']

@property
Expand Down Expand Up @@ -132,16 +156,27 @@ def wmi(self):
"""
return self.vin[0:3]

@property
def manufacturer(self):
wmi = self.wmi
print self.wmi
if wmi[:3] in wmi_map.WMI_MAP:
return wmi_map.WMI_MAP[wmi[:3]]
if wmi[:2] in wmi_map.WMI_MAP:
return wmi_map.WMI_MAP[wmi[:2]]
return None

make = manufacturer

@property
def year(self):
"""
Returns the model year of the vehicle
"""
if self.is_pre_2010:
return YEARS_CODES_PRE_2010[self.vin[9]]
return YEARS_CODES_PRE_2010.get(self.vin[9], None)
else:
print self.vin[9]
return YEARS_CODES_PRE_2040[self.vin[9]]
return YEARS_CODES_PRE_2040.get(self.vin[9], None)


def decode(vin):
Expand Down
Loading