Files
Autoparts-DB/docs/METABASE_GUIDE.md
consultoria-as 7b2a904498 feat: migrate to PostgreSQL + SQLAlchemy ORM, rebrand to Nexus Autoparts
- Migrate from SQLite to PostgreSQL with normalized schema
- Add 11 lookup tables (fuel_type, body_type, drivetrain, transmission,
  materials, position_part, manufacture_type, quality_tier, countries,
  reference_type, shapes)
- Rewrite dashboard/server.py (76 routes) using SQLAlchemy text() queries
- Rewrite console/db.py (27 methods) using SQLAlchemy ORM
- Add models.py with 27 SQLAlchemy model definitions
- Add config.py for centralized DB_URL configuration
- Add migrate_to_postgres.py migration script
- Add docs/METABASE_GUIDE.md with complete data entry guide
- Rebrand from "AUTOPARTS DB" to "NEXUS AUTOPARTS"
- Fill vehicle data gaps via NHTSA API + heuristics:
  engines (cylinders, power, torque), brands (country, founded_year),
  models (body_type, production years), MYE (drivetrain, transmission, trim)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 05:24:47 +00:00

20 KiB

Guía Metabase — Nexus Autoparts

1. Conexión a la Base de Datos

Datos de conexión PostgreSQL

Campo Valor
Host localhost (o la IP del servidor: 192.168.10.198)
Puerto 5432
Base de datos nexus_autoparts
Usuario nexus
Contraseña nexus_autoparts_2026
SSL No requerido (conexión local)

Pasos en Metabase

  1. Ir a AdminDatabasesAdd database
  2. Seleccionar PostgreSQL
  3. Llenar los campos con los datos anteriores
  4. Click Save
  5. Metabase sincronizará las tablas automáticamente (~30 segundos)

Tip: Si Metabase está en otro servidor, usar la IP 192.168.10.198 en lugar de localhost.


2. Estructura de la Base de Datos

Diagrama de relaciones simplificado

brands ──→ models ──→ model_year_engine (MYE) ←── years
                            ↑    ↑                    ↑
                         engines  │               vehicle_parts ──→ parts
                                  │                                   ↑
                            vehicle_diagrams              part_cross_references
                                  ↓                       aftermarket_parts
                              diagrams                    diagram_hotspots

Tablas principales (con datos)

Tabla Registros Descripción
brands 102 Marcas de vehículos
models 4,031 Modelos por marca
years 80 Años (1946-2026)
engines 13,430 Motores con specs
model_year_engine 47,858 Combinación marca-modelo-año-motor

Tablas de piezas (vacías — para llenar)

Tabla Descripción
parts Piezas OEM (número de parte, nombre, grupo)
vehicle_parts Relación pieza ↔ vehículo (fitments)
aftermarket_parts Piezas aftermarket por fabricante
part_cross_references Intercambios y referencias cruzadas
manufacturers 47 fabricantes ya cargados

Tablas lookup (catálogos de referencia)

Tabla Valores actuales
part_categories 12: Body, Brake, Cooling, Drivetrain, Electrical, Engine, Exhaust, Fuel, HVAC, Steering, Suspension, Transmission
part_groups 63 grupos dentro de las categorías
quality_tier economy, standard, oem, premium
reference_type competitor, interchange, oem_alternate, supersession
position_part front, rear
manufacture_type aftermarket, oem
materials (vacía — agregar según se necesite)

3. Alta de Piezas OEM

Orden obligatorio de carga

1. materials        (si aplica)
2. parts            ← PRIMERO las piezas
3. vehicle_parts    ← DESPUÉS los fitments
4. aftermarket_parts
5. part_cross_references

IMPORTANTE: Respetar este orden. vehicle_parts y aftermarket_parts requieren que la pieza ya exista en parts.


3.1 Agregar una pieza OEM (parts)

En Metabase: click + NewSQL query → ejecutar:

INSERT INTO parts (oem_part_number, name_part, name_es, group_id, description, description_es, weight_kg, id_material)
VALUES (
    '04465-06090',                    -- Número OEM (obligatorio, único por grupo)
    'Front Brake Pad Set',            -- Nombre en inglés (obligatorio)
    'Juego de Balatas Delanteras',    -- Nombre en español (opcional)
    16,                               -- group_id: 16 = Brake Pads (ver tabla abajo)
    'Ceramic brake pad set for front axle',  -- Descripción EN (opcional)
    'Juego de balatas cerámicas para eje delantero',  -- Descripción ES (opcional)
    1.2,                              -- Peso en kg (opcional)
    NULL                              -- id_material (opcional, ver tabla materials)
);

Carga masiva de piezas

INSERT INTO parts (oem_part_number, name_part, name_es, group_id, description) VALUES
    ('04465-06090', 'Front Brake Pad Set', 'Juego Balatas Delanteras', 16, 'Ceramic front pads'),
    ('04465-33471', 'Front Brake Pad Set', 'Juego Balatas Delanteras', 16, 'Semi-metallic front pads'),
    ('43512-06150', 'Front Brake Rotor', 'Disco de Freno Delantero', 17, 'Vented front rotor 296mm'),
    ('19101-28491', 'Radiator', 'Radiador', 31, 'Aluminum core radiator'),
    ('16400-28531', 'Cooling Fan Assembly', 'Ensamble Ventilador', 35, 'Electric cooling fan')
ON CONFLICT DO NOTHING;

Referencia de group_id (grupos de piezas)

group_id Grupo Categoría
Frenos y Ruedas
16 Brake Pads Brake & Wheel Hub
17 Brake Rotors Brake & Wheel Hub
20 Brake Calipers Brake & Wheel Hub
27 Wheel Bearings Brake & Wheel Hub
28 Wheel Hubs Brake & Wheel Hub
Motor
70 Oil Filters Engine
71 Air Filters Engine
72 Spark Plugs Engine
73 Belts Engine
76 Timing Components Engine
91 Engine Mounts Engine
Enfriamiento
31 Radiators Cooling System
33 Water Pumps Cooling System
34 Thermostats Cooling System
35 Cooling Fans Cooling System
Eléctrico
55 Alternators Electrical & Lighting
56 Starters Electrical & Lighting
57 Ignition Coils Electrical & Lighting
65 Sensors Electrical & Lighting
Combustible
110 Fuel Pumps Fuel & Air
111 Fuel Filters Fuel & Air
112 Fuel Injectors Fuel & Air
Escape
99 Catalytic Converters Exhaust
100 Mufflers Exhaust
108 Headers Exhaust
Dirección
141 Power Steering Pumps Steering
144 Steering Racks Steering
145 Steering Gearboxes Steering
146 Tie Rods Steering
147 Tie Rod Ends Steering
148 Inner Tie Rods Steering
151 Pitman Arms Steering
152 Idler Arms Steering
153 Center Links Steering
154 Drag Links Steering
155 Steering Knuckles Steering
191 Steering Dampers Steering
Suspensión
156 Shocks Suspension
157 Struts Suspension
158 Strut Mounts Suspension
159 Coil Springs Suspension
160 Leaf Springs Suspension
161 Control Arms Suspension
164 Ball Joints Suspension
165 Bushings Suspension
167 Sway Bar Links Suspension
168 Sway Bar Bushings Suspension
169 Torsion Bars Suspension
170 Trailing Arms Suspension
Transmisión
175 Transmission Filters Transmission
185 Transmission Mounts Transmission
A/C y Calefacción
127 AC Compressors Heat & Air Conditioning
135 Blower Motors Heat & Air Conditioning
138 Cabin Air Filters Heat & Air Conditioning
Tren Motriz
44 CV Axles Drivetrain
45 CV Joints Drivetrain
50 Axle Shafts Drivetrain
Carrocería
15 Moldings & Trim Body & Lamp Assembly

4. Alta de Fitments (Pieza ↔ Vehículo)

Un fitment vincula una pieza con un vehículo específico (combinación modelo-año-motor).

4.1 Encontrar el id_mye del vehículo

-- Buscar el id_mye para un vehículo específico
SELECT mye.id_mye, b.name_brand, m.name_model, y.year_car, e.name_engine
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
JOIN engines e ON mye.engine_id = e.id_engine
WHERE b.name_brand ILIKE 'Toyota'
  AND m.name_model ILIKE 'Camry'
  AND y.year_car = 2020
ORDER BY e.name_engine;

4.2 Encontrar el id_part de la pieza

-- Buscar pieza por número OEM
SELECT id_part, oem_part_number, name_part FROM parts
WHERE oem_part_number = '04465-06090';

4.3 Crear el fitment (vehicle_parts)

INSERT INTO vehicle_parts (model_year_engine_id, part_id, quantity_required, id_position_part, fitment_notes)
VALUES (
    12345,     -- id_mye del vehículo (del paso 4.1)
    67890,     -- id_part de la pieza (del paso 4.2)
    1,         -- Cantidad requerida (1 juego)
    1,         -- Posición: 1 = front, 2 = rear (ver position_part)
    'Fits all trim levels'  -- Notas de compatibilidad (opcional)
);

4.4 Fitment masivo (misma pieza en varios vehículos)

-- Ejemplo: Balata 04465-06090 compatible con todos los Camry 2018-2023
INSERT INTO vehicle_parts (model_year_engine_id, part_id, quantity_required, id_position_part)
SELECT mye.id_mye,
       (SELECT id_part FROM parts WHERE oem_part_number = '04465-06090'),
       1,    -- cantidad
       1     -- posición: front
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
WHERE b.name_brand = 'TOYOTA'
  AND m.name_model = 'Camry'
  AND y.year_car BETWEEN 2018 AND 2023
ON CONFLICT DO NOTHING;

Valores de position_part

id_position_part Nombre
1 front
2 rear

Para agregar más posiciones (left, right, upper, lower):

INSERT INTO position_part (name_position_part) VALUES ('left'), ('right'), ('upper'), ('lower');

5. Alta de Piezas Aftermarket

Vincula una pieza aftermarket con su equivalente OEM y el fabricante.

5.1 Crear pieza aftermarket (aftermarket_parts)

INSERT INTO aftermarket_parts (
    oem_part_id, manufacturer_id, part_number,
    name_aftermarket_parts, name_es,
    id_quality_tier, price_usd, warranty_months
) VALUES (
    67890,           -- id_part de la pieza OEM equivalente (de tabla parts)
    3,               -- id_manufacture del fabricante (ver manufacturers)
    'CXD1293',       -- Número de parte aftermarket
    'Ceramic Brake Pad Set',         -- Nombre EN
    'Juego Balatas Cerámicas',       -- Nombre ES
    3,               -- Calidad: 1=economy, 2=oem, 3=premium, 4=standard
    45.99,           -- Precio USD
    24               -- Garantía en meses
);

5.2 Carga masiva aftermarket

INSERT INTO aftermarket_parts (oem_part_id, manufacturer_id, part_number, name_aftermarket_parts, id_quality_tier, price_usd, warranty_months) VALUES
    (67890, 3, 'CXD1293', 'Premium Ceramic Brake Pad', 3, 45.99, 24),
    (67890, 5, 'MKD1293', 'Economy Semi-Metallic Brake Pad', 1, 22.50, 12),
    (67890, 8, 'PBR1293', 'OEM Replacement Brake Pad', 2, 38.00, 18)
ON CONFLICT DO NOTHING;

Referencia de quality_tier

id_quality_tier Nombre Descripción
1 economy Económica, calidad básica
2 oem Calidad equivalente al original
3 premium Calidad superior al original
4 standard Calidad estándar del mercado

Consultar fabricantes existentes

SELECT m.id_manufacture, m.name_manufacture, mt.name_type_manu AS tipo,
       qt.name_quality AS calidad, c.name_country AS pais
FROM manufacturers m
LEFT JOIN manufacture_type mt ON m.id_type_manu = mt.id_type_manu
LEFT JOIN quality_tier qt ON m.id_quality_tier = qt.id_quality_tier
LEFT JOIN countries c ON m.id_country = c.id_country
ORDER BY m.name_manufacture;

Agregar un nuevo fabricante

INSERT INTO manufacturers (name_manufacture, id_type_manu, id_quality_tier, id_country, website)
VALUES (
    'BREMBO',
    1,      -- 1=aftermarket, 2=oem
    3,      -- 1=economy, 2=oem, 3=premium, 4=standard
    NULL,   -- id_country (buscar en tabla countries o agregar)
    'https://www.brembo.com'
);

6. Alta de Referencias Cruzadas / Intercambios

Las cross-references vinculan una pieza OEM con números de parte alternativos.

6.1 Crear referencia cruzada (part_cross_references)

INSERT INTO part_cross_references (part_id, cross_reference_number, id_ref_type, source_ref, notes)
VALUES (
    67890,              -- id_part de la pieza OEM
    'D1293',            -- Número de referencia cruzada
    2,                  -- Tipo: 2 = interchange (ver tabla abajo)
    'Wagner Catalog',   -- Fuente de la referencia (opcional)
    'Direct replacement'  -- Notas (opcional)
);

6.2 Carga masiva de cross-references

INSERT INTO part_cross_references (part_id, cross_reference_number, id_ref_type, source_ref) VALUES
    (67890, 'D1293', 2, 'Wagner'),         -- interchange
    (67890, '04465-06100', 3, 'Toyota'),   -- oem_alternate (número OEM alterno)
    (67890, 'BC1293', 1, 'Akebono'),       -- competitor
    (67890, '04465-06080', 4, 'Toyota')    -- supersession (reemplaza a esta)
ON CONFLICT DO NOTHING;

Valores de reference_type

id_ref_type Nombre Uso
1 competitor Número equivalente de competidor
2 interchange Intercambio directo compatible
3 oem_alternate Número OEM alterno del mismo fabricante
4 supersession El número OEM que esta pieza reemplaza

7. Alta de Materiales

Si necesitas especificar el material de una pieza:

-- Agregar materiales
INSERT INTO materials (name_material) VALUES
    ('Steel'),
    ('Aluminum'),
    ('Ceramic'),
    ('Rubber'),
    ('Cast Iron'),
    ('Stainless Steel'),
    ('Copper'),
    ('Plastic')
ON CONFLICT (name_material) DO NOTHING;

-- Luego, al crear una pieza, usar el id_material correspondiente:
-- SELECT id_material FROM materials WHERE name_material = 'Ceramic';

8. Queries Útiles para Metabase (Dashboards)

Vista completa de una pieza con todos sus datos

SELECT
    p.oem_part_number AS "Número OEM",
    p.name_part AS "Nombre EN",
    p.name_es AS "Nombre ES",
    pg.name_part_group AS "Grupo",
    pc.name_part_category AS "Categoría",
    p.weight_kg AS "Peso (kg)",
    mat.name_material AS "Material",
    COUNT(DISTINCT vp.model_year_engine_id) AS "Vehículos compatibles",
    COUNT(DISTINCT ap.id_aftermarket_parts) AS "Opciones aftermarket",
    COUNT(DISTINCT pcr.id_part_cross_ref) AS "Cross-references"
FROM parts p
JOIN part_groups pg ON p.group_id = pg.id_part_group
JOIN part_categories pc ON pg.category_id = pc.id_part_category
LEFT JOIN materials mat ON p.id_material = mat.id_material
LEFT JOIN vehicle_parts vp ON vp.part_id = p.id_part
LEFT JOIN aftermarket_parts ap ON ap.oem_part_id = p.id_part
LEFT JOIN part_cross_references pcr ON pcr.part_id = p.id_part
GROUP BY p.id_part, p.oem_part_number, p.name_part, p.name_es,
         pg.name_part_group, pc.name_part_category, p.weight_kg, mat.name_material
ORDER BY pc.name_part_category, pg.name_part_group, p.name_part;

Catálogo de piezas por vehículo

SELECT
    b.name_brand AS "Marca",
    m.name_model AS "Modelo",
    y.year_car AS "Año",
    e.name_engine AS "Motor",
    pc.name_part_category AS "Categoría",
    pg.name_part_group AS "Grupo",
    p.oem_part_number AS "# OEM",
    p.name_part AS "Pieza",
    vp.quantity_required AS "Cantidad",
    pp.name_position_part AS "Posición"
FROM vehicle_parts vp
JOIN model_year_engine mye ON vp.model_year_engine_id = mye.id_mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
JOIN engines e ON mye.engine_id = e.id_engine
JOIN parts p ON vp.part_id = p.id_part
JOIN part_groups pg ON p.group_id = pg.id_part_group
JOIN part_categories pc ON pg.category_id = pc.id_part_category
LEFT JOIN position_part pp ON vp.id_position_part = pp.id_position_part
WHERE b.name_brand ILIKE '{{marca}}'
  AND m.name_model ILIKE '{{modelo}}'
ORDER BY pc.display_order, pg.display_order, p.name_part;

En Metabase, {{marca}} y {{modelo}} se convierten en filtros interactivos.

Piezas aftermarket con precios

SELECT
    p.oem_part_number AS "OEM #",
    p.name_part AS "Pieza OEM",
    mfr.name_manufacture AS "Fabricante",
    ap.part_number AS "# Aftermarket",
    ap.name_aftermarket_parts AS "Nombre",
    qt.name_quality AS "Calidad",
    ap.price_usd AS "Precio USD",
    ap.warranty_months AS "Garantía (meses)"
FROM aftermarket_parts ap
JOIN parts p ON ap.oem_part_id = p.id_part
JOIN manufacturers mfr ON ap.manufacturer_id = mfr.id_manufacture
LEFT JOIN quality_tier qt ON ap.id_quality_tier = qt.id_quality_tier
ORDER BY p.oem_part_number, qt.name_quality DESC, ap.price_usd;

Cross-references de una pieza

SELECT
    p.oem_part_number AS "OEM #",
    p.name_part AS "Pieza",
    pcr.cross_reference_number AS "# Referencia",
    rt.name_ref_type AS "Tipo",
    pcr.source_ref AS "Fuente",
    pcr.notes AS "Notas"
FROM part_cross_references pcr
JOIN parts p ON pcr.part_id = p.id_part
LEFT JOIN reference_type rt ON pcr.id_ref_type = rt.id_ref_type
WHERE p.oem_part_number = '{{numero_oem}}'
ORDER BY rt.name_ref_type, pcr.cross_reference_number;

Estadísticas generales

SELECT
    (SELECT COUNT(*) FROM parts) AS "Total piezas",
    (SELECT COUNT(*) FROM vehicle_parts) AS "Total fitments",
    (SELECT COUNT(*) FROM aftermarket_parts) AS "Total aftermarket",
    (SELECT COUNT(*) FROM part_cross_references) AS "Total cross-refs",
    (SELECT COUNT(*) FROM manufacturers) AS "Total fabricantes",
    (SELECT COUNT(DISTINCT model_year_engine_id) FROM vehicle_parts) AS "Vehículos con piezas";

9. Flujo Completo: Ejemplo Paso a Paso

Dar de alta "Balata delantera Toyota Camry 2020"

Paso 1: Agregar la pieza OEM

INSERT INTO parts (oem_part_number, name_part, name_es, group_id)
VALUES ('04465-06090', 'Front Brake Pad Set', 'Juego Balatas Delanteras', 16)
RETURNING id_part;
-- Resultado: id_part = 1 (anotar este ID)

Paso 2: Buscar los vehículos compatibles

SELECT mye.id_mye, y.year_car, e.name_engine
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
JOIN engines e ON mye.engine_id = e.id_engine
WHERE b.name_brand = 'TOYOTA' AND m.name_model = 'Camry'
  AND y.year_car BETWEEN 2018 AND 2023;
-- Resultado: lista de id_mye para cada configuración

Paso 3: Crear los fitments

INSERT INTO vehicle_parts (model_year_engine_id, part_id, quantity_required, id_position_part)
SELECT mye.id_mye, 1, 1, 1  -- id_part=1, qty=1, position=front
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
WHERE b.name_brand = 'TOYOTA' AND m.name_model = 'Camry'
  AND y.year_car BETWEEN 2018 AND 2023
ON CONFLICT DO NOTHING;

Paso 4: Agregar opciones aftermarket

INSERT INTO aftermarket_parts (oem_part_id, manufacturer_id, part_number, name_aftermarket_parts, id_quality_tier, price_usd, warranty_months) VALUES
    (1, 3, 'CXD1293', 'Premium Ceramic Pad', 3, 45.99, 24),
    (1, 5, 'MKD1293', 'Economy Brake Pad', 1, 22.50, 12);

Paso 5: Agregar cross-references

INSERT INTO part_cross_references (part_id, cross_reference_number, id_ref_type, source_ref) VALUES
    (1, 'D1293', 2, 'Wagner'),
    (1, 'BC1293', 1, 'Akebono'),
    (1, '04465-06100', 3, 'Toyota OEM Alternate');

10. Notas Importantes

Restricciones únicas (evitan duplicados)

  • parts: No tiene constraint único en oem_part_number (puede haber mismo OEM en diferentes grupos)
  • vehicle_parts: Único por (model_year_engine_id, part_id, id_position_part)
  • vehicle_diagrams: Único por (diagram_id, model_year_engine_id)
  • model_year_engine: Único por (model_id, year_id, engine_id, trim_level)

Búsqueda full-text

La tabla parts tiene un trigger que auto-genera search_vector al insertar/actualizar. Esto permite búsquedas rápidas en español:

SELECT * FROM parts
WHERE search_vector @@ plainto_tsquery('spanish', 'balata delantera');

Para agregar nuevas categorías o grupos

-- Nueva categoría
INSERT INTO part_categories (name_part_category, name_es, display_order)
VALUES ('Interior', 'Interior', 13);

-- Nuevo grupo dentro de una categoría
INSERT INTO part_groups (category_id, name_part_group, name_es, display_order)
VALUES (1, 'Headlights', 'Faros Delanteros', 10);
-- (category_id 1 = Body & Lamp Assembly)

Para agregar un país (para fabricantes)

INSERT INTO countries (name_country) VALUES ('Italy')
ON CONFLICT (name_country) DO NOTHING;