fix encar id mapping and docker build stability
This commit is contained in:
@@ -42,6 +42,7 @@ BATCH_VEHICLES_CHUNK_SIZE = 20
|
||||
ENCAR_DETAIL_URL_TEMPLATE = "https://www.encar.com/dc/dc_cardetailview.do?carid={vehicle_id}"
|
||||
ENCAR_IMAGE_BASE = "https://ci.encar.com"
|
||||
VEHICLE_ID_RE = re.compile(r"(?:carid|vehicleId)=?(\d+)")
|
||||
NUMERIC_ID_RE = re.compile(r"(\d{6,})")
|
||||
PHOTO_VEHICLE_ID_RE = re.compile(r"/(\d+)_\d+\.(?:jpg|jpeg|png|webp)$", re.IGNORECASE)
|
||||
|
||||
# Максимальный номер фото для пробинга и допустимые промахи подряд
|
||||
@@ -155,30 +156,24 @@ class RuntimeFilterSpec:
|
||||
@dataclass(slots=True)
|
||||
class EncarMapper:
|
||||
def map_to_car_record(self, vehicle_url: str, payload: dict[str, Any], probe_all_photos: bool = False) -> CarRecord:
|
||||
vehicle_id = self._extract_vehicle_id(vehicle_url) or str(
|
||||
payload.get("Id") or payload.get("vehicleId") or ""
|
||||
)
|
||||
vehicle_id = self._resolve_vehicle_id(vehicle_url, payload)
|
||||
origin_id = f"encar:{vehicle_id}"
|
||||
parser_id = f"encar:{vehicle_id}"
|
||||
origin_url = vehicle_url or ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id=vehicle_id)
|
||||
url_vehicle_id = self._extract_vehicle_id(vehicle_url)
|
||||
if vehicle_url and url_vehicle_id == vehicle_id:
|
||||
origin_url = vehicle_url
|
||||
else:
|
||||
origin_url = ENCAR_DETAIL_URL_TEMPLATE.format(vehicle_id=vehicle_id)
|
||||
|
||||
brand = self._as_str(payload.get("Manufacturer") or payload.get("Brand") or "UNKNOWN")
|
||||
# Перевод бренда на русский
|
||||
brand = BRAND_TRANSLATIONS.get(brand, brand)
|
||||
|
||||
# Model + Badge + BadgeDetail = полное название
|
||||
# Нужна только базовая модель (без trim/sub-trim)
|
||||
model_raw = self._as_str(payload.get("Model") or "UNKNOWN")
|
||||
badge = self._as_str(payload.get("Badge") or "")
|
||||
badge_detail = self._as_str(payload.get("BadgeDetail") or "")
|
||||
|
||||
# Полное описание модели: "Model / Badge BadgeDetail"
|
||||
model_parts = [model_raw]
|
||||
if badge:
|
||||
model_parts.append(badge)
|
||||
if badge_detail and badge_detail not in badge:
|
||||
model_parts.append(badge_detail)
|
||||
model = " / ".join(model_parts) if len(model_parts) > 1 else model_parts[0]
|
||||
model = self._translate_text(model)
|
||||
model = self._translate_text(model_raw)
|
||||
|
||||
year = self._to_year(payload.get("FormYear") or payload.get("Year"))
|
||||
# Price в 만원 (10 000 KRW) → переводим в KRW
|
||||
@@ -283,6 +278,66 @@ class EncarMapper:
|
||||
match = VEHICLE_ID_RE.search(vehicle_url)
|
||||
return match.group(1) if match else None
|
||||
|
||||
def _resolve_vehicle_id(self, vehicle_url: str | None, payload: dict[str, Any]) -> str:
|
||||
# 1) Из URL (carid=123...)
|
||||
from_url = self._extract_vehicle_id(vehicle_url)
|
||||
if from_url:
|
||||
return from_url
|
||||
|
||||
# 2) Из явных полей payload
|
||||
for key in ("Id", "id", "vehicleId", "VehicleId", "carId", "carid"):
|
||||
value = payload.get(key)
|
||||
extracted = self._extract_numeric_id(value)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
# 3) Из фото-пути (например /.../41854374_001.jpg)
|
||||
photos = payload.get("Photos") or []
|
||||
if isinstance(photos, list):
|
||||
for raw in photos:
|
||||
if not isinstance(raw, dict):
|
||||
continue
|
||||
location = raw.get("location") or raw.get("Location") or ""
|
||||
extracted = self._extract_numeric_id(location)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
# 4) Фолбэк: любой id-like ключ в payload
|
||||
fallback = self._find_any_numeric_id(payload)
|
||||
if fallback:
|
||||
return fallback
|
||||
|
||||
raise ValueError("Cannot resolve numeric Encar vehicle id")
|
||||
|
||||
def _extract_numeric_id(self, value: Any) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, int):
|
||||
return str(value)
|
||||
text = self._as_str(value)
|
||||
if not text:
|
||||
return None
|
||||
match = NUMERIC_ID_RE.search(text)
|
||||
return match.group(1) if match else None
|
||||
|
||||
def _find_any_numeric_id(self, payload: dict[str, Any]) -> str | None:
|
||||
stack: list[Any] = [payload]
|
||||
while stack:
|
||||
current = stack.pop()
|
||||
if isinstance(current, dict):
|
||||
for key, value in current.items():
|
||||
if isinstance(key, str) and "id" in key.lower():
|
||||
extracted = self._extract_numeric_id(value)
|
||||
if extracted:
|
||||
return extracted
|
||||
if isinstance(value, (dict, list)):
|
||||
stack.append(value)
|
||||
elif isinstance(current, list):
|
||||
for item in current:
|
||||
if isinstance(item, (dict, list)):
|
||||
stack.append(item)
|
||||
return None
|
||||
|
||||
def _build_slug(self, brand: str, model: str, vehicle_id: str) -> str:
|
||||
raw = re.sub(r"[^a-z0-9]+", "-", f"{brand}-{model}-{vehicle_id}".lower()).strip("-")
|
||||
return raw or f"encar-{vehicle_id}"
|
||||
|
||||
Reference in New Issue
Block a user