Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fake automotive vin number function (#1879) #1884

Merged
merged 2 commits into from
Jul 7, 2023
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
41 changes: 41 additions & 0 deletions faker/providers/automotive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@
localized = True


def calculate_vin_str_weight(s: str, weight_factor: list) -> int:
"""
multiply s(str) by weight_factor char by char
e.g.
input: s="ABCDE", weight_factor=[1, 2, 3, 4, 5]
return: A*1 + B*2 + C*3 + D*4 + E*5

will multiply 0 when len(weight_factor) less than len(s)
"""

def _get_char_weight(c: str) -> int:
"""A=1, B=2, ...., I=9,
J=1, K=2, ..., R=9,
S=2, T=3, ..., Z=9
"""
if ord(c) <= 64: # 0-9
return int(c)
if ord(c) <= 73: # A-I
return ord(c) - 64
if ord(c) <= 82: # J-R
return ord(c) - 73
# S-Z
return ord(c) - 81

res = 0
for i, c in enumerate(s):
res += _get_char_weight(c) * weight_factor[i] if i < len(weight_factor) else 0
return res


class Provider(BaseProvider):
"""Implement default automotive provider for Faker."""

Expand All @@ -20,3 +50,14 @@ def license_plate(self) -> str:
self.random_element(self.license_formats),
)
return self.numerify(temp)

def vin(self) -> str:
"""Generate vin number."""
vin_chars = "1234567890ABCDEFGHJKLMNPRSTUVWXYZ" # I, O, Q are restricted
front_part = self.bothify("????????", letters=vin_chars)
rear_part = self.bothify("????????", letters=vin_chars)
front_part_weight = calculate_vin_str_weight(front_part, [8, 7, 6, 5, 4, 3, 2, 10])
rear_part_weight = calculate_vin_str_weight(rear_part, [9, 8, 7, 6, 5, 4, 3, 2])
checksum = (front_part_weight + rear_part_weight) % 11
checksum_char = "X" if checksum == 10 else str(checksum)
return front_part + checksum_char + rear_part
14 changes: 14 additions & 0 deletions tests/providers/test_automotive.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from faker.providers.automotive.ru_RU import Provider as RuRuAutomotiveProvider
from faker.providers.automotive.sk_SK import Provider as SkSkAutomotiveProvider
from faker.providers.automotive.tr_TR import Provider as TrTrAutomotiveProvider
from faker.providers.automotive import calculate_vin_str_weight


class _SimpleAutomotiveTestMixin:
Expand All @@ -23,6 +24,19 @@ def test_license_plate(self, faker, num_samples):
assert match is not None
self.perform_extra_checks(license_plate, match)

def test_vin(self, faker, num_samples):
for _ in range(num_samples):
vin_number = faker.vin()
# length check: 17
assert len(vin_number) == 17

# verify checksum: vin_number[8]
front_part_weight = calculate_vin_str_weight(vin_number[:8], [8, 7, 6, 5, 4, 3, 2, 10])
rear_part_weight = calculate_vin_str_weight(vin_number[9:], [9, 8, 7, 6, 5, 4, 3, 2])
checksum = (front_part_weight + rear_part_weight) % 11
checksum_str = "X" if checksum == 10 else str(checksum)
assert vin_number[8] == checksum_str


class TestArBh(_SimpleAutomotiveTestMixin):
"""Test ar_BH automotive provider methods"""
Expand Down
Loading