# ----------------------------------------------------------------------------- # Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/clubautozone.py : Club Auto Zone (Longueuil) — WordPress sur la # plateforme Convertus (thème « achilles » + plugin convertus-vms). # # Stratégie : la page inventaire est une app Vue qui charge les véhicules # depuis l'API VMS (vms.prod.convertus.rocks) VIA le proxy PHP du site : # /wp-content/plugins/convertus-vms/include/php/ajax-vehicles.php # ?endpoint=&action=vms_data # (l'API VMS directe répond 403 ; le proxy du site est la voie officielle). # Réponse JSON riche : prix, km, VIN, moteur, couleurs, URL VDP… 30/page. # ----------------------------------------------------------------------------- from __future__ import annotations import urllib.parse from ..schema import Vehicle from .base import BaseConnector _VMS_API = "https://vms.prod.convertus.rocks/api/" _PROXY = "/wp-content/plugins/convertus-vms/include/php/ajax-vehicles.php" def _num(value) -> float | None: try: v = float(value) return v if v > 0 else None except (TypeError, ValueError): return None class ClubAutoZone(BaseConnector): source_id = "clubautozone" base_url = "https://www.clubautozone.ca" dealer_name = "Club Auto Zone" city = "Longueuil" inventory_id = 2292 # globalVars.inventoryId du site (cp=…) request_delay: float = 1.0 def _page(self, pg: int) -> dict: endpoint = (f"{_VMS_API}filtering/?cp={self.inventory_id}" f"&ln=fr&sc=used&pg={pg}&pc=100") url = (f"{self.base_url}{_PROXY}" f"?endpoint={urllib.parse.quote(endpoint, safe='')}" f"&action=vms_data") return self.get(url).json() def fetch(self) -> list[Vehicle]: vehicles: list[Vehicle] = [] seen: set[str] = set() pg = 1 while pg <= 40: data = self._page(pg) results = data.get("results") or [] new = 0 for d in results: veh = self._to_vehicle(d) if veh is not None and veh.external_id not in seen: seen.add(veh.external_id) vehicles.append(veh) new += 1 if not results or new == 0: # page vide ou déjà tout vu break pg += 1 return vehicles def _to_vehicle(self, d: dict) -> Vehicle | None: ext_id = str(d.get("vehicle_id") or d.get("ad_id") or "") if not ext_id: return None sale_class = str(d.get("sale_class") or "") if sale_class and "occasion" not in sale_class.lower() \ and "used" not in sale_class.lower(): return None year = d.get("year") if isinstance(d.get("year"), int) else None title = " ".join(str(x) for x in (year, d.get("make"), d.get("model"), d.get("search_trim") or d.get("trim")) if x) price = (_num(d.get("final_price")) or _num(d.get("internet_price")) or _num(d.get("asking_price"))) km = _num(d.get("odometer")) image = d.get("image") or {} images = [] if isinstance(image, dict) and image.get("image_original") \ and not d.get("placeholder_image"): images = [image["image_original"]] try: doors = int(d.get("doors")) or None except (TypeError, ValueError): doors = None try: seats = int(d.get("passenger")) or None except (TypeError, ValueError): seats = None details = {k: d[k] for k in ("days_on_lot", "certified", "cylinder", "engine_displacement", "vehicle_class") if d.get(k) not in (None, "", 0)} return Vehicle( source=self.source_id, external_id=ext_id, url=str(d.get("vdp_url") or f"{self.base_url}/vehicles/used/"), title=title, make=str(d.get("make") or ""), model=str(d.get("model") or ""), trim=str(d.get("search_trim") or d.get("trim") or ""), year=year, price=price, price_label=(f"{price:,.0f} $".replace(",", " ") if price else ""), mileage_km=km, mileage_label=(f"{km:,.0f} km".replace(",", " ") if km else ""), transmission=str(d.get("transmission") or ""), fuel=str(d.get("fuel_type") or ""), drivetrain=str(d.get("drive_train") or ""), body_type=str(d.get("body_style") or ""), exterior_color=str(d.get("exterior_color") or ""), interior_color=str(d.get("interior_color") or ""), engine=str(d.get("engine") or ""), doors=doors, seats=seats, vin=str(d.get("vin") or ""), stock_number=str(d.get("stock_number") or ""), dealer_name=self.dealer_name, city=self.city, details=details, images=images, )