FASE 1: Parts Database - Added part_categories, part_groups, parts, vehicle_parts tables - 12 categories, 190 groups with Spanish translations - API endpoints for categories, groups, parts CRUD FASE 2: Cross-References & Aftermarket - Added manufacturers, aftermarket_parts, part_cross_references tables - 24 manufacturers, quality tier system (economy/standard/premium/oem) - Part number search across OEM and aftermarket FASE 3: Exploded Diagrams - Added diagrams, vehicle_diagrams, diagram_hotspots tables - SVG viewer with zoom controls and interactive hotspots - 3 sample diagrams (brake, oil filter, suspension) FASE 4: Search & VIN Decoder - SQLite FTS5 full-text search with auto-sync triggers - NHTSA VIN decoder API integration with 30-day cache - Unified search endpoint FASE 5: Optimization & UX - API pagination (page/per_page, max 100 items) - Dark mode with localStorage persistence - Keyboard shortcuts (/, Ctrl+K, Escape, Backspace, Ctrl+D) - Breadcrumb navigation - ARIA accessibility (labels, roles, focus management) - Skip link for keyboard users Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
575 lines
21 KiB
Python
575 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
FASE 2: Populate cross-references and aftermarket parts
|
|
This script creates FASE 2 tables and populates them with manufacturers,
|
|
aftermarket part alternatives, and cross-references.
|
|
"""
|
|
|
|
import sqlite3
|
|
import os
|
|
import random
|
|
import string
|
|
from typing import List, Dict, Tuple, Optional
|
|
|
|
# Database path configuration
|
|
DB_PATH = os.path.join(os.path.dirname(__file__), '..', 'vehicle_database.db')
|
|
SCHEMA_PATH = os.path.join(os.path.dirname(__file__), '..', 'sql', 'schema.sql')
|
|
|
|
|
|
class Fase2Manager:
|
|
"""Manager for FASE 2 tables: manufacturers, aftermarket_parts, and cross-references"""
|
|
|
|
def __init__(self, db_path: str = DB_PATH):
|
|
self.db_path = db_path
|
|
self.connection = None
|
|
|
|
def connect(self):
|
|
"""Connect to the SQLite database"""
|
|
self.connection = sqlite3.connect(self.db_path)
|
|
self.connection.row_factory = sqlite3.Row
|
|
print(f"Connected to database: {self.db_path}")
|
|
|
|
def disconnect(self):
|
|
"""Close the database connection"""
|
|
if self.connection:
|
|
self.connection.close()
|
|
print("Disconnected from database")
|
|
|
|
def create_fase2_tables(self):
|
|
"""Create FASE 2 tables from schema file"""
|
|
if not os.path.exists(SCHEMA_PATH):
|
|
raise FileNotFoundError(f"Schema file not found: {SCHEMA_PATH}")
|
|
|
|
with open(SCHEMA_PATH, 'r') as f:
|
|
schema = f.read()
|
|
|
|
if self.connection:
|
|
cursor = self.connection.cursor()
|
|
cursor.executescript(schema)
|
|
self.connection.commit()
|
|
print("FASE 2 tables created successfully")
|
|
|
|
def get_manufacturer_by_name(self, name: str) -> Optional[int]:
|
|
"""Get manufacturer ID by name, returns None if not found"""
|
|
cursor = self.connection.cursor()
|
|
cursor.execute("SELECT id FROM manufacturers WHERE name = ?", (name,))
|
|
result = cursor.fetchone()
|
|
return result[0] if result else None
|
|
|
|
def insert_manufacturer(self, name: str, type_: str, quality_tier: str,
|
|
country: str = None, logo_url: str = None,
|
|
website: str = None) -> int:
|
|
"""Insert a manufacturer if it doesn't exist, return its ID"""
|
|
existing_id = self.get_manufacturer_by_name(name)
|
|
if existing_id:
|
|
print(f" Manufacturer '{name}' already exists (ID: {existing_id})")
|
|
return existing_id
|
|
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"""INSERT INTO manufacturers (name, type, quality_tier, country, logo_url, website)
|
|
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
(name, type_, quality_tier, country, logo_url, website)
|
|
)
|
|
self.connection.commit()
|
|
manufacturer_id = cursor.lastrowid
|
|
print(f" Inserted manufacturer: {name} (ID: {manufacturer_id})")
|
|
return manufacturer_id
|
|
|
|
def get_all_parts(self) -> List[Dict]:
|
|
"""Get all parts from the parts table"""
|
|
cursor = self.connection.cursor()
|
|
cursor.execute("""
|
|
SELECT p.id, p.oem_part_number, p.name, p.name_es, p.group_id,
|
|
pg.name as group_name, pc.name as category_name
|
|
FROM parts p
|
|
LEFT JOIN part_groups pg ON p.group_id = pg.id
|
|
LEFT JOIN part_categories pc ON pg.category_id = pc.id
|
|
""")
|
|
return [dict(row) for row in cursor.fetchall()]
|
|
|
|
def get_aftermarket_part(self, oem_part_id: int, manufacturer_id: int) -> Optional[int]:
|
|
"""Check if an aftermarket part already exists"""
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"""SELECT id FROM aftermarket_parts
|
|
WHERE oem_part_id = ? AND manufacturer_id = ?""",
|
|
(oem_part_id, manufacturer_id)
|
|
)
|
|
result = cursor.fetchone()
|
|
return result[0] if result else None
|
|
|
|
def insert_aftermarket_part(self, oem_part_id: int, manufacturer_id: int,
|
|
part_number: str, name: str = None, name_es: str = None,
|
|
quality_tier: str = 'standard', price_usd: float = None,
|
|
warranty_months: int = 12, in_stock: bool = True) -> int:
|
|
"""Insert an aftermarket part if it doesn't exist"""
|
|
existing_id = self.get_aftermarket_part(oem_part_id, manufacturer_id)
|
|
if existing_id:
|
|
return existing_id
|
|
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"""INSERT INTO aftermarket_parts
|
|
(oem_part_id, manufacturer_id, part_number, name, name_es,
|
|
quality_tier, price_usd, warranty_months, in_stock)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
(oem_part_id, manufacturer_id, part_number, name, name_es,
|
|
quality_tier, price_usd, warranty_months, in_stock)
|
|
)
|
|
self.connection.commit()
|
|
return cursor.lastrowid
|
|
|
|
def get_cross_reference(self, part_id: int, cross_reference_number: str) -> Optional[int]:
|
|
"""Check if a cross-reference already exists"""
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"""SELECT id FROM part_cross_references
|
|
WHERE part_id = ? AND cross_reference_number = ?""",
|
|
(part_id, cross_reference_number)
|
|
)
|
|
result = cursor.fetchone()
|
|
return result[0] if result else None
|
|
|
|
def insert_cross_reference(self, part_id: int, cross_reference_number: str,
|
|
reference_type: str, source: str = None,
|
|
notes: str = None) -> int:
|
|
"""Insert a cross-reference if it doesn't exist"""
|
|
existing_id = self.get_cross_reference(part_id, cross_reference_number)
|
|
if existing_id:
|
|
return existing_id
|
|
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"""INSERT INTO part_cross_references
|
|
(part_id, cross_reference_number, reference_type, source, notes)
|
|
VALUES (?, ?, ?, ?, ?)""",
|
|
(part_id, cross_reference_number, reference_type, source, notes)
|
|
)
|
|
self.connection.commit()
|
|
return cursor.lastrowid
|
|
|
|
def get_manufacturers_by_tier(self, quality_tier: str) -> List[Dict]:
|
|
"""Get all manufacturers of a specific quality tier"""
|
|
cursor = self.connection.cursor()
|
|
cursor.execute(
|
|
"SELECT * FROM manufacturers WHERE quality_tier = ?",
|
|
(quality_tier,)
|
|
)
|
|
return [dict(row) for row in cursor.fetchall()]
|
|
|
|
|
|
# Manufacturer data
|
|
MANUFACTURERS_DATA = {
|
|
# OEM manufacturers
|
|
'oem': [
|
|
{'name': 'Toyota', 'country': 'Japan', 'website': 'https://www.toyota.com'},
|
|
{'name': 'Honda', 'country': 'Japan', 'website': 'https://www.honda.com'},
|
|
{'name': 'Ford', 'country': 'USA', 'website': 'https://www.ford.com'},
|
|
{'name': 'GM/ACDelco', 'country': 'USA', 'website': 'https://www.acdelco.com'},
|
|
{'name': 'Volkswagen', 'country': 'Germany', 'website': 'https://www.vw.com'},
|
|
{'name': 'Nissan', 'country': 'Japan', 'website': 'https://www.nissan.com'},
|
|
{'name': 'Hyundai/Kia', 'country': 'South Korea', 'website': 'https://www.hyundai.com'},
|
|
],
|
|
# Premium aftermarket
|
|
'premium': [
|
|
{'name': 'Bosch', 'country': 'Germany', 'website': 'https://www.bosch.com'},
|
|
{'name': 'Denso', 'country': 'Japan', 'website': 'https://www.denso.com'},
|
|
{'name': 'NGK', 'country': 'Japan', 'website': 'https://www.ngk.com'},
|
|
{'name': 'Akebono', 'country': 'Japan', 'website': 'https://www.akebono.com'},
|
|
{'name': 'Brembo', 'country': 'Italy', 'website': 'https://www.brembo.com'},
|
|
{'name': 'KYB', 'country': 'Japan', 'website': 'https://www.kyb.com'},
|
|
{'name': 'Moog', 'country': 'USA', 'website': 'https://www.moogparts.com'},
|
|
{'name': 'Continental', 'country': 'Germany', 'website': 'https://www.continental.com'},
|
|
],
|
|
# Standard aftermarket
|
|
'standard': [
|
|
{'name': 'Monroe', 'country': 'USA', 'website': 'https://www.monroe.com'},
|
|
{'name': 'Raybestos', 'country': 'USA', 'website': 'https://www.raybestos.com'},
|
|
{'name': 'Wagner', 'country': 'USA', 'website': 'https://www.wagnerbrake.com'},
|
|
{'name': 'Cardone', 'country': 'USA', 'website': 'https://www.cardone.com'},
|
|
{'name': 'Standard Motor Products', 'country': 'USA', 'website': 'https://www.smpcorp.com'},
|
|
],
|
|
# Economy aftermarket
|
|
'economy': [
|
|
{'name': 'Fram', 'country': 'USA', 'website': 'https://www.fram.com'},
|
|
{'name': 'WIX', 'country': 'USA', 'website': 'https://www.wixfilters.com'},
|
|
{'name': 'Duralast', 'country': 'USA', 'website': 'https://www.autozone.com'},
|
|
{'name': 'AutoZone Valucraft', 'country': 'USA', 'website': 'https://www.autozone.com'},
|
|
],
|
|
}
|
|
|
|
|
|
# Part number prefixes by manufacturer for realistic generation
|
|
MANUFACTURER_PREFIXES = {
|
|
'Bosch': ['0 280', '0 986', '1 457', 'F 00M'],
|
|
'Denso': ['234-', '471-', '210-', '950-'],
|
|
'NGK': ['ZFR', 'BKR', 'LFR', 'TR'],
|
|
'Akebono': ['ACT', 'ASP', 'EUR', 'PRO'],
|
|
'Brembo': ['P 85', 'P 06', 'P 23', 'P 50'],
|
|
'KYB': ['332', '334', '343', '344'],
|
|
'Moog': ['K', 'ES', 'RK', 'CK'],
|
|
'Continental': ['49', '50', '51', 'A1'],
|
|
'Monroe': ['32', '33', '34', '37'],
|
|
'Raybestos': ['FRC', 'SGD', 'ATD', 'PGD'],
|
|
'Wagner': ['QC', 'OEX', 'TQ', 'ZD'],
|
|
'Cardone': ['18-', '19-', '20-', '21-'],
|
|
'Standard Motor Products': ['FD', 'TM', 'AC', 'JH'],
|
|
'Fram': ['PH', 'CA', 'TG', 'XG'],
|
|
'WIX': ['51', '57', '46', '33'],
|
|
'Duralast': ['DL', 'BP', 'AF', 'OF'],
|
|
'AutoZone Valucraft': ['VC', 'VB', 'VA', 'VP'],
|
|
}
|
|
|
|
|
|
# Price multipliers by quality tier (relative to a base OEM price)
|
|
PRICE_MULTIPLIERS = {
|
|
'premium': (0.75, 1.10), # 75-110% of OEM price
|
|
'standard': (0.50, 0.75), # 50-75% of OEM price
|
|
'economy': (0.25, 0.50), # 25-50% of OEM price
|
|
}
|
|
|
|
|
|
# Warranty months by quality tier
|
|
WARRANTY_MONTHS = {
|
|
'premium': [24, 36, 48],
|
|
'standard': [12, 18, 24],
|
|
'economy': [6, 12],
|
|
}
|
|
|
|
|
|
def generate_part_number(manufacturer_name: str, oem_number: str) -> str:
|
|
"""Generate a realistic aftermarket part number"""
|
|
prefixes = MANUFACTURER_PREFIXES.get(manufacturer_name, ['XX'])
|
|
prefix = random.choice(prefixes)
|
|
|
|
# Extract numeric portion from OEM number or generate random
|
|
numeric_part = ''.join(filter(str.isdigit, oem_number))
|
|
if len(numeric_part) < 4:
|
|
numeric_part = ''.join(random.choices(string.digits, k=5))
|
|
else:
|
|
# Modify slightly to make it different
|
|
numeric_part = numeric_part[:4] + str(random.randint(0, 99)).zfill(2)
|
|
|
|
return f"{prefix}{numeric_part}"
|
|
|
|
|
|
def generate_base_price(part_name: str, category_name: str = None) -> float:
|
|
"""Generate a realistic base price for a part based on category"""
|
|
# Base price ranges by category/keyword
|
|
price_ranges = {
|
|
'spark plug': (5, 25),
|
|
'filter': (8, 45),
|
|
'oil filter': (5, 20),
|
|
'air filter': (12, 35),
|
|
'brake pad': (25, 80),
|
|
'brake rotor': (40, 150),
|
|
'shock': (50, 200),
|
|
'strut': (80, 250),
|
|
'sensor': (20, 120),
|
|
'alternator': (100, 350),
|
|
'starter': (80, 300),
|
|
'water pump': (30, 120),
|
|
'radiator': (100, 400),
|
|
'thermostat': (10, 40),
|
|
'belt': (15, 60),
|
|
'hose': (10, 50),
|
|
'gasket': (5, 80),
|
|
'bearing': (15, 100),
|
|
'cv joint': (40, 150),
|
|
'tie rod': (25, 80),
|
|
'ball joint': (30, 100),
|
|
'control arm': (60, 200),
|
|
'default': (20, 100),
|
|
}
|
|
|
|
# Find matching price range
|
|
part_name_lower = part_name.lower() if part_name else ''
|
|
category_lower = (category_name or '').lower()
|
|
|
|
for keyword, (min_price, max_price) in price_ranges.items():
|
|
if keyword in part_name_lower or keyword in category_lower:
|
|
return round(random.uniform(min_price, max_price), 2)
|
|
|
|
return round(random.uniform(*price_ranges['default']), 2)
|
|
|
|
|
|
def generate_cross_reference_number(oem_number: str, ref_type: str) -> str:
|
|
"""Generate a cross-reference number based on type"""
|
|
if ref_type == 'oem_alternate':
|
|
# Slight variation of OEM number
|
|
chars = list(oem_number)
|
|
if len(chars) > 2:
|
|
idx = random.randint(0, len(chars) - 1)
|
|
if chars[idx].isdigit():
|
|
chars[idx] = str((int(chars[idx]) + 1) % 10)
|
|
elif chars[idx].isalpha():
|
|
chars[idx] = random.choice(string.ascii_uppercase)
|
|
return ''.join(chars)
|
|
|
|
elif ref_type == 'supersession':
|
|
# New part number format
|
|
return f"SUP-{oem_number[-6:]}" if len(oem_number) > 6 else f"SUP-{oem_number}"
|
|
|
|
elif ref_type == 'interchange':
|
|
# Generic interchange format
|
|
numeric = ''.join(filter(str.isdigit, oem_number))
|
|
return f"INT-{numeric[:6] if len(numeric) > 6 else numeric}"
|
|
|
|
elif ref_type == 'competitor':
|
|
# Competitor format
|
|
return f"CMP-{random.choice(string.ascii_uppercase)}{random.randint(1000, 9999)}"
|
|
|
|
return oem_number
|
|
|
|
|
|
def populate_manufacturers(manager: Fase2Manager) -> Dict[str, int]:
|
|
"""Populate the manufacturers table and return a mapping of name to ID"""
|
|
print("\n=== Populating Manufacturers ===")
|
|
manufacturer_ids = {}
|
|
|
|
# Insert OEM manufacturers
|
|
print("\nOEM Manufacturers:")
|
|
for mfr in MANUFACTURERS_DATA['oem']:
|
|
mfr_id = manager.insert_manufacturer(
|
|
name=mfr['name'],
|
|
type_='oem',
|
|
quality_tier='oem',
|
|
country=mfr['country'],
|
|
website=mfr['website']
|
|
)
|
|
manufacturer_ids[mfr['name']] = mfr_id
|
|
|
|
# Insert Premium aftermarket
|
|
print("\nPremium Aftermarket Manufacturers:")
|
|
for mfr in MANUFACTURERS_DATA['premium']:
|
|
mfr_id = manager.insert_manufacturer(
|
|
name=mfr['name'],
|
|
type_='aftermarket',
|
|
quality_tier='premium',
|
|
country=mfr['country'],
|
|
website=mfr['website']
|
|
)
|
|
manufacturer_ids[mfr['name']] = mfr_id
|
|
|
|
# Insert Standard aftermarket
|
|
print("\nStandard Aftermarket Manufacturers:")
|
|
for mfr in MANUFACTURERS_DATA['standard']:
|
|
mfr_id = manager.insert_manufacturer(
|
|
name=mfr['name'],
|
|
type_='aftermarket',
|
|
quality_tier='standard',
|
|
country=mfr['country'],
|
|
website=mfr['website']
|
|
)
|
|
manufacturer_ids[mfr['name']] = mfr_id
|
|
|
|
# Insert Economy aftermarket
|
|
print("\nEconomy Aftermarket Manufacturers:")
|
|
for mfr in MANUFACTURERS_DATA['economy']:
|
|
mfr_id = manager.insert_manufacturer(
|
|
name=mfr['name'],
|
|
type_='aftermarket',
|
|
quality_tier='economy',
|
|
country=mfr['country'],
|
|
website=mfr['website']
|
|
)
|
|
manufacturer_ids[mfr['name']] = mfr_id
|
|
|
|
print(f"\nTotal manufacturers: {len(manufacturer_ids)}")
|
|
return manufacturer_ids
|
|
|
|
|
|
def populate_aftermarket_parts(manager: Fase2Manager, manufacturer_ids: Dict[str, int]):
|
|
"""Generate aftermarket parts for each OEM part in the database"""
|
|
print("\n=== Generating Aftermarket Parts ===")
|
|
|
|
parts = manager.get_all_parts()
|
|
if not parts:
|
|
print("No parts found in the database. Aftermarket parts will be generated when parts are added.")
|
|
return
|
|
|
|
total_aftermarket = 0
|
|
|
|
for part in parts:
|
|
oem_part_id = part['id']
|
|
oem_number = part['oem_part_number']
|
|
part_name = part['name']
|
|
category_name = part.get('category_name', '')
|
|
|
|
# Generate base price for this part
|
|
base_price = generate_base_price(part_name, category_name)
|
|
|
|
# Determine how many aftermarket alternatives (2-4)
|
|
num_alternatives = random.randint(2, 4)
|
|
|
|
# Select manufacturers from different tiers
|
|
tiers_to_use = ['premium', 'standard', 'economy']
|
|
random.shuffle(tiers_to_use)
|
|
|
|
alternatives_created = 0
|
|
for tier in tiers_to_use:
|
|
if alternatives_created >= num_alternatives:
|
|
break
|
|
|
|
# Get manufacturers for this tier
|
|
tier_manufacturers = [
|
|
name for name, data in
|
|
[(m['name'], m) for m in (
|
|
MANUFACTURERS_DATA.get(tier, [])
|
|
)]
|
|
]
|
|
|
|
if not tier_manufacturers:
|
|
continue
|
|
|
|
# Pick 1-2 manufacturers from this tier
|
|
selected = random.sample(
|
|
tier_manufacturers,
|
|
min(2, len(tier_manufacturers), num_alternatives - alternatives_created)
|
|
)
|
|
|
|
for mfr_name in selected:
|
|
if alternatives_created >= num_alternatives:
|
|
break
|
|
|
|
mfr_id = manufacturer_ids.get(mfr_name)
|
|
if not mfr_id:
|
|
continue
|
|
|
|
# Generate aftermarket part number
|
|
am_part_number = generate_part_number(mfr_name, oem_number)
|
|
|
|
# Calculate price based on tier
|
|
price_range = PRICE_MULTIPLIERS.get(tier, (0.5, 0.8))
|
|
price_multiplier = random.uniform(*price_range)
|
|
am_price = round(base_price * price_multiplier, 2)
|
|
|
|
# Get warranty for tier
|
|
warranty = random.choice(WARRANTY_MONTHS.get(tier, [12]))
|
|
|
|
# Determine quality tier for the part
|
|
quality_tier = tier
|
|
|
|
# Insert aftermarket part
|
|
am_id = manager.insert_aftermarket_part(
|
|
oem_part_id=oem_part_id,
|
|
manufacturer_id=mfr_id,
|
|
part_number=am_part_number,
|
|
name=f"{mfr_name} {part_name}",
|
|
name_es=part.get('name_es'),
|
|
quality_tier=quality_tier,
|
|
price_usd=am_price,
|
|
warranty_months=warranty,
|
|
in_stock=random.random() > 0.1 # 90% in stock
|
|
)
|
|
|
|
if am_id:
|
|
alternatives_created += 1
|
|
total_aftermarket += 1
|
|
|
|
print(f" Part {oem_number}: {alternatives_created} aftermarket alternatives created")
|
|
|
|
print(f"\nTotal aftermarket parts created: {total_aftermarket}")
|
|
|
|
|
|
def populate_cross_references(manager: Fase2Manager):
|
|
"""Generate cross-references for OEM parts"""
|
|
print("\n=== Generating Cross-References ===")
|
|
|
|
parts = manager.get_all_parts()
|
|
if not parts:
|
|
print("No parts found in the database. Cross-references will be generated when parts are added.")
|
|
return
|
|
|
|
total_refs = 0
|
|
reference_types = ['oem_alternate', 'supersession', 'interchange', 'competitor']
|
|
sources = ['RockAuto', 'PartsGeek', 'AutoZone', 'OReilly', 'NAPA', 'Manufacturer']
|
|
|
|
for part in parts:
|
|
part_id = part['id']
|
|
oem_number = part['oem_part_number']
|
|
|
|
# Generate 1-3 cross-references per part
|
|
num_refs = random.randint(1, 3)
|
|
used_types = random.sample(reference_types, min(num_refs, len(reference_types)))
|
|
|
|
for ref_type in used_types:
|
|
cross_ref_number = generate_cross_reference_number(oem_number, ref_type)
|
|
source = random.choice(sources)
|
|
|
|
notes = None
|
|
if ref_type == 'supersession':
|
|
notes = "New part number supersedes original"
|
|
elif ref_type == 'interchange':
|
|
notes = "Interchangeable with original"
|
|
|
|
ref_id = manager.insert_cross_reference(
|
|
part_id=part_id,
|
|
cross_reference_number=cross_ref_number,
|
|
reference_type=ref_type,
|
|
source=source,
|
|
notes=notes
|
|
)
|
|
|
|
if ref_id:
|
|
total_refs += 1
|
|
|
|
print(f" Part {oem_number}: {len(used_types)} cross-references created")
|
|
|
|
print(f"\nTotal cross-references created: {total_refs}")
|
|
|
|
|
|
def main():
|
|
"""Main entry point for FASE 2 population"""
|
|
print("=" * 60)
|
|
print("FASE 2: Cross-References and Aftermarket Parts Population")
|
|
print("=" * 60)
|
|
|
|
manager = Fase2Manager()
|
|
|
|
try:
|
|
# Connect to database
|
|
manager.connect()
|
|
|
|
# Create FASE 2 tables (idempotent)
|
|
manager.create_fase2_tables()
|
|
|
|
# Populate manufacturers
|
|
manufacturer_ids = populate_manufacturers(manager)
|
|
|
|
# Generate aftermarket parts
|
|
populate_aftermarket_parts(manager, manufacturer_ids)
|
|
|
|
# Generate cross-references
|
|
populate_cross_references(manager)
|
|
|
|
print("\n" + "=" * 60)
|
|
print("FASE 2 population completed successfully!")
|
|
print("=" * 60)
|
|
|
|
# Print summary
|
|
cursor = manager.connection.cursor()
|
|
cursor.execute("SELECT COUNT(*) FROM manufacturers")
|
|
mfr_count = cursor.fetchone()[0]
|
|
cursor.execute("SELECT COUNT(*) FROM aftermarket_parts")
|
|
am_count = cursor.fetchone()[0]
|
|
cursor.execute("SELECT COUNT(*) FROM part_cross_references")
|
|
xref_count = cursor.fetchone()[0]
|
|
|
|
print(f"\nSummary:")
|
|
print(f" Manufacturers: {mfr_count}")
|
|
print(f" Aftermarket Parts: {am_count}")
|
|
print(f" Cross-References: {xref_count}")
|
|
|
|
except Exception as e:
|
|
print(f"\nError: {e}")
|
|
raise
|
|
|
|
finally:
|
|
manager.disconnect()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|