feat(console): add formatting utils and VIN API client

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 01:38:28 +00:00
parent ceacab789b
commit 211883393e
3 changed files with 347 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
"""
Display formatting utilities for the AUTOPARTES console application.
Functions for currency, numbers, text truncation, table layout, and
quality-tier visual bars.
"""
def format_currency(value) -> str:
"""Format a numeric value as USD currency.
None -> '──'
0 -> '$0.00'
45.99 -> '$45.99'
"""
if value is None:
return "──"
return f"${value:,.2f}"
def format_number(value) -> str:
"""Format an integer with thousands separators.
None -> '0'
13685 -> '13,685'
"""
if value is None:
return "0"
return f"{value:,}"
def truncate(text, max_len) -> str:
"""Truncate text to *max_len* characters, appending '...' if trimmed.
None -> ''
fits -> text unchanged
too long -> text[:max_len-3] + '...'
"""
if text is None:
return ""
if len(text) <= max_len:
return text
return text[: max_len - 3] + "..."
def pad_right(text, width) -> str:
"""Pad *text* to *width* with spaces on the right, or truncate if longer.
None -> ''
fits -> ljust(width)
too long -> text[:width]
"""
if text is None:
return ""
if len(text) > width:
return text[:width]
return text.ljust(width)
def format_table_row(values, widths, separator="") -> str:
"""Join *values* padded to corresponding *widths* with *separator*.
Each value is passed through :func:`pad_right` to ensure uniform column
widths, then all columns are joined by the separator string.
"""
cells = [pad_right(str(v), w) for v, w in zip(values, widths)]
return separator.join(cells)
# ── Quality-tier bars ──────────────────────────────────────────────────
_QUALITY_BARS = {
"oem": "███████████",
"premium": "██████████░",
"standard": "███████░░░░",
"economy": "█████░░░░░░",
}
def quality_bar(tier) -> str:
"""Return a Unicode block-bar representing a quality tier.
Recognised tiers: oem, premium, standard, economy.
Unknown tiers fall back to a minimal bar.
"""
return _QUALITY_BARS.get(tier, "░░░░░░░░░░░")

93
console/utils/vin_api.py Normal file
View File

@@ -0,0 +1,93 @@
"""
NHTSA VIN Decoder API client for the AUTOPARTES console application.
Wraps the National Highway Traffic Safety Administration (NHTSA) Vehicle
Product Information Catalog (vPIC) DecodeVin endpoint to retrieve vehicle
specifications from a 17-character VIN.
"""
import requests
from console.config import NHTSA_API_URL
# NHTSA result variables we care about, mapped to our internal keys.
_FIELD_MAP = {
"Make": "make",
"Model": "model",
"Model Year": "year",
"Body Class": "body_class",
"Drive Type": "drive_type",
"Displacement (L)": "displacement_l",
"Engine Number of Cylinders": "cylinders",
"Fuel Type - Primary": "fuel_type",
"Engine Brake (hp) From": "power_hp",
}
def decode_vin_nhtsa(vin: str) -> dict:
"""Decode a VIN using the NHTSA vPIC API.
Parameters
----------
vin : str
A 17-character Vehicle Identification Number.
Returns
-------
dict
On success::
{
"make": "TOYOTA",
"model": "Corolla",
"year": "2020",
"body_class": "Sedan/Saloon",
"drive_type": "FWD",
"engine_info": {
"displacement_l": "2.0",
"cylinders": "4",
"fuel_type": "Gasoline",
"power_hp": "169",
"raw": { ... full variable->value mapping ... },
},
}
On error::
{"error": "<description>"}
"""
try:
url = f"{NHTSA_API_URL}/{vin}"
response = requests.get(url, params={"format": "json"}, timeout=15)
response.raise_for_status()
data = response.json()
results = data.get("Results", [])
# Build a flat lookup: variable name -> value (skip empty/None)
raw: dict[str, str] = {}
for item in results:
var = item.get("Variable", "")
val = item.get("Value")
if val and str(val).strip():
raw[var] = str(val).strip()
# Extract top-level vehicle fields
vehicle: dict = {}
engine_info: dict = {"raw": raw}
engine_keys = {"displacement_l", "cylinders", "fuel_type", "power_hp"}
for nhtsa_var, our_key in _FIELD_MAP.items():
value = raw.get(nhtsa_var, "")
if our_key in engine_keys:
engine_info[our_key] = value
else:
vehicle[our_key] = value
vehicle["engine_info"] = engine_info
return vehicle
except Exception as e:
return {"error": str(e)}