fix(catalog): unifica modelos duplicados por variante de carroceria/generacion

- catalog_service.get_models ahora agrupa variantes (p. ej. AVEO Saloon,
  AVEO Hatchback) bajo un unico display_name y devuelve variant_ids.
- Se elige el id_model mas bajo como canonico para presentacion.
- /catalog/years y /catalog/engines aceptan model_id como lista separada
  por comas para consultar todos los MYEs de las variantes agrupadas.
- catalog.js usa variant_ids al cargar años/motores y en el selector
  desplegable (incluyendo carga desde VIN).
This commit is contained in:
2026-06-15 18:24:58 +00:00
parent 85ecf52561
commit f5711ae22f
3 changed files with 87 additions and 37 deletions

View File

@@ -285,20 +285,24 @@ def get_models(master_conn, brand_id, year_id=None, brand_name=None, mye_ids=Non
# Filter to North America models only, add clean display name, deduplicate
filtered = [r for r in rows if is_na_model(brand_name, r[1])]
# Group by (display_name, raw name) so distinct body-style variants
# (e.g. AVEO vs AVEO SALOON) remain selectable.
seen = set()
results = []
# Group by display_name so body-style/generation variants
# (e.g. AVEO Saloon, AVEO Hatchback) are shown as a single model.
groups = {}
for r in filtered:
display = _clean_model_name(r[1])
key = (display, r[1])
if key not in seen:
seen.add(key)
results.append({
'id_model': r[0],
'name_model': r[1],
'display_name': display,
})
groups.setdefault(display, []).append(r)
results = []
for display, variants in groups.items():
# Sort by raw model id ascending; first becomes the canonical id.
variants.sort(key=lambda x: x[0])
canonical = variants[0]
results.append({
'id_model': canonical[0],
'name_model': canonical[1],
'display_name': display,
'variant_ids': [v[0] for v in variants],
})
# Sort by display_name
results.sort(key=lambda x: x['display_name'])
@@ -306,34 +310,37 @@ def get_models(master_conn, brand_id, year_id=None, brand_name=None, mye_ids=Non
def get_years(master_conn, model_id, mye_ids=None):
"""Get distinct years for a model via MYE (fast, no vehicle_parts scan). Ordered DESC."""
"""Get distinct years for a model (or list of model variants) via MYE.
Ordered DESC."""
cur = master_conn.cursor()
model_ids = model_id if isinstance(model_id, (list, tuple, set)) else [model_id]
if mye_ids:
cur.execute("""
SELECT DISTINCT y.id_year, y.year_car
FROM years y
JOIN model_year_engine mye ON mye.year_id = y.id_year
WHERE mye.model_id = %s AND mye.id_mye = ANY(%s)
WHERE mye.model_id = ANY(%s) AND mye.id_mye = ANY(%s)
ORDER BY y.year_car DESC
""", (model_id, mye_ids))
""", (list(model_ids), mye_ids))
else:
cur.execute("""
SELECT DISTINCT y.id_year, y.year_car
FROM years y
JOIN model_year_engine mye ON mye.year_id = y.id_year
WHERE mye.model_id = %s
WHERE mye.model_id = ANY(%s)
ORDER BY y.year_car DESC
""", (model_id,))
""", (list(model_ids),))
rows = cur.fetchall()
cur.close()
return [{'id_year': r[0], 'year_car': r[1]} for r in rows]
def get_engines(master_conn, model_id, year_id, mye_ids=None):
"""Get MYE entries (engine + trim) for a model+year combo."""
"""Get MYE entries (engine + trim) for a model (or list of variants) + year combo."""
cur = master_conn.cursor()
model_ids = model_id if isinstance(model_id, (list, tuple, set)) else [model_id]
mye_filter = ""
params = [model_id, year_id]
params = [list(model_ids), year_id]
if mye_ids:
mye_filter = " AND mye.id_mye = ANY(%s)"
params.append(mye_ids)
@@ -341,7 +348,7 @@ def get_engines(master_conn, model_id, year_id, mye_ids=None):
SELECT mye.id_mye, e.name_engine, mye.trim_level
FROM model_year_engine mye
JOIN engines e ON e.id_engine = mye.engine_id
WHERE mye.model_id = %s AND mye.year_id = %s{mye_filter}
WHERE mye.model_id = ANY(%s) AND mye.year_id = %s{mye_filter}
ORDER BY e.name_engine, mye.trim_level
""", tuple(params))
rows = cur.fetchall()