This commit is contained in:
formcore 2025-01-09 15:19:25 +00:00
commit a39858932a
8 changed files with 2483 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.git
.venv
.pytest_cache
.Python
*.tgz
*/__pycache__
*/*.egg-info

0
README.md Normal file
View File

2292
data/area_codes.json Normal file

File diff suppressed because it is too large Load Diff

16
generate_json.py Normal file

File diff suppressed because one or more lines are too long

60
makefile Normal file
View File

@ -0,0 +1,60 @@
SHELL := /bin/bash
MIN_PYTHON_VERSION = 3.11.0
INSTALLED_PYTHON := $(shell python -V | grep -Po '(?<=Python)(.+)')
MIN_VERSION_MET := $(shell if awk "BEGIN {exit !($(INSTALLED_PYTHON) >= $(MIN_PYTHON_VERSION))}"; then echo true; else echo false; fi)
LOCAL_PYTHON_PATH = .Python/bin/python$(shell echo $(MIN_PYTHON_VERSION)|grep -Po '[0-9]{1,2}.[0-9]{1,2}')
MINIMUM_COVERAGE = 80
WD = $(shell pwd)
define venv_run
source .venv/bin/activate && $1
endef
all: init standards test
create-environment:
ifeq ($(MIN_VERSION_MET), true)
python -m venv .venv
else
ifeq (,$(wildcard $(LOCAL_PYTHON_PATH)))
mkdir -p .Python
wget https://www.python.org/ftp/python/$(MIN_PYTHON_VERSION)/Python-$(MIN_PYTHON_VERSION).tgz -O Python-$(MIN_PYTHON_VERSION).tgz
tar -xvzf Python-$(MIN_PYTHON_VERSION).tgz
cd Python-$(MIN_PYTHON_VERSION) && ./configure --prefix $(WD)/.Python && make && make install
rm Python-$(MIN_PYTHON_VERSION).tgz
rm -R Python-$(MIN_PYTHON_VERSION)
endif
$(LOCAL_PYTHON_PATH) -m venv .venv
endif
install-dev:
$(call venv_run, pip install --upgrade pip)
$(call venv_run, pip install .[dev])
$(call venv_run, pip install -e .)
install:
$(call venv_run, pip install --upgrade pip)
$(call venv_run, pip install .)
dev-init: create-environment install-dev
init: create-environment install
# testing / standards
pytest:
$(call venv_run, pytest -v)
safety:
$(call venv_run, safety check)
bandit:
$(call venv_run, bandit -r src/)
flake:
$(call venv_run, flake8 src/)
coverage:
$(call venv_run, coverage run --omit .venv -m pytest && coverage report -m --fail-under=$(MINIMUM_COVERAGE))
autopep8:
$(call venv_run, autopep8 --in-place --recursive src/*)
pkg_size:
test/package_size.sh
test: pytest coverage pkg_size
standards: autopep8 bandit flake safety
clean:
rm -Rf build dist .pytest_cache .coverage src/droppii/__pycache__ test/__pycache__

19
pyproject.toml Normal file
View File

@ -0,0 +1,19 @@
[project]
name = "uk_plate_reader"
version = "0.0.0"
description = ""
readme = ""
requires-python = ">=3.11"
dependencies = []
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: Unix",
]
[project.optional-dependencies]
dev = ["pytest"]
[build-system]
requires = ["setuptools>=65.0"]
build-backend = "setuptools.build_meta"

56
src/uk_plate_reader.py Normal file
View File

@ -0,0 +1,56 @@
import json
import sys
with open("./data/area_codes.json", "r") as f:
areas = json.load(f)
def get_legacy_year(plate:str):
base_year = 1983
try:
year_indicator = plate[0]
except IndexError:
return False
year_offset = ord(year_indicator.upper()) - ord("A")
year = base_year + year_offset
if year < 1983 or year >= 2001:
year = False
return year
def get_year(plate:str)->int:
year_indicator = int(plate[2:4])
if year_indicator >= 50:
year_indicator = year_indicator - 50
return year_indicator + 2000
def get_legacy_area(plate:str):
if len(plate.replace(" ","")) == 5:
area_code = plate[-1]
elif len(plate.replace(" ", "")) == 6:
area_code = plate[-2:]
return areas.get("legacy_style").get(area_code)
def get_area(plate:str):
area_code = plate[0:2]
area, _ = areas.get("modern_style").get(area_code)
return area
def is_legacy(plate):
try:
return int(plate[1])
except ValueError:
return False
def read_licence_plate(plate:str):
if is_legacy(plate):
area = get_legacy_area(plate)
year = get_legacy_year(plate)
else:
area = get_area(plate)
year = get_year(plate)
if not area or not year:
raise Exception("Not a valid licence plate")
return [area, year]
if __name__ == "__main__":
if len(sys.argv) > 1:
print(read_licence_plate(sys.argv[1]))

33
test/test_main.py Normal file
View File

@ -0,0 +1,33 @@
from uk_plate_reader import read_licence_plate
import pytest # type: ignore
def test_old_plate_returns_correct_area():
assert read_licence_plate("N12 ANA")[0] == "Manchester"
assert read_licence_plate("L12 AOE")[0] == "Birmingham"
assert read_licence_plate("L12 AP")[0] == "Surrey"
def test_new_plate_returns_correct_area():
assert read_licence_plate("LL07 XYZ")[0] == "London"
assert read_licence_plate("OJ57 XYZ")[0] == "Oxford"
assert read_licence_plate("BB62 XYZ")[0] == "Birmingham"
def test_old_plate_returns_correct_year():
assert read_licence_plate("N12 AOE")[1] in [1995, 1996]
assert read_licence_plate("L12 AOE")[1] in [1993, 1994]
def test_new_plate_returns_correct_year():
assert read_licence_plate("AB07 XYZ")[1] == 2007
assert read_licence_plate("AB57 XYZ")[1] in [2007, 2008]
assert read_licence_plate("AB62 XYZ")[1] in [2012, 2013]
def test_invalid_plate_returns_error():
for plate in ["A", "ZZ07 LMA", "Z12 ANA", "Y12 ZZZ"]:
with pytest.raises(Exception):
read_licence_plate(plate)
def test_empty_input_returns_error():
with pytest.raises(IndexError):
read_licence_plate("")
def test_legacy_single_char_area():
assert read_licence_plate("N12 AM")[0] == "Cheshire"