Skip to content
Merged
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
13 changes: 12 additions & 1 deletion src/vininfo/brands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,15 @@ class Dafra(Brand):

class Bajaj(Brand):

extractor = BajajDetails
extractor = BajajDetails


class FordAustralia(Brand):

extractor = FordAustraliaDetails

# Year letter at VIN position 11 (vis index 1), not SAE position 10.
year_position = 1

# Position 9 is a model code, not an SAE check digit.
uses_sae_checkdigit = False
6 changes: 6 additions & 0 deletions src/vininfo/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ def __str__(self):
class Brand(Assembler):
extractor: type['VinDetails'] = None

# VIS index of the model-year letter. SAE J853 default is 0 (= VIN pos 10).
year_position: int = 0

# SAE J853 check digit at position 9 applies to this brand.
uses_sae_checkdigit: bool = True

@property
def brands(self) -> set['Brand']:
return {self}
Expand Down
9 changes: 8 additions & 1 deletion src/vininfo/details/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
from .avtovaz import AvtoVazDetails
from .bajaj import BajajDetails
from .dafra import DafraDetails
from .ford import FordAustraliaDetails
from .nissan import NissanDetails
from .opel import OpelDetails
from .renault import RenaultDetails

__all__ = [
'AvtoVazDetails', 'BajajDetails', 'DafraDetails', 'NissanDetails', 'OpelDetails', 'RenaultDetails'
'AvtoVazDetails',
'BajajDetails',
'DafraDetails',
'FordAustraliaDetails',
'NissanDetails',
'OpelDetails',
'RenaultDetails'
]

36 changes: 36 additions & 0 deletions src/vininfo/details/ford.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from ._base import Detail, VinDetails


class FordAustraliaDetails(VinDetails):
"""Ford Australia VIN details extractor."""

platform = Detail(('vds', 3), {
'A': 'North America',
'C': 'Europe / Britain',
'J': 'Australia',
'U': 'Japan (Mazda)',
})

plant = Detail(('vds', 4), {
'G': 'Broadmeadows (main line)',
'H': 'Brisbane',
'K': 'Sydney',
'L': 'Broadmeadows (secondary line)',
})

# Position 9 + 10 hold a 2-char body code, but the Detail descriptor
# reads from a single section (vds ends at 9, vis starts at 10), so
# split: body_class = pos 9, body_subclass = pos 10.
body_class = Detail(('vds', 5), {
'A': 'Territory',
'C': 'Commercial / Ute',
'S': 'Sedan / Wagon',
})
body_subclass = Detail(('vis', 0))

# Brand binding in Vin.__init__ requires details.model.name to be truthy.
model = body_class

month = Detail(('vis', 2))

serial = Detail(('vis', slice(3, None)))
4 changes: 2 additions & 2 deletions src/vininfo/dicts/wmi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ..assemblers import Dafra
from ..brands import Bajaj, Lada, Nissan, Opel, Renault
from ..brands import Bajaj, FordAustralia, Lada, Nissan, Opel, Renault

# NOTE:
# if you want to extend this mapping with new WMIs, please use
Expand Down Expand Up @@ -249,7 +249,7 @@
'6F': 'Ford',
'6F4': Nissan('Nissan Motor Company'),
'6F5': 'Kenworth',
'6FP': 'Ford Motor Company',
'6FP': FordAustralia('Ford Australia'),
'6G': 'General Motors',
'6G1': 'Chevrolet',
'6G2': 'Pontiac',
Expand Down
5 changes: 4 additions & 1 deletion src/vininfo/toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def verify_checksum(self, *, check_year: bool = True) -> bool:
Note that not all manufacturer abey the rule. Default: True.

"""
if not getattr(self.assembler, 'uses_sae_checkdigit', True):
return False

if check_year and self.vis[0] in {'U', 'Z', '0'}:
return False

Expand Down Expand Up @@ -168,7 +171,7 @@ def country(self) -> str | None:

@property
def years_code(self) -> str:
return self.vis[0]
return self.vis[getattr(self.assembler, 'year_position', 0)]

@property
def years(self) -> list[int]:
Expand Down
55 changes: 55 additions & 0 deletions tests/test_ford_australia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from vininfo import Vin


def test_ford_au_year_at_position_11_not_10():
"""Synthetic 2004 Territory."""
vin = Vin('6FPAAAJGAT4Z00001')

assert vin.wmi == '6FP'
assert vin.manufacturer == 'Ford Australia'
assert vin.country == 'Australia'

# Year is decoded from VIS index 1 (= VIN position 11), not the
# SAE-default VIS index 0 (position 10).
assert vin.years_code == '4'
assert vin.years == [2004]

# Position 9 ('A') is a model/body class code, not an SAE check digit,
# so the global checksum check never matches for this brand.
assert vin.verify_checksum() is False

details = vin.details
assert details.platform.code == 'J'
assert details.platform.name == 'Australia'
assert details.plant.code == 'G'
assert details.plant.name == 'Broadmeadows (main line)'
assert details.body_class.code == 'A'
assert details.body_class.name == 'Territory'
assert details.body_subclass.code == 'T'
assert details.serial.code == '00001'


def test_ford_au_falcon_ute_year_letter_b_means_2011():
"""Synthetic 2011 Falcon Ute"""
vin = Vin('6FPAAAJGCMBZ00002')
assert vin.manufacturer == 'Ford Australia'
assert vin.years_code == 'B'
assert 2011 in vin.years
assert vin.details.body_class.name == 'Commercial / Ute'
assert vin.details.plant.name == 'Broadmeadows (main line)'


def test_ford_au_falcon_sedan_year_digit_2_means_2002():
"""Synthetic 2002 Falcon Sedan"""
vin = Vin('6FPAAAJGSW2Z00003')
assert vin.years_code == '2'
assert vin.years == [2002]
assert vin.details.body_class.name == 'Sedan / Wagon'


def test_ford_au_secondary_line_pre_2000():
"""Synthetic pre-2000 Falcon built on the Broadmeadows secondary line"""
vin = Vin('6FPAAAJL0MSZ00004')
assert vin.years_code == 'S'
assert 1995 in vin.years
assert vin.details.plant.name == 'Broadmeadows (secondary line)'
Loading