Before updates to NHTSA

This commit is contained in:
Eric Gullickson
2025-12-14 14:53:45 -06:00
parent 61e87bb9ad
commit 1fc69b7779
12 changed files with 1680503 additions and 1156458 deletions

518
ETL-FIX-V2.md Normal file
View File

@@ -0,0 +1,518 @@
# ETL Fix V2: Year-Accurate Vehicle Dropdown Data
## Executive Summary
This document provides a complete implementation plan for fixing the vehicle dropdown ETL to produce year-accurate data. The fix addresses impossible year/trim combinations (e.g., "1992 Corvette Z06") by using the NHTSA VPIC API for authoritative Year/Make/Model validation and automobiles.json for evidence-based trim data with year ranges.
---
## Problem Statement
### Current Issues
1. **Year-inaccurate trims**: The `makes-filter/*.json` files contain ALL trims ever made for a model, applied to EVERY year
2. **Impossible combinations**: Users can select "1992 Corvette Z06" (Z06 didn't exist until 2001)
3. **Data bloat**: 400 records for 2000 Corvette with 20 trims instead of ~3-4
### Root Cause
The `makes-filter/*.json` data structure does NOT have year-specific trims. Example from `chevrolet.json`:
```json
{
"year": "2025",
"models": [{
"name": "corvette",
"submodels": ["LT", "35th Anniversary Edition", "427", "Z06", "ZR1", ...]
}]
}
```
The same `submodels` array is repeated for every year, making ALL trims appear valid for ALL years.
---
## Solution Architecture
### Data Sources (Priority Order)
1. **NHTSA VPIC API** - Authoritative Year/Make/Model validation
2. **automobiles.json** - Primary trim source with year-range evidence
3. **makes-filter/*.json** - Engine data enrichment
4. **Defaults** - "Base" trim, "Gas" engine, "Manual"/"Automatic" transmission
### Year Range
- Minimum: 1990
- Maximum: 2026
---
## Implementation Steps
### Phase 1: Create NHTSA Data Fetcher
**Create file:** `data/make-model-import/nhtsa_fetch.py`
```python
#!/usr/bin/env python3
"""
NHTSA VPIC API Data Fetcher
Fetches authoritative Year/Make/Model data from the US government database.
"""
import json
import os
import time
from pathlib import Path
from typing import Dict, List, Set
import urllib.request
import urllib.error
class NHTSAFetcher:
BASE_URL = "https://vpic.nhtsa.dot.gov/api/vehicles"
CACHE_DIR = Path("nhtsa_cache")
OUTPUT_FILE = Path("nhtsa_vehicles.json")
def __init__(self):
self.min_year = int(os.getenv("MIN_YEAR", "1990"))
self.max_year = int(os.getenv("MAX_YEAR", "2026"))
self.request_delay = 0.1 # 100ms between requests
# Makes we care about (from makes-filter)
self.target_makes = self._load_target_makes()
def _load_target_makes(self) -> Set[str]:
"""Load makes from makes-filter directory."""
makes_dir = Path("makes-filter")
makes = set()
for f in makes_dir.glob("*.json"):
make_name = f.stem.replace("_", " ").title()
makes.add(make_name)
return makes
def fetch_url(self, url: str) -> dict:
"""Fetch JSON from URL with error handling."""
try:
with urllib.request.urlopen(url, timeout=30) as response:
return json.loads(response.read().decode())
except urllib.error.URLError as e:
print(f" Error fetching {url}: {e}")
return {"Results": []}
def get_all_makes(self) -> List[Dict]:
"""Fetch all makes for passenger cars and trucks."""
makes = []
for vehicle_type in ["car", "truck"]:
url = f"{self.BASE_URL}/GetMakesForVehicleType/{vehicle_type}?format=json"
data = self.fetch_url(url)
makes.extend(data.get("Results", []))
return makes
def get_models_for_make_year(self, make: str, year: int) -> List[str]:
"""Fetch models for a specific make and year."""
cache_file = self.CACHE_DIR / f"{make.lower().replace(' ', '_')}_{year}.json"
# Check cache first
if cache_file.exists():
with open(cache_file) as f:
return json.load(f)
url = f"{self.BASE_URL}/GetModelsForMakeYear/make/{make}/modelyear/{year}?format=json"
time.sleep(self.request_delay)
data = self.fetch_url(url)
models = list(set(r.get("Model_Name", "") for r in data.get("Results", []) if r.get("Model_Name")))
# Cache result
self.CACHE_DIR.mkdir(exist_ok=True)
with open(cache_file, "w") as f:
json.dump(models, f)
return models
def fetch_all_data(self) -> Dict:
"""Fetch all Year/Make/Model data."""
print("Fetching NHTSA data...")
# Filter to target makes
all_makes = self.get_all_makes()
target_make_names = [m["MakeName"] for m in all_makes
if m["MakeName"].title() in self.target_makes
or m["MakeName"].upper() in ["BMW", "GMC", "RAM"]]
print(f"Found {len(target_make_names)} matching makes")
result = {}
for year in range(self.min_year, self.max_year + 1):
result[str(year)] = {}
for make in target_make_names:
models = self.get_models_for_make_year(make, year)
if models:
# Normalize make name
make_normalized = make.title()
if make.upper() in ["BMW", "GMC", "RAM"]:
make_normalized = make.upper()
result[str(year)][make_normalized] = sorted(models)
print(f" Year {year}: {sum(len(v) for v in result[str(year)].values())} models")
# Save output
with open(self.OUTPUT_FILE, "w") as f:
json.dump(result, f, indent=2)
print(f"Saved to {self.OUTPUT_FILE}")
return result
if __name__ == "__main__":
NHTSAFetcher().fetch_all_data()
```
### Phase 2: Refactor ETL Script
**Modify file:** `data/make-model-import/etl_generate_sql.py`
Key changes:
#### 2.1 Load NHTSA data as primary source
```python
def load_nhtsa_data(self):
"""Load NHTSA Year/Make/Model data."""
nhtsa_file = Path("nhtsa_vehicles.json")
if not nhtsa_file.exists():
raise FileNotFoundError("Run nhtsa_fetch.py first to generate nhtsa_vehicles.json")
with open(nhtsa_file) as f:
self.nhtsa_data = json.load(f)
print(f" Loaded NHTSA data for {len(self.nhtsa_data)} years")
```
#### 2.2 Build trim evidence from automobiles.json
```python
def build_trim_evidence(self):
"""
Parse automobiles.json to build year-range evidence for trims.
"""
self.trim_evidence: Dict[Tuple[str, str], List[Dict]] = defaultdict(list)
brand_lookup = {b.get("id"): self.get_canonical_make_name(b.get("name", ""))
for b in self.brands_data}
for auto in self.automobiles_data:
brand_id = auto.get("brand_id")
make = brand_lookup.get(brand_id)
if not make:
continue
name = auto.get("name", "")
year_range = self.parse_year_range_from_name(name)
if not year_range:
continue
year_start, year_end = year_range
# Extract model and trim from name
model, trim = self.extract_model_trim_from_name(name, make)
if not model:
continue
self.trim_evidence[(make, model)].append({
"trim": trim or "Base",
"year_start": year_start,
"year_end": year_end,
"source_name": name
})
print(f" Built trim evidence for {len(self.trim_evidence)} make/model combinations")
def extract_model_trim_from_name(self, name: str, make: str) -> Tuple[Optional[str], Optional[str]]:
"""
Extract model and trim from automobile name.
Examples:
"CHEVROLET Corvette Z06 2021-Present" -> ("Corvette", "Z06")
"2020 Chevrolet Corvette C8 Stingray" -> ("Corvette", "Stingray")
"FORD F-150 Raptor 2021-Present" -> ("F-150", "Raptor")
"""
# Remove make prefix
clean = re.sub(rf"^{re.escape(make)}\s+", "", name, flags=re.IGNORECASE)
# Remove year/range patterns
clean = re.sub(r"\d{4}(-\d{4}|-Present)?", "", clean)
# Remove common suffixes
clean = re.sub(r"\s*(Photos|engines|full specs|&).*$", "", clean, flags=re.IGNORECASE)
# Clean up extra spaces
clean = " ".join(clean.split())
# Try to match against known models
known_models = self.known_models_by_make.get(make, set())
for model in sorted(known_models, key=len, reverse=True):
pattern = re.compile(rf"^{re.escape(model)}\b\s*(.*)", re.IGNORECASE)
match = pattern.match(clean)
if match:
trim = match.group(1).strip()
# Remove generation codes like C5, C6, C7, C8
trim = re.sub(r"^C\d+\s*", "", trim)
return (model, trim if trim else None)
return (None, None)
```
#### 2.3 New trim resolution logic
```python
def get_trims_for_vehicle(self, year: int, make: str, model: str) -> List[str]:
"""
Get valid trims for a year/make/model combination.
Uses evidence from automobiles.json, falls back to "Base".
"""
evidence = self.trim_evidence.get((make, model), [])
valid_trims = set()
for entry in evidence:
if entry['year_start'] <= year <= entry['year_end']:
valid_trims.add(entry['trim'])
# Always include "Base" as an option
valid_trims.add("Base")
return sorted(valid_trims)
```
#### 2.4 Updated vehicle record building
```python
def build_vehicle_records(self):
"""Build vehicle records using NHTSA for Y/M/M, evidence for trims."""
print("\n Building vehicle option records...")
records = []
for year_str, makes in self.nhtsa_data.items():
year = int(year_str)
if year < self.min_year or year > self.max_year:
continue
for make, models in makes.items():
for model in models:
# Get valid trims from evidence
trims = self.get_trims_for_vehicle(year, make, model)
# Get engines from makes-filter (or default)
engines = self.get_engines_for_vehicle(year, make, model)
# Default transmissions
transmissions = ["Manual", "Automatic"]
for trim in trims:
for engine in engines:
for trans in transmissions:
records.append({
"year": year,
"make": make,
"model": model,
"trim": trim,
"engine_name": engine,
"trans_name": trans
})
# Deduplicate
unique_set = set()
deduped = []
for r in records:
key = (r["year"], r["make"].lower(), r["model"].lower(),
r["trim"].lower(), r["engine_name"].lower(), r["trans_name"].lower())
if key not in unique_set:
unique_set.add(key)
deduped.append(r)
self.vehicle_records = deduped
print(f" Vehicle records: {len(self.vehicle_records):,}")
def get_engines_for_vehicle(self, year: int, make: str, model: str) -> List[str]:
"""Get engines from makes-filter or use defaults."""
# Try to find in makes-filter data
for baseline in self.baseline_records:
if (baseline['year'] == year and
baseline['make'].lower() == make.lower() and
baseline['model'].lower() == model.lower()):
engines = []
for trim_data in baseline.get('trims', []):
engines.extend(trim_data.get('engines', []))
if engines:
return list(set(engines))
# Default based on make/model patterns
model_lower = model.lower()
if 'electric' in model_lower or 'ev' in model_lower or 'lightning' in model_lower:
return ["Electric"]
return ["Gas"]
```
### Phase 3: Update Import Script
**Modify file:** `data/make-model-import/import_data.sh`
Add NHTSA cache check:
```bash
#!/bin/bash
# Import generated SQL files into PostgreSQL database
set -e
echo "=========================================="
echo " Automotive Database Import"
echo "=========================================="
# Check NHTSA cache freshness
NHTSA_FILE="nhtsa_vehicles.json"
CACHE_AGE_DAYS=30
if [ ! -f "$NHTSA_FILE" ]; then
echo "NHTSA data not found. Fetching..."
python3 nhtsa_fetch.py
elif [ $(find "$NHTSA_FILE" -mtime +$CACHE_AGE_DAYS 2>/dev/null | wc -l) -gt 0 ]; then
echo "NHTSA cache is stale (>$CACHE_AGE_DAYS days). Refreshing..."
python3 nhtsa_fetch.py
else
echo "Using cached NHTSA data"
fi
# Continue with existing import logic...
```
### Phase 4: Update QA Validation
**Modify file:** `data/make-model-import/qa_validate.py`
Add invalid combination checks:
```python
def check_invalid_combinations():
"""Verify known invalid combinations do not exist."""
invalid_combos = [
# (year, make, model, trim) - known to be invalid
(1992, 'Chevrolet', 'Corvette', 'Z06'), # Z06 started 2001
(2000, 'Chevrolet', 'Corvette', '35th Anniversary Edition'), # Was 1988
(2000, 'Chevrolet', 'Corvette', 'Stingray'), # Stingray started 2014
(1995, 'Ford', 'Mustang', 'Mach-E'), # Mach-E is 2021+
]
issues = []
for year, make, model, trim in invalid_combos:
query = f"""
SELECT COUNT(*) FROM vehicle_options
WHERE year = {year}
AND make = '{make}'
AND model = '{model}'
AND trim = '{trim}'
"""
count = int(run_psql(query).strip())
if count > 0:
issues.append(f"Invalid combo found: {year} {make} {model} {trim}")
return issues
def check_trim_coverage():
"""Report on trim coverage statistics."""
query = """
SELECT
COUNT(DISTINCT (year, make, model)) as total_models,
COUNT(DISTINCT (year, make, model)) FILTER (WHERE trim = 'Base') as base_only,
COUNT(DISTINCT (year, make, model)) FILTER (WHERE trim != 'Base') as has_specific_trims
FROM vehicle_options
"""
result = run_psql(query).strip()
print(f"Trim coverage: {result}")
```
---
## Files Summary
| File | Action | Purpose |
|------|--------|---------|
| `data/make-model-import/nhtsa_fetch.py` | CREATE | Fetch Year/Make/Model from NHTSA API |
| `data/make-model-import/etl_generate_sql.py` | MODIFY | Use NHTSA data, evidence-based trims |
| `data/make-model-import/import_data.sh` | MODIFY | Add NHTSA cache refresh |
| `data/make-model-import/qa_validate.py` | MODIFY | Add invalid combo checks |
---
## Execution Order
```bash
# 1. Navigate to ETL directory
cd data/make-model-import
# 2. Fetch NHTSA data (creates nhtsa_vehicles.json)
python3 nhtsa_fetch.py
# 3. Generate SQL files
python3 etl_generate_sql.py
# 4. Import to database
./import_data.sh
# 5. Validate results
python3 qa_validate.py
```
---
## Expected Results
### Before
- 2000 Corvette: 400 records, 20 trims (most invalid)
- Total records: ~1,675,335
- Many impossible year/trim combinations
### After
- 2000 Corvette: ~8 records (Base, Coupe, Convertible)
- 2015 Corvette: ~20 records (Stingray, Z06, Grand Sport, Base)
- Total records: ~400,000-600,000
- No invalid year/trim combinations
### Validation Checks
1. No 1992 Corvette Z06
2. No 2000 Corvette Stingray
3. No 1995 Mustang Mach-E
4. Year range: 1990-2026
---
## Data Source Coverage
**automobiles.json trim coverage (samples):**
| Model | Entries | Trims Found |
|-------|---------|-------------|
| Civic | 67 | Si, Type R, eHEV, Sedan, Hatchback |
| Mustang | 38 | GT, Dark Horse, Mach-E GT, GTD |
| Accord | 35 | Sedan, Coupe (various years) |
| Corvette | 31 | Z06, ZR1, Stingray, Grand Sport |
| Camaro | 29 | ZL1, Convertible, Coupe |
| F-150 | 19 | Lightning, Raptor, Tremor |
---
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `MIN_YEAR` | 1990 | Minimum year to include |
| `MAX_YEAR` | 2026 | Maximum year to include |
---
## Troubleshooting
### NHTSA API Rate Limiting
The script includes 100ms delays between requests. If you encounter rate limiting:
- Increase `request_delay` in `nhtsa_fetch.py`
- Use cached data in `nhtsa_cache/` directory
### Missing Models
If NHTSA returns fewer models than expected:
- Check if the make name matches exactly
- Some brands (BMW, GMC) need uppercase handling
- Verify the year range is supported (NHTSA has data back to ~1995)
### Cache Refresh
To force refresh NHTSA data:
```bash
rm nhtsa_vehicles.json
rm -rf nhtsa_cache/
python3 nhtsa_fetch.py
```

View File

@@ -264,6 +264,14 @@ Acceptance:
4) Flush Redis dropdown caches (if needed) and re-test dropdowns. 4) Flush Redis dropdown caches (if needed) and re-test dropdowns.
5) Run QA harness and capture summary output in a `stats.txt` (or similar). 5) Run QA harness and capture summary output in a `stats.txt` (or similar).
## Status Update (completed)
- ETL rewritten to use makes-filter as baseline (year/make/model + trims/engines) and overlay evidence only to prune impossible year/trim combos and enrich engines/transmissions.
- Engines/transmissions now deduped by display name; vehicle_options deduped on full key.
- Uniqueness constraints added to prevent duplicates on import.
- Import script made rerunnable (truncate + restart identity) and prints year range.
- QA script added and validated (duplicates=0, year range 20002026).
- Example issue (GMC Sierra 1500 AT4X 6.2L V8) now present via baseline engines for that trim/year and Automatic/Manual fallback when transmissions are absent.
## Acceptance Criteria (End-to-End) ## Acceptance Criteria (End-to-End)
- Years available in dropdown are exactly those loaded (default 20002026). - Years available in dropdown are exactly those loaded (default 20002026).
- Makes for a year only include makes with models in that year. - Makes for a year only include makes with models in that year.
@@ -272,4 +280,3 @@ Acceptance:
- Engines show detailed specs when available; otherwise show one of `Gas/Diesel/Electric/Hybrid`. - Engines show detailed specs when available; otherwise show one of `Gas/Diesel/Electric/Hybrid`.
- Transmissions show derived options when available; otherwise show both `Manual` and `Automatic`. - Transmissions show derived options when available; otherwise show both `Manual` and `Automatic`.
- No duplicate dimension rows; no duplicate fact rows. - No duplicate dimension rows; no duplicate fact rows.

1048
data/make-model-import/etl_generate_sql.py Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,20 +31,30 @@ docker exec -i mvp-postgres psql -U postgres -d motovaultpro < migrations/001_cr
echo "✓ Schema migration completed" echo "✓ Schema migration completed"
echo "" echo ""
# Truncate tables for a clean rerun
echo "🧹 Step 2: Truncating existing data..."
docker exec -i mvp-postgres psql -U postgres -d motovaultpro <<'EOF'
TRUNCATE TABLE vehicle_options RESTART IDENTITY CASCADE;
TRUNCATE TABLE engines RESTART IDENTITY CASCADE;
TRUNCATE TABLE transmissions RESTART IDENTITY CASCADE;
EOF
echo "✓ Tables truncated"
echo ""
# Import engines # Import engines
echo "📥 Step 2: Importing engines (34MB)..." echo "📥 Step 3: Importing engines..."
docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/01_engines.sql docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/01_engines.sql
echo "✓ Engines imported" echo "✓ Engines imported"
echo "" echo ""
# Import transmissions # Import transmissions
echo "📥 Step 3: Importing transmissions..." echo "📥 Step 4: Importing transmissions..."
docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/02_transmissions.sql docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/02_transmissions.sql
echo "✓ Transmissions imported" echo "✓ Transmissions imported"
echo "" echo ""
# Import vehicle options # Import vehicle options
echo "📥 Step 4: Importing vehicle options (56MB - this may take a minute)..." echo "📥 Step 5: Importing vehicle options (this may take a minute)..."
docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/03_vehicle_options.sql docker exec -i mvp-postgres psql -U postgres -d motovaultpro < output/03_vehicle_options.sql
echo "✓ Vehicle options imported" echo "✓ Vehicle options imported"
echo "" echo ""
@@ -58,6 +68,7 @@ echo "🔍 Database verification:"
docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as engine_count FROM engines;" docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as engine_count FROM engines;"
docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as transmission_count FROM transmissions;" docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as transmission_count FROM transmissions;"
docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as vehicle_count FROM vehicle_options;" docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) as vehicle_count FROM vehicle_options;"
docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT MIN(year) as min_year, MAX(year) as max_year FROM vehicle_options;"
echo "" echo ""
docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT * FROM available_years;" docker exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT * FROM available_years;"
echo "" echo ""

View File

@@ -27,6 +27,8 @@ CREATE TABLE engines (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Prevent duplicate engine display names (case-insensitive)
CREATE UNIQUE INDEX IF NOT EXISTS uq_engines_name_lower ON engines (LOWER(name));
CREATE INDEX idx_engines_displacement ON engines(displacement); CREATE INDEX idx_engines_displacement ON engines(displacement);
CREATE INDEX idx_engines_config ON engines(configuration); CREATE INDEX idx_engines_config ON engines(configuration);
@@ -40,6 +42,8 @@ CREATE TABLE transmissions (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Prevent duplicate transmission display names (case-insensitive)
CREATE UNIQUE INDEX IF NOT EXISTS uq_transmissions_type_lower ON transmissions (LOWER(type));
CREATE INDEX idx_transmissions_type ON transmissions(type); CREATE INDEX idx_transmissions_type ON transmissions(type);
@@ -55,6 +59,10 @@ CREATE TABLE vehicle_options (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Prevent duplicate vehicle option rows
CREATE UNIQUE INDEX IF NOT EXISTS uq_vehicle_options_full ON vehicle_options (
year, make, model, trim, engine_id, transmission_id
);
-- Indexes for cascading dropdown performance -- Indexes for cascading dropdown performance
CREATE INDEX idx_vehicle_year ON vehicle_options(year); CREATE INDEX idx_vehicle_year ON vehicle_options(year);

File diff suppressed because it is too large Load Diff

View File

@@ -5,834 +5,25 @@ BEGIN;
INSERT INTO transmissions (id, type) VALUES INSERT INTO transmissions (id, type) VALUES
(1,'1-Speed Automatic'), (1,'1-Speed Automatic'),
(2,'1-Speed Automatic'), (2,'10-Speed Automatic'),
(3,'1-Speed Automatic'), (3,'2-Speed Automatic'),
(4,'1-Speed Automatic'), (4,'3-Speed Automatic'),
(5,'1-Speed Automatic'), (5,'4-Speed Automatic'),
(6,'1-Speed Automatic'), (6,'4-Speed Manual'),
(7,'10-Speed Automatic'), (7,'5-Speed Automatic'),
(8,'10-Speed Automatic'), (8,'5-Speed Manual'),
(9,'10-Speed Automatic'), (9,'6-Speed Automatic'),
(10,'10-Speed Automatic'), (10,'6-Speed Dual-Clutch'),
(11,'10-Speed Automatic'), (11,'6-Speed Manual'),
(12,'10-Speed Automatic'), (12,'7-Speed Automatic'),
(13,'10-Speed Automatic'), (13,'7-Speed Dual-Clutch'),
(14,'10-Speed Automatic'), (14,'7-Speed Manual'),
(15,'10-Speed Automatic'), (15,'8-Speed Automatic'),
(16,'10-Speed Automatic'), (16,'9-Speed Automatic'),
(17,'10-Speed Automatic'), (17,'Automatic'),
(18,'10-Speed Automatic'), (18,'CVT'),
(19,'2-Speed Automatic'), (19,'Manual');
(20,'2-Speed Automatic'),
(21,'2-Speed Automatic'),
(22,'2-Speed Automatic'),
(23,'2-Speed Automatic'),
(24,'2-Speed Automatic'),
(25,'3-Speed Automatic'),
(26,'3-Speed Automatic'),
(27,'3-Speed Manual'),
(28,'3-Speed Automatic'),
(29,'3-Speed Automatic'),
(30,'3-Speed Automatic'),
(31,'3-Speed Automatic'),
(32,'3-Speed Manual'),
(33,'3-Speed Automatic'),
(34,'3-Speed Automatic'),
(35,'3-Speed Automatic'),
(36,'3-Speed Manual'),
(37,'3-Speed Manual'),
(38,'3-Speed Manual'),
(39,'4-Speed Manual'),
(40,'4-Speed Automatic'),
(41,'4-Speed Automatic'),
(42,'4-Speed Automatic'),
(43,'4-Speed Manual'),
(44,'4-Speed Manual'),
(45,'4-Speed Automatic'),
(46,'4-Speed Automatic'),
(47,'4-Speed Automatic'),
(48,'4-Speed Automatic'),
(49,'4-Speed Automatic'),
(50,'4-Speed Manual'),
(51,'4-Speed Automatic'),
(52,'4-Speed Automatic'),
(53,'4-Speed Automatic'),
(54,'4-Speed Automatic'),
(55,'4-Speed Automatic'),
(56,'4-Speed Automatic'),
(57,'4-Speed Automatic'),
(58,'4-Speed Automatic'),
(59,'4-Speed Automatic'),
(60,'4-Speed Manual'),
(61,'4-Speed Automatic'),
(62,'4-Speed Automatic'),
(63,'4-Speed Automatic'),
(64,'4-Speed Automatic'),
(65,'4-Speed Automatic'),
(66,'4-Speed Manual'),
(67,'4-Speed Manual'),
(68,'4-Speed Manual'),
(69,'4-Speed Automatic'),
(70,'4-Speed Automatic'),
(71,'4-Speed Automatic'),
(72,'5-Speed Manual'),
(73,'5-Speed Manual'),
(74,'5-Speed Automatic'),
(75,'5-Speed Automatic'),
(76,'5-Speed Automatic'),
(77,'5-Speed Automatic'),
(78,'5-Speed Automatic'),
(79,'5-Speed Automatic'),
(80,'5-Speed Manual'),
(81,'5-Speed Manual'),
(82,'5-Speed Manual'),
(83,'5-Speed Manual'),
(84,'5-Speed Manual'),
(85,'5-Speed Automatic'),
(86,'5-Speed Automatic'),
(87,'5-Speed Manual'),
(88,'5-Speed Manual'),
(89,'5-Speed Manual'),
(90,'5-Speed Automatic'),
(91,'5-Speed Automatic'),
(92,'5-Speed Automatic'),
(93,'5-Speed Automatic'),
(94,'5-Speed Automatic'),
(95,'5-Speed Automatic'),
(96,'5-Speed Automatic'),
(97,'5-Speed Automatic'),
(98,'5-Speed Automatic'),
(99,'5-Speed Automatic'),
(100,'5-Speed Automatic'),
(101,'5-Speed Automatic'),
(102,'5-Speed Automatic'),
(103,'5-Speed Automatic'),
(104,'5-Speed Automatic'),
(105,'5-Speed Automatic'),
(106,'5-Speed Automatic'),
(107,'5-Speed Automatic'),
(108,'5-Speed Automatic'),
(109,'5-Speed Automatic'),
(110,'5-Speed Automatic'),
(111,'5-Speed Automatic'),
(112,'5-Speed Automatic'),
(113,'5-Speed Automatic'),
(114,'5-Speed Automatic'),
(115,'5-Speed Automatic'),
(116,'5-Speed Automatic'),
(117,'5-Speed Automatic'),
(118,'5-Speed Automatic'),
(119,'5-Speed Automatic'),
(120,'5-Speed Automatic'),
(121,'5-Speed Automatic'),
(122,'5-Speed Automatic'),
(123,'5-Speed Automatic'),
(124,'5-Speed Automatic'),
(125,'5-Speed Automatic'),
(126,'5-Speed Automatic'),
(127,'5-Speed Automatic'),
(128,'5-Speed Automatic'),
(129,'5-Speed Automatic'),
(130,'5-Speed Automatic'),
(131,'5-Speed Automatic'),
(132,'5-Speed Automatic'),
(133,'5-Speed Automatic'),
(134,'5-Speed Automatic'),
(135,'5-Speed Automatic'),
(136,'5-Speed Automatic'),
(137,'5-Speed Automatic'),
(138,'5-Speed Automatic'),
(139,'5-Speed Automatic'),
(140,'5-Speed Manual'),
(141,'5-Speed Manual'),
(142,'5-Speed Manual'),
(143,'5-Speed Manual'),
(144,'5-Speed Manual'),
(145,'5-Speed Manual'),
(146,'5-Speed Automatic'),
(147,'5-Speed Automatic'),
(148,'5-Speed Automatic'),
(149,'5-Speed Manual'),
(150,'5-Speed Manual'),
(151,'5-Speed Manual'),
(152,'6-Speed Automatic'),
(153,'6-Speed Automatic'),
(154,'6-Speed Automatic'),
(155,'6-Speed Automatic'),
(156,'6-Speed Automatic'),
(157,'6-Speed Automatic'),
(158,'6-Speed Automatic'),
(159,'6-Speed Automatic'),
(160,'6-Speed Automatic'),
(161,'6-Speed Automatic'),
(162,'6-Speed Automatic'),
(163,'6-Speed Automatic'),
(164,'CVT'),
(165,'6-Speed Manual'),
(166,'6-Speed Manual'),
(167,'6-Speed Manual'),
(168,'6-Speed Manual'),
(169,'6-Speed Manual'),
(170,'6-Speed Automatic'),
(171,'6-Speed Automatic'),
(172,'6-Speed Automatic'),
(173,'6-Speed Automatic'),
(174,'6-Speed Automatic'),
(175,'6-Speed Manual'),
(176,'6-Speed Automatic'),
(177,'6-Speed Manual'),
(178,'6-Speed Manual'),
(179,'6-Speed Automatic'),
(180,'6-Speed Automatic'),
(181,'6-Speed Automatic'),
(182,'6-Speed Automatic'),
(183,'6-Speed Automatic'),
(184,'6-Speed Automatic'),
(185,'6-Speed Automatic'),
(186,'6-Speed Automatic'),
(187,'6-Speed Automatic'),
(188,'6-Speed Automatic'),
(189,'6-Speed Automatic'),
(190,'6-Speed Automatic'),
(191,'6-Speed Automatic'),
(192,'6-Speed Automatic'),
(193,'6-Speed Automatic'),
(194,'6-Speed Manual'),
(195,'6-Speed Automatic'),
(196,'6-Speed Manual'),
(197,'6-Speed Manual'),
(198,'6-Speed Manual'),
(199,'6-Speed Manual'),
(200,'6-Speed Manual'),
(201,'6-Speed Manual'),
(202,'6-Speed Automatic'),
(203,'6-Speed Automatic'),
(204,'6-Speed Automatic'),
(205,'6-Speed Automatic'),
(206,'6-Speed Automatic'),
(207,'6-Speed Automatic'),
(208,'6-Speed Automatic'),
(209,'6-Speed Automatic'),
(210,'6-Speed Automatic'),
(211,'6-Speed Automatic'),
(212,'6-Speed Automatic'),
(213,'6-Speed Automatic'),
(214,'6-Speed Automatic'),
(215,'6-Speed Automatic'),
(216,'6-Speed Automatic'),
(217,'6-Speed Automatic'),
(218,'6-Speed Automatic'),
(219,'6-Speed Automatic'),
(220,'6-Speed Automatic'),
(221,'6-Speed Automatic'),
(222,'6-Speed Automatic'),
(223,'6-Speed Automatic'),
(224,'6-Speed Automatic'),
(225,'6-Speed Automatic'),
(226,'6-Speed Automatic'),
(227,'6-Speed Automatic'),
(228,'6-Speed Automatic'),
(229,'6-Speed Automatic'),
(230,'6-Speed Automatic'),
(231,'6-Speed Automatic'),
(232,'6-Speed Automatic'),
(233,'6-Speed Automatic'),
(234,'6-Speed Automatic'),
(235,'6-Speed Automatic'),
(236,'6-Speed Automatic'),
(237,'6-Speed Automatic'),
(238,'6-Speed Automatic'),
(239,'6-Speed Automatic'),
(240,'6-Speed Automatic'),
(241,'6-Speed Automatic'),
(242,'6-Speed Automatic'),
(243,'6-Speed Automatic'),
(244,'6-Speed Automatic'),
(245,'6-Speed Automatic'),
(246,'6-Speed Automatic'),
(247,'6-Speed Automatic'),
(248,'6-Speed Automatic'),
(249,'6-Speed Automatic'),
(250,'6-Speed Automatic'),
(251,'6-Speed Automatic'),
(252,'6-Speed Automatic'),
(253,'6-Speed Automatic'),
(254,'6-Speed Automatic'),
(255,'6-Speed Automatic'),
(256,'6-Speed Automatic'),
(257,'6-Speed Automatic'),
(258,'6-Speed Automatic'),
(259,'6-Speed Automatic'),
(260,'6-Speed Automatic'),
(261,'6-Speed Automatic'),
(262,'6-Speed Automatic'),
(263,'6-Speed Automatic'),
(264,'6-Speed Automatic'),
(265,'6-Speed Automatic'),
(266,'6-Speed Automatic'),
(267,'6-Speed Automatic'),
(268,'6-Speed Automatic'),
(269,'6-Speed Automatic'),
(270,'6-Speed Automatic'),
(271,'6-Speed Automatic'),
(272,'6-Speed Automatic'),
(273,'6-Speed Automatic'),
(274,'6-Speed Automatic'),
(275,'6-Speed Automatic'),
(276,'6-Speed Automatic'),
(277,'6-Speed Automatic'),
(278,'6-Speed Automatic'),
(279,'CVT'),
(280,'6-Speed Dual-Clutch'),
(281,'6-Speed Automatic'),
(282,'6-Speed Automatic'),
(283,'6-Speed Automatic'),
(284,'6-Speed Automatic'),
(285,'6-Speed Automatic'),
(286,'6-Speed Automatic'),
(287,'6-Speed Automatic'),
(288,'6-Speed Automatic'),
(289,'6-Speed Manual'),
(290,'6-Speed Manual'),
(291,'6-Speed Manual'),
(292,'6-Speed Manual'),
(293,'6-Speed Manual'),
(294,'6-Speed Manual'),
(295,'6-Speed Manual'),
(296,'6-Speed Manual'),
(297,'6-Speed Manual'),
(298,'6-Speed Manual'),
(299,'6-Speed Manual'),
(300,'6-Speed Manual'),
(301,'6-Speed Manual'),
(302,'6-Speed Manual'),
(303,'6-Speed Automatic'),
(304,'6-Speed Automatic'),
(305,'6-Speed Automatic'),
(306,'6-Speed Dual-Clutch'),
(307,'6-Speed Automatic'),
(308,'6-Speed Automatic'),
(309,'6-Speed Automatic'),
(310,'6-Speed Automatic'),
(311,'6-Speed Automatic'),
(312,'6-Speed Automatic'),
(313,'6-Speed Automatic'),
(314,'6-Speed Automatic'),
(315,'6-Speed Manual'),
(316,'6-Speed Manual'),
(317,'6-Speed Manual'),
(318,'6-Speed Automatic'),
(319,'7-Speed Automatic'),
(320,'7-Speed Automatic'),
(321,'7-Speed Automatic'),
(322,'7-Speed Automatic'),
(323,'7-Speed Automatic'),
(324,'7-Speed Automatic'),
(325,'7-Speed Automatic'),
(326,'7-Speed Manual'),
(327,'7-Speed Automatic'),
(328,'7-Speed Automatic'),
(329,'7-Speed Automatic'),
(330,'7-Speed Automatic'),
(331,'7-Speed Automatic'),
(332,'7-Speed Automatic'),
(333,'7-Speed Automatic'),
(334,'7-Speed Automatic'),
(335,'7-Speed Automatic'),
(336,'7-Speed Automatic'),
(337,'7-Speed Automatic'),
(338,'7-Speed Automatic'),
(339,'7-Speed Automatic'),
(340,'7-Speed Automatic'),
(341,'7-Speed Automatic'),
(342,'7-Speed Automatic'),
(343,'7-Speed Automatic'),
(344,'7-Speed Automatic'),
(345,'7-Speed Automatic'),
(346,'7-Speed Automatic'),
(347,'7-Speed Automatic'),
(348,'7-Speed Automatic'),
(349,'7-Speed Automatic'),
(350,'7-Speed Automatic'),
(351,'7-Speed Automatic'),
(352,'7-Speed Automatic'),
(353,'7-Speed Automatic'),
(354,'7-Speed Automatic'),
(355,'7-Speed Automatic'),
(356,'7-Speed Automatic'),
(357,'7-Speed Automatic'),
(358,'7-Speed Automatic'),
(359,'7-Speed Automatic'),
(360,'7-Speed Automatic'),
(361,'7-Speed Automatic'),
(362,'7-Speed Automatic'),
(363,'7-Speed Automatic'),
(364,'7-Speed Automatic'),
(365,'7-Speed Automatic'),
(366,'7-Speed Automatic'),
(367,'7-Speed Automatic'),
(368,'7-Speed Automatic'),
(369,'7-Speed Automatic'),
(370,'7-Speed Automatic'),
(371,'7-Speed Automatic'),
(372,'7-Speed Automatic'),
(373,'7-Speed Automatic'),
(374,'7-Speed Automatic'),
(375,'7-Speed Automatic'),
(376,'7-Speed Automatic'),
(377,'7-Speed Automatic'),
(378,'7-Speed Automatic'),
(379,'7-Speed Automatic'),
(380,'7-Speed Automatic'),
(381,'7-Speed Automatic'),
(382,'7-Speed Automatic'),
(383,'7-Speed Automatic'),
(384,'7-Speed Automatic'),
(385,'7-Speed Automatic'),
(386,'7-Speed Automatic'),
(387,'7-Speed Automatic'),
(388,'7-Speed Automatic'),
(389,'7-Speed Automatic'),
(390,'7-Speed Automatic'),
(391,'7-Speed Automatic'),
(392,'7-Speed Automatic'),
(393,'7-Speed Automatic'),
(394,'7-Speed Automatic'),
(395,'7-Speed Automatic'),
(396,'7-Speed Automatic'),
(397,'7-Speed Automatic'),
(398,'7-Speed Automatic'),
(399,'7-Speed Automatic'),
(400,'7-Speed Automatic'),
(401,'7-Speed Automatic'),
(402,'7-Speed Automatic'),
(403,'7-Speed Automatic'),
(404,'7-Speed Automatic'),
(405,'7-Speed Automatic'),
(406,'7-Speed Automatic'),
(407,'7-Speed Automatic'),
(408,'7-Speed Automatic'),
(409,'7-Speed Automatic'),
(410,'7-Speed Automatic'),
(411,'7-Speed Automatic'),
(412,'7-Speed Automatic'),
(413,'7-Speed Automatic'),
(414,'7-Speed Automatic'),
(415,'7-Speed Automatic'),
(416,'7-Speed Automatic'),
(417,'7-Speed Automatic'),
(418,'7-Speed Automatic'),
(419,'7-Speed Automatic'),
(420,'7-Speed Automatic'),
(421,'7-Speed Automatic'),
(422,'7-Speed Automatic'),
(423,'7-Speed Automatic'),
(424,'7-Speed Automatic'),
(425,'7-Speed Automatic'),
(426,'7-Speed Automatic'),
(427,'7-Speed Automatic'),
(428,'7-Speed Automatic'),
(429,'7-Speed Automatic'),
(430,'7-Speed Automatic'),
(431,'7-Speed Automatic'),
(432,'7-Speed Automatic'),
(433,'7-Speed Automatic'),
(434,'7-Speed Automatic'),
(435,'7-Speed Automatic'),
(436,'7-Speed Automatic'),
(437,'7-Speed Automatic'),
(438,'7-Speed Automatic'),
(439,'7-Speed Automatic'),
(440,'7-Speed Automatic'),
(441,'7-Speed Automatic'),
(442,'7-Speed Automatic'),
(443,'7-Speed Automatic'),
(444,'7-Speed Automatic'),
(445,'7-Speed Automatic'),
(446,'7-Speed Automatic'),
(447,'7-Speed Automatic'),
(448,'7-Speed Automatic'),
(449,'7-Speed Automatic'),
(450,'7-Speed Automatic'),
(451,'7-Speed Automatic'),
(452,'7-Speed Automatic'),
(453,'7-Speed Automatic'),
(454,'7-Speed Automatic'),
(455,'CVT'),
(456,'7-Speed Dual-Clutch'),
(457,'7-Speed Automatic'),
(458,'7-Speed Automatic'),
(459,'7-Speed Manual'),
(460,'7-Speed Automatic'),
(461,'7-Speed Automatic'),
(462,'7-Speed Automatic'),
(463,'7-Speed Manual'),
(464,'7-Speed Manual'),
(465,'7-Speed Manual'),
(466,'7-Speed Manual'),
(467,'7-Speed Manual'),
(468,'7-Speed Dual-Clutch'),
(469,'7-Speed Automatic'),
(470,'7-Speed Automatic'),
(471,'7-Speed Automatic'),
(472,'7-Speed Manual'),
(473,'7-Speed Automatic'),
(474,'7-Speed Automatic'),
(475,'7-Speed Automatic'),
(476,'7-Speed Automatic'),
(477,'7-Speed Automatic'),
(478,'7-Speed Automatic'),
(479,'7-Speed Automatic'),
(480,'7-Speed Automatic'),
(481,'7-Speed Automatic'),
(482,'7-Speed Automatic'),
(483,'7-Speed Automatic'),
(484,'7-Speed Automatic'),
(485,'7-Speed Automatic'),
(486,'7-Speed Automatic'),
(487,'7-Speed Automatic'),
(488,'7-Speed Automatic'),
(489,'77-Speed Automatic'),
(490,'7-Speed Automatic'),
(491,'7-Speed Automatic'),
(492,'7-Speed Automatic'),
(493,'8-Speed Automatic'),
(494,'8-Speed Automatic'),
(495,'8-Speed Automatic'),
(496,'8-Speed Automatic'),
(497,'8-Speed Automatic'),
(498,'8-Speed Automatic'),
(499,'8-Speed Automatic'),
(500,'8-Speed Automatic'),
(501,'8-Speed Automatic'),
(502,'8-Speed Automatic'),
(503,'8-Speed Automatic'),
(504,'8-Speed Automatic'),
(505,'8-Speed Automatic'),
(506,'8-Speed Automatic'),
(507,'8-Speed Automatic'),
(508,'8-Speed Automatic'),
(509,'8-Speed Automatic'),
(510,'8-Speed Automatic'),
(511,'8-Speed Automatic'),
(512,'8-Speed Automatic'),
(513,'8-Speed Automatic'),
(514,'8-Speed Automatic'),
(515,'8-Speed Automatic'),
(516,'8-Speed Automatic'),
(517,'8-Speed Automatic'),
(518,'8-Speed Automatic'),
(519,'8-Speed Automatic'),
(520,'8-Speed Automatic'),
(521,'8-Speed Automatic'),
(522,'8-Speed Automatic'),
(523,'8-Speed Automatic'),
(524,'8-Speed Automatic'),
(525,'8-Speed Automatic'),
(526,'8-Speed Automatic'),
(527,'8-Speed Automatic'),
(528,'8-Speed Automatic'),
(529,'8-Speed Automatic'),
(530,'8-Speed Automatic'),
(531,'8-Speed Automatic'),
(532,'8-Speed Automatic'),
(533,'8-Speed Automatic'),
(534,'8-Speed Automatic'),
(535,'8-Speed Automatic'),
(536,'8-Speed Automatic'),
(537,'8-Speed Automatic'),
(538,'8-Speed Automatic'),
(539,'8-Speed Automatic'),
(540,'8-Speed Automatic'),
(541,'8-Speed Automatic'),
(542,'8-Speed Automatic'),
(543,'8-Speed Automatic'),
(544,'8-Speed Automatic'),
(545,'8-Speed Automatic'),
(546,'8-Speed Automatic'),
(547,'8-Speed Automatic'),
(548,'8-Speed Automatic'),
(549,'8-Speed Automatic'),
(550,'8-Speed Automatic'),
(551,'8-Speed Automatic'),
(552,'8-Speed Automatic'),
(553,'8-Speed Automatic'),
(554,'8-Speed Automatic'),
(555,'8-Speed Automatic'),
(556,'8-Speed Automatic'),
(557,'8-Speed Automatic'),
(558,'8-Speed Automatic'),
(559,'8-Speed Automatic'),
(560,'8-Speed Automatic'),
(561,'8-Speed Automatic'),
(562,'8-Speed Automatic'),
(563,'8-Speed Automatic'),
(564,'8-Speed Automatic'),
(565,'8-Speed Automatic'),
(566,'8-Speed Automatic'),
(567,'8-Speed Automatic'),
(568,'8-Speed Automatic'),
(569,'8-Speed Automatic'),
(570,'8-Speed Automatic'),
(571,'8-Speed Automatic'),
(572,'8-Speed Automatic'),
(573,'8-Speed Automatic'),
(574,'8-Speed Dual-Clutch'),
(575,'8-Speed Automatic'),
(576,'8-Speed Automatic'),
(577,'8-Speed Automatic'),
(578,'8-Speed Automatic'),
(579,'8-Speed Automatic'),
(580,'8-Speed Automatic'),
(581,'8-Speed Automatic'),
(582,'8-Speed Automatic'),
(583,'8-Speed Automatic'),
(584,'8-Speed Automatic'),
(585,'8-Speed Automatic'),
(586,'8-Speed Automatic'),
(587,'9-Speed Automatic'),
(588,'9-Speed Automatic'),
(589,'9-Speed Automatic'),
(590,'9-Speed Automatic'),
(591,'9-Speed Automatic'),
(592,'9-Speed Automatic'),
(593,'9-Speed Automatic'),
(594,'9-Speed Automatic'),
(595,'9-Speed Automatic'),
(596,'9-Speed Automatic'),
(597,'9-Speed Automatic'),
(598,'9-Speed Automatic'),
(599,'9-Speed Automatic'),
(600,'9-Speed Automatic'),
(601,'9-Speed Automatic'),
(602,'9-Speed Automatic'),
(603,'9-Speed Automatic'),
(604,'9-Speed Automatic'),
(605,'9-Speed Automatic'),
(606,'9-Speed Automatic'),
(607,'9-Speed Automatic'),
(608,'9-Speed Automatic'),
(609,'9-Speed Automatic'),
(610,'9-Speed Automatic'),
(611,'9-Speed Automatic'),
(612,'9-Speed Automatic'),
(613,'9-Speed Automatic'),
(614,'9-Speed Automatic'),
(615,'9-Speed Automatic'),
(616,'9-Speed Automatic'),
(617,'9-Speed Automatic'),
(618,'9-Speed Automatic'),
(619,'9-Speed Automatic'),
(620,'9-Speed Automatic'),
(621,'9-Speed Automatic'),
(622,'6-Speed Automatic'),
(623,'7-Speed Automatic'),
(624,'7-Speed Automatic'),
(625,'7-Speed Automatic'),
(626,'7-Speed Automatic'),
(627,'9-Speed Automatic'),
(628,'5-Speed Manual'),
(629,'Automatic'),
(630,'7-Speed Manual'),
(631,'7-Speed Manual'),
(632,'Automatic'),
(633,'Automatic'),
(634,'Automatic'),
(635,'Automatic'),
(636,'Automatic'),
(637,'Automatic'),
(638,'6-Speed Automatic'),
(639,'Automatic'),
(640,'3-Speed Automatic'),
(641,'3-Speed Automatic'),
(642,'4-Speed Automatic'),
(643,'4-Speed Automatic'),
(644,'4-Speed Automatic'),
(645,'4-Speed Automatic'),
(646,'4-Speed Automatic'),
(647,'5-Speed Automatic'),
(648,'5-Speed Automatic'),
(649,'5-Speed Automatic'),
(650,'5-Speed Automatic'),
(651,'5-Speed Automatic'),
(652,'6-Speed Automatic'),
(653,'6-Speed Automatic'),
(654,'6-Speed Automatic'),
(655,'7-Speed Automatic'),
(656,'7-Speed Automatic'),
(657,'7-Speed Automatic'),
(658,'7-Speed Automatic'),
(659,'8-Speed Automatic'),
(660,'8-Speed Automatic'),
(661,'Automatic'),
(662,'Automatic'),
(663,'Automatic'),
(664,'Automatic'),
(665,'Automatic'),
(666,'Automatic'),
(667,'CVT'),
(668,'CVT'),
(669,'Automatic'),
(670,'Automatic'),
(671,'CVT'),
(672,'CVT'),
(673,'CVT'),
(674,'CVT'),
(675,'CVT'),
(676,'CVT'),
(677,'CVT'),
(678,'CVT'),
(679,'CVT'),
(680,'Automatic'),
(681,'Automatic'),
(682,'Automatic'),
(683,'CVT'),
(684,'CVT'),
(685,'CVT'),
(686,'CVT'),
(687,'CVT'),
(688,'CVT'),
(689,'CVT'),
(690,'CVT'),
(691,'CVT'),
(692,'CVT'),
(693,'CVT'),
(694,'CVT'),
(695,'CVT'),
(696,'CVT'),
(697,'CVT'),
(698,'Automatic'),
(699,'7-Speed Manual'),
(700,'CVT'),
(701,'CVT'),
(702,'CVT'),
(703,'CVT'),
(704,'Automatic'),
(705,'5-Speed Automatic'),
(706,'CVT'),
(707,'CVT'),
(708,'CVT'),
(709,'Automatic'),
(710,'15-Speed Automatic'),
(711,'CVT'),
(712,'CVT'),
(713,'CVT'),
(714,'CVT'),
(715,'CVT'),
(716,'Automatic'),
(717,'Automatic'),
(718,'Automatic'),
(719,'Automatic'),
(720,'Automatic'),
(721,'Automatic'),
(722,'Automatic'),
(723,'Automatic'),
(724,'CVT'),
(725,'CVT'),
(726,'CVT'),
(727,'CVT'),
(728,'Manual'),
(729,'6-Speed Manual'),
(730,'8-Speed Manual'),
(731,'Automatic'),
(732,'CVT'),
(733,'150-Speed Automatic'),
(734,'6-Speed Automatic'),
(735,'6-Speed Automatic'),
(736,'6-Speed Automatic'),
(737,'6-Speed Automatic'),
(738,'6-Speed Automatic'),
(739,'6-Speed Automatic'),
(740,'Dual-Clutch'),
(741,'Automatic'),
(742,'Automatic'),
(743,'Automatic'),
(744,'Automatic'),
(745,'Automatic'),
(746,'CVT'),
(747,'CVT'),
(748,'Automatic'),
(749,'6-Speed Manual'),
(750,'8-Speed Manual'),
(751,'355-Speed Manual'),
(752,'Manual'),
(753,'4-Speed Manual'),
(754,'1-Speed Manual'),
(755,'2-Speed Manual'),
(756,'3-Speed Manual'),
(757,'3-Speed Manual'),
(758,'4-Speed Manual'),
(759,'4-Speed Manual'),
(760,'4-Speed Manual'),
(761,'5-Speed Manual'),
(762,'5-Speed Manual'),
(763,'5-Speed Manual'),
(764,'6-Speed Manual'),
(765,'6-Speed Manual'),
(766,'6-Speed Manual'),
(767,'CVT'),
(768,'Automatic'),
(769,'CVT'),
(770,'CVT'),
(771,'265-Speed Automatic'),
(772,'Automatic'),
(773,'Automatic'),
(774,'6-Speed Automatic'),
(775,'Automatic'),
(776,'3-Speed Automatic'),
(777,'3-Speed Automatic'),
(778,'4-Speed Automatic'),
(779,'4-Speed Automatic'),
(780,'5-Speed Automatic'),
(781,'5-Speed Automatic'),
(782,'6-Speed Automatic'),
(783,'6-Speed Automatic'),
(784,'8-Speed Automatic'),
(785,'Automatic'),
(786,'Automatic'),
(787,'Automatic'),
(788,'Automatic'),
(789,'Automatic'),
(790,'Automatic'),
(791,'Automatic'),
(792,'Dual-Clutch'),
(793,'Automatic'),
(794,'Automatic'),
(795,'Automatic'),
(796,'Automatic'),
(797,'Automatic'),
(798,'Automatic'),
(799,'Automatic'),
(800,'Automatic'),
(801,'Manual'),
(802,'Manual'),
(803,'Automatic'),
(804,'Automatic'),
(805,'2-Speed Automatic'),
(806,'Automatic'),
(807,'Automatic'),
(808,'Automatic'),
(809,'Automatic'),
(810,'Manual'),
(811,'Manual'),
(812,'Manual'),
(813,'Manual'),
(814,'Manual'),
(815,'Manual'),
(816,'Automatic'),
(817,'Automatic'),
(818,'2-Speed Automatic'),
(819,'Automatic'),
(820,'Automatic'),
(821,'Automatic'),
(822,'5-Speed Automatic'),
(823,'Automatic'),
(824,'Automatic'),
(825,'CVT'),
(826,'CVT'),
(827,'8-Speed Automatic'),
(828,'8-Speed Automatic');
SELECT setval('transmissions_id_seq', 829); SELECT setval('transmissions_id_seq', 19);
COMMIT; COMMIT;

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,10 @@
ETL Statistics ETL Statistics
============================================================ ============================================================
Total Engines: 30,066 Min Year: 2,000
Total Transmissions: 828 Max Year: 2,026
Total Vehicles: 1,122,644 Vehicle Records: 1,675,335
Unique Years: 47 Engines: 622
Unique Makes: 53 Transmissions: 19
Unique Models: 1,741 Makes: 54
Year Range: 1980-2026 Models: 1,881

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
Post-import QA validation for vehicle dropdown data.
Runs basic duplicate and range checks against the motovaultpro Postgres container.
"""
import os
import subprocess
import sys
def run_psql(query: str) -> str:
cmd = [
"docker",
"exec",
"mvp-postgres",
"psql",
"-U",
"postgres",
"-d",
"motovaultpro",
"-At",
"-c",
query,
]
return subprocess.check_output(cmd, text=True)
def check_container():
try:
subprocess.check_output(["docker", "ps"], text=True)
except Exception:
print("❌ Docker not available.")
sys.exit(1)
try:
containers = subprocess.check_output(
["docker", "ps", "--filter", "name=mvp-postgres", "--format", "{{.Names}}"],
text=True,
).strip()
if not containers:
print("❌ mvp-postgres container not running.")
sys.exit(1)
except Exception as exc:
print(f"❌ Failed to check containers: {exc}")
sys.exit(1)
def main():
check_container()
print("🔍 Running QA checks...\n")
queries = {
"engine_duplicate_names": """
SELECT COUNT(*) FROM (
SELECT LOWER(name) as n, COUNT(*) c
FROM engines
GROUP BY 1 HAVING COUNT(*) > 1
) t;
""",
"transmission_duplicate_types": """
SELECT COUNT(*) FROM (
SELECT LOWER(type) as t, COUNT(*) c
FROM transmissions
GROUP BY 1 HAVING COUNT(*) > 1
) t;
""",
"vehicle_option_duplicates": """
SELECT COUNT(*) FROM (
SELECT year, make, model, trim, engine_id, transmission_id, COUNT(*) c
FROM vehicle_options
GROUP BY 1,2,3,4,5,6 HAVING COUNT(*) > 1
) t;
""",
"year_range": """
SELECT MIN(year) || ' - ' || MAX(year) FROM vehicle_options;
""",
"counts": """
SELECT
(SELECT COUNT(*) FROM engines) AS engines,
(SELECT COUNT(*) FROM transmissions) AS transmissions,
(SELECT COUNT(*) FROM vehicle_options) AS vehicle_options;
""",
}
results = {}
for key, query in queries.items():
try:
results[key] = run_psql(query).strip()
except subprocess.CalledProcessError as exc:
print(f"❌ Query failed ({key}): {exc}")
sys.exit(1)
print(f"Engine duplicate names: {results['engine_duplicate_names']}")
print(f"Transmission duplicate types: {results['transmission_duplicate_types']}")
print(f"Vehicle option duplicates: {results['vehicle_option_duplicates']}")
print(f"Year range: {results['year_range']}")
print(f"Counts (engines, transmissions, vehicle_options): {results['counts']}")
if (
results["engine_duplicate_names"] == "0"
and results["transmission_duplicate_types"] == "0"
and results["vehicle_option_duplicates"] == "0"
):
print("\n✅ QA checks passed.")
else:
print("\n❌ QA checks found issues.")
if __name__ == "__main__":
main()

View File

@@ -20,7 +20,7 @@ Your task is to create a plan that can be dispatched to a seprate set of AI agen
*** PERSONALITY *** *** PERSONALITY ***
Read README.md CLAUDE.md and AI-INDEX.md to understand this code repository. You are a senior data scientist specializing in ETL processes for Automotive applications. Read README.md CLAUDE.md and AI-INDEX.md to understand this code repository. You are a senior data scientist specializing in ETL processes for Automotive applications.
Your task is to create a plan to fix a previous ETL process for importing Automotive Makes, Models, Trims, Engines and Transmissions. The resulting data is not clean and accurate. The folder to start research in is the make-model-import folder. There is documentation in there from the previous implementation. Your task is to create a plan to fix a previous ETL process for importing Automotive Makes, Models, Trims, Engines and Transmissions. The resulting data is not clean and accurate. Read @ETL-FIXES.md as that was the latest attempt to fix this problem. The folder to start research in is the make-model-import folder. There is documentation in there from the previous implementation.
*** FEATURE *** *** FEATURE ***
- This is focusing on the backend data in the database for the Vehicles features that populates the drop down menus. - This is focusing on the backend data in the database for the Vehicles features that populates the drop down menus.
@@ -51,4 +51,5 @@ Your task is to create a plan to fix a previous ETL process for importing Automo
- If no specific transmission data is available default to "Manual" or "Automatic" - If no specific transmission data is available default to "Manual" or "Automatic"
*** CRITICAL *** *** CRITICAL ***
- Make no assumptions. Ask for clarification on anything not clear. - Make no assumptions. Ask for clarification on anything not clear.
- Ultrathink through this problem.