feat: AR-VINchecker v1.0 — VIN report generator with AI analysis
FastAPI server querying NHTSA/EPA APIs, generating PDF reports with risk scoring, and local Ollama AI analysis via DealAnalyzer. Security hardened: XSS fix, SSRF protection, CORS restricted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import httpx
|
||||
|
||||
_VPIC_BASE = "https://vpic.nhtsa.dot.gov/api/vehicles"
|
||||
_NHTSA_BASE = "https://api.nhtsa.gov"
|
||||
|
||||
_VPIC_FIELDS = {
|
||||
"Make", "Model", "Model Year", "Trim", "Body Class",
|
||||
"Displacement (L)", "Engine Number of Cylinders", "Fuel Type - Primary",
|
||||
"Drive Type", "Transmission Style", "Plant Country",
|
||||
"Electrification Level", "Engine Model",
|
||||
}
|
||||
|
||||
_FIELD_MAP = {
|
||||
"Make": "Make",
|
||||
"Model": "Model",
|
||||
"Model Year": "ModelYear",
|
||||
"Trim": "Trim",
|
||||
"Body Class": "BodyClass",
|
||||
"Displacement (L)": "DisplacementL",
|
||||
"Engine Number of Cylinders": "Cylinders",
|
||||
"Fuel Type - Primary": "FuelType",
|
||||
"Drive Type": "DriveType",
|
||||
"Transmission Style": "Transmission",
|
||||
"Plant Country": "PlantCountry",
|
||||
"Electrification Level": "EVLevel",
|
||||
"Engine Model": "EngineModel",
|
||||
}
|
||||
|
||||
|
||||
async def decode_vin(vin: str) -> dict:
|
||||
url = f"{_VPIC_BASE}/decodevin/{vin}?format=json"
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
resp = await client.get(url)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
result: dict = {}
|
||||
for item in data.get("Results", []):
|
||||
var = item.get("Variable", "")
|
||||
val = (item.get("Value") or "").strip()
|
||||
if var in _FIELD_MAP and val and val not in ("Not Applicable", "null", "None"):
|
||||
result[_FIELD_MAP[var]] = val
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def fetch_recalls(make: str, model: str, year: str) -> list:
|
||||
url = f"{_NHTSA_BASE}/recalls/recallsByVehicle"
|
||||
params = {"make": make, "model": model, "modelYear": year}
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
resp = await client.get(url, params=params)
|
||||
resp.raise_for_status()
|
||||
return resp.json().get("results", [])
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
async def fetch_complaints(make: str, model: str, year: str) -> list:
|
||||
url = f"{_NHTSA_BASE}/complaints/complaintsByVehicle"
|
||||
params = {"make": make, "model": model, "modelYear": year}
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
resp = await client.get(url, params=params)
|
||||
resp.raise_for_status()
|
||||
return resp.json().get("results", [])
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
async def fetch_investigations(make: str, model: str, year: str) -> list:
|
||||
url = f"{_NHTSA_BASE}/investigations/investigationsByVehicle"
|
||||
params = {"make": make, "model": model, "modelYear": year}
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
resp = await client.get(url, params=params)
|
||||
resp.raise_for_status()
|
||||
return resp.json().get("results", [])
|
||||
except Exception:
|
||||
return []
|
||||
Reference in New Issue
Block a user