feat: 7-module VINchecker v2 — NICB, auction history, Florida DMV, theft stats, odometer fraud, safety ratings, risk score expansion

New modules: nicb.py, auction_history.py, dmv_florida.py, theft_stats.py, odometer_validator.py
Expanded risk.py with 8 new factors (NICB alert, safety rating, auction flags, odo validation, theft level, odo fraud, DMV lien, open VIN recalls)
PDF now has 11 sections including NICB check, theft stats, odometer fraud, odometer validation, auction history, Florida HSMV
AI prompt enriched with all new data fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 13:54:16 -04:00
parent 078128f7b1
commit df09627ccb
11 changed files with 549 additions and 18 deletions
+53 -14
View File
@@ -21,10 +21,15 @@ from src.ai.ollama_analyzer import analyze_vehicle
from src.api.epa import fetch_epa_data
from src.api.nhtsa import (
decode_vin, fetch_complaints, fetch_investigations, fetch_recalls,
fetch_vin_recalls, fetch_safety_ratings,
fetch_vin_recalls, fetch_safety_ratings, check_odometer_complaints,
)
from src.api.nicb import check_nicb
from src.api.auction_history import search_auction_history
from src.api.dmv_florida import check_florida_dmv
from src.api.theft_stats import get_theft_risk
from src.report.pdf import generate_pdf
from src.utils.risk import calculate_risk, compute_extended_fields
from src.utils.odometer_validator import validate_odometer
from src.utils.validator import validate_vin
Path("output").mkdir(exist_ok=True)
@@ -117,31 +122,44 @@ async def fetch(
yield evt(f"✅ Vehículo: {year} {make} {model}", "success")
await asyncio.sleep(0.05)
# 3 — Recalls + Complaints + Investigations + VIN check + Safety (paralelo)
yield evt("⏳ Consultando NHTSA: recalls, quejas, VIN check, seguridad...", "progress")
recalls, complaints, invests, vin_recalls, safety_ratings = await asyncio.gather(
# 3 — NHTSA + EPA + NICB + Subastas + DMV (todo en paralelo)
yield evt("⏳ Consultando NHTSA, EPA, NICB, historial web, Florida DMV...", "progress")
(
recalls, complaints, invests,
vin_recalls, safety_ratings, epa,
nicb, auction_hist, dmv_fl,
) = await asyncio.gather(
fetch_recalls(make, model, year),
fetch_complaints(make, model, year),
fetch_investigations(make, model, year),
fetch_vin_recalls(vin_clean),
fetch_safety_ratings(year, make, model),
fetch_epa_data(year, make, model),
check_nicb(vin_clean),
search_auction_history(vin_clean),
check_florida_dmv(vin_clean),
)
open_count = len(vin_recalls)
# Local enrichment (no I/O)
theft_info = get_theft_risk(make, model)
odo_check = validate_odometer(year, odometer or "0")
odo_fraud = check_odometer_complaints(complaints)
open_vin_recalls = len([
r for r in vin_recalls
if not (r.get("RemedyAvailability") or "").lower().startswith("remedy")
])
stars = safety_ratings.get("overall", "")
stars_txt = f" · {stars}★ seguridad" if stars and stars != "NR" else ""
nicb_status = nicb.get("status", "NO_DISPONIBLE")
yield evt(
f"{len(recalls)} recalls · {len(complaints)} quejas · {open_count} VIN-recalls{stars_txt}",
f"{len(recalls)} recalls · {len(complaints)} quejas · "
f"NICB: {nicb_status} · "
f"{stars+'' if stars and stars != 'NR' else 'seg. N/A'}",
"success",
)
await asyncio.sleep(0.05)
# 4 — EPA
yield evt("⏳ Consultando EPA FuelEconomy...", "progress")
epa = await fetch_epa_data(year, make, model)
yield evt(f"✅ Eficiencia: {epa.get('mpg_combined', 'N/A')}", "success")
await asyncio.sleep(0.05)
# 5 — Photo
# 4 — Photo
photo_bytes = None
if photo_url and photo_url.strip():
yield evt("⏳ Descargando foto del vehículo...", "progress")
@@ -166,6 +184,15 @@ async def fetch(
secondary_damage=secondary_damage or "",
complaints=complaints,
odometer=odometer or "0",
nicb_status=nicb_status,
safety_overall=stars,
auction_flags=auction_hist.get("flags", []),
auction_pts=auction_hist.get("extra_points", 0),
odo_extra_pts=odo_check.get("extra_points", 0),
theft_level=theft_info.get("level", ""),
odo_fraud_count=odo_fraud.get("count", 0),
dmv_has_lien=dmv_fl.get("has_lien", False),
open_vin_recalls=open_vin_recalls,
)
extended = compute_extended_fields(
title=title or "",
@@ -195,6 +222,12 @@ async def fetch(
"complaints": complaints,
"vin_recalls": vin_recalls,
"safety_ratings": safety_ratings,
"nicb": nicb,
"auction_hist": auction_hist,
"dmv_fl": dmv_fl,
"theft_info": theft_info,
"odo_check": odo_check,
"odo_fraud": odo_fraud,
"critical_recall_count": extended["critical_recall_count"],
"engine_complaints": extended["engine_complaints"],
"transmission_complaints": extended["transmission_complaints"],
@@ -224,6 +257,12 @@ async def fetch(
"investigations": invests,
"vin_recalls": vin_recalls,
"safety_ratings": safety_ratings,
"nicb": nicb,
"auction_hist": auction_hist,
"dmv_fl": dmv_fl,
"theft_info": theft_info,
"odo_check": odo_check,
"odo_fraud": odo_fraud,
"epa": epa,
"risk": risk,
"ai_analysis": ai_analysis,