feat(inventory): Qwen vehicle compatibility — store AI unmatched vehicles as text, Celery background sync, fix brand filter fallback, increase vehicle limits
This commit is contained in:
@@ -178,18 +178,31 @@ def auto_match_vehicle_compatibility(master_conn, tenant_conn, inventory_id, par
|
||||
JOIN brands b ON b.id_brand = m.brand_id
|
||||
WHERE vp.part_id = ANY(%s)
|
||||
AND b.name_brand = %s
|
||||
LIMIT 200
|
||||
LIMIT 500
|
||||
""", (oem_ids, brand_hint))
|
||||
mye_ids = [r[0] for r in cur.fetchall()]
|
||||
|
||||
# Fallback: if brand filter yields nothing, the brand hint may be an
|
||||
# aftermarket supplier (e.g. Motorcraft, NGK, Bosch) rather than an
|
||||
# OEM vehicle brand. Search without brand filter.
|
||||
if not mye_ids:
|
||||
cur.execute("""
|
||||
SELECT DISTINCT model_year_engine_id
|
||||
FROM vehicle_parts
|
||||
WHERE part_id = ANY(%s)
|
||||
LIMIT 500
|
||||
""", (oem_ids,))
|
||||
mye_ids = [r[0] for r in cur.fetchall()]
|
||||
else:
|
||||
# No brand hint — return all MYEs for these parts
|
||||
cur.execute("""
|
||||
SELECT DISTINCT model_year_engine_id
|
||||
FROM vehicle_parts
|
||||
WHERE part_id = ANY(%s)
|
||||
LIMIT 200
|
||||
LIMIT 500
|
||||
""", (oem_ids,))
|
||||
mye_ids = [r[0] for r in cur.fetchall()]
|
||||
|
||||
mye_ids = [r[0] for r in cur.fetchall()]
|
||||
cur.close()
|
||||
|
||||
# ── Insert into tenant table ─────────────────────────────────────────
|
||||
@@ -243,6 +256,20 @@ def remove_compatibility(tenant_conn, inventory_id, model_year_engine_id):
|
||||
return deleted
|
||||
|
||||
|
||||
def remove_compatibility_by_id(tenant_conn, compat_id):
|
||||
"""Remove a compatibility by its primary key (works for both MYE-linked
|
||||
and text-only QWEN records)."""
|
||||
cur = tenant_conn.cursor()
|
||||
cur.execute("""
|
||||
DELETE FROM inventory_vehicle_compat
|
||||
WHERE id = %s
|
||||
""", (compat_id,))
|
||||
deleted = cur.rowcount
|
||||
tenant_conn.commit()
|
||||
cur.close()
|
||||
return deleted
|
||||
|
||||
|
||||
def remove_all_compatibility(tenant_conn, inventory_id):
|
||||
cur = tenant_conn.cursor()
|
||||
cur.execute("""
|
||||
@@ -259,14 +286,18 @@ def get_compatibility(tenant_conn, master_conn, inventory_id):
|
||||
|
||||
Queries inventory_vehicle_compat from the tenant DB, then resolves
|
||||
vehicle details (brand/model/year/engine) from the master DB.
|
||||
|
||||
Vehicles with model_year_engine_id IS NULL are text-only QWEN records
|
||||
(master DB lacks the vehicle) and are returned using their stored text.
|
||||
"""
|
||||
# 1. Get MYE IDs + metadata from tenant
|
||||
# 1. Get all rows from tenant
|
||||
cur_t = tenant_conn.cursor()
|
||||
cur_t.execute("""
|
||||
SELECT model_year_engine_id, source, confidence, created_at
|
||||
SELECT id, model_year_engine_id, make, model, year, engine, engine_code,
|
||||
source, confidence, created_at
|
||||
FROM inventory_vehicle_compat
|
||||
WHERE inventory_id = %s
|
||||
ORDER BY model_year_engine_id
|
||||
ORDER BY COALESCE(make, ''), COALESCE(model, ''), COALESCE(year, 0)
|
||||
""", (inventory_id,))
|
||||
rows = cur_t.fetchall()
|
||||
cur_t.close()
|
||||
@@ -274,34 +305,52 @@ def get_compatibility(tenant_conn, master_conn, inventory_id):
|
||||
if not rows:
|
||||
return []
|
||||
|
||||
mye_ids = [r[0] for r in rows]
|
||||
|
||||
# 2. Resolve vehicle details from master DB
|
||||
cur_m = master_conn.cursor()
|
||||
cur_m.execute("""
|
||||
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 m.id_model = mye.model_id
|
||||
JOIN brands b ON b.id_brand = m.brand_id
|
||||
JOIN years y ON y.id_year = mye.year_id
|
||||
JOIN engines e ON e.id_engine = mye.engine_id
|
||||
WHERE mye.id_mye = ANY(%s)
|
||||
ORDER BY b.name_brand, m.name_model, y.year_car
|
||||
""", (mye_ids,))
|
||||
details = {r[0]: r for r in cur_m.fetchall()}
|
||||
cur_m.close()
|
||||
# 2. Resolve MYE-linked vehicles from master DB
|
||||
mye_ids = [r[0] for r in rows if r[0] is not None]
|
||||
details = {}
|
||||
if mye_ids:
|
||||
cur_m = master_conn.cursor()
|
||||
cur_m.execute("""
|
||||
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 m.id_model = mye.model_id
|
||||
JOIN brands b ON b.id_brand = m.brand_id
|
||||
JOIN years y ON y.id_year = mye.year_id
|
||||
JOIN engines e ON e.id_engine = mye.engine_id
|
||||
WHERE mye.id_mye = ANY(%s)
|
||||
ORDER BY b.name_brand, m.name_model, y.year_car
|
||||
""", (mye_ids,))
|
||||
details = {r[0]: r for r in cur_m.fetchall()}
|
||||
cur_m.close()
|
||||
|
||||
# 3. Merge
|
||||
result = []
|
||||
for mye_id, source, confidence, created_at in rows:
|
||||
d = details.get(mye_id)
|
||||
if d:
|
||||
for (compat_id, mye_id, make, model, year, engine, engine_code,
|
||||
source, confidence, created_at) in rows:
|
||||
if mye_id is not None and mye_id in details:
|
||||
d = details[mye_id]
|
||||
result.append({
|
||||
'id': compat_id,
|
||||
'model_year_engine_id': mye_id,
|
||||
'brand': d[1],
|
||||
'model': d[2],
|
||||
'year': d[3],
|
||||
'engine': d[4],
|
||||
'engine_code': '',
|
||||
'source': source,
|
||||
'confidence': float(confidence),
|
||||
'created_at': str(created_at),
|
||||
})
|
||||
else:
|
||||
# Text-only QWEN record
|
||||
result.append({
|
||||
'id': compat_id,
|
||||
'model_year_engine_id': None,
|
||||
'brand': make or '',
|
||||
'model': model or '',
|
||||
'year': year,
|
||||
'engine': engine or '',
|
||||
'engine_code': engine_code or '',
|
||||
'source': source,
|
||||
'confidence': float(confidence),
|
||||
'created_at': str(created_at),
|
||||
@@ -374,6 +423,9 @@ def batch_add_compatibilities(tenant_conn, inventory_id, mye_ids, source='manual
|
||||
def save_qwen_fitment(tenant_conn, inventory_id, fitment_result):
|
||||
"""Save QWEN fitment results into inventory_vehicle_compat.
|
||||
|
||||
Supports both TecDoc-linked vehicles (mye_id present) and text-only
|
||||
QWEN vehicles (mye_id=None) when the master DB lacks the vehicle.
|
||||
|
||||
Args:
|
||||
tenant_conn: Connection to tenant DB.
|
||||
inventory_id: The inventory item ID.
|
||||
@@ -390,14 +442,30 @@ def save_qwen_fitment(tenant_conn, inventory_id, fitment_result):
|
||||
cur = tenant_conn.cursor()
|
||||
for v in vehicles:
|
||||
mye_id = v.get('mye_id')
|
||||
if not mye_id:
|
||||
continue
|
||||
cur.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, source, confidence, created_at)
|
||||
VALUES (%s, %s, 'qwen_ai', %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id) DO NOTHING
|
||||
""", (inventory_id, mye_id, fitment_result.get('confidence', 0)))
|
||||
if mye_id is not None and mye_id:
|
||||
# TecDoc-linked vehicle
|
||||
cur.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, source, confidence, created_at)
|
||||
VALUES (%s, %s, 'qwen_ai', %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id, make, model, year) DO NOTHING
|
||||
""", (inventory_id, mye_id, fitment_result.get('confidence', 0)))
|
||||
else:
|
||||
# Text-only QWEN vehicle (master DB doesn't have this vehicle)
|
||||
cur.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, make, model, year, engine, engine_code, source, confidence, created_at)
|
||||
VALUES (%s, NULL, %s, %s, %s, %s, %s, 'qwen_ai', %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id, make, model, year) DO NOTHING
|
||||
""", (
|
||||
inventory_id,
|
||||
v.get('make', '') or '',
|
||||
v.get('model', '') or '',
|
||||
v.get('year', 0) or 0,
|
||||
v.get('engine', '') or '',
|
||||
v.get('engine_code', '') or '',
|
||||
fitment_result.get('confidence', 0),
|
||||
))
|
||||
if cur.rowcount > 0:
|
||||
inserted += 1
|
||||
tenant_conn.commit()
|
||||
|
||||
Reference in New Issue
Block a user