SPB Git

spb/auto-ka Public

Python 81.9% TypeScript 12.4% CSS 5.5%
5.2 KB · 134 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/clubautozone.py : Club Auto Zone (Longueuil) — WordPress sur la5#   plateforme Convertus (thème « achilles » + plugin convertus-vms).6#7#   Stratégie : la page inventaire est une app Vue qui charge les véhicules8#   depuis l'API VMS (vms.prod.convertus.rocks) VIA le proxy PHP du site :9#     /wp-content/plugins/convertus-vms/include/php/ajax-vehicles.php10#         ?endpoint=<URL VMS encodée>&action=vms_data11#   (l'API VMS directe répond 403 ; le proxy du site est la voie officielle).12#   Réponse JSON riche : prix, km, VIN, moteur, couleurs, URL VDP… 30/page.13# -----------------------------------------------------------------------------14from __future__ import annotations1516import urllib.parse1718from ..schema import Vehicle19from .base import BaseConnector2021_VMS_API = "https://vms.prod.convertus.rocks/api/"22_PROXY = "/wp-content/plugins/convertus-vms/include/php/ajax-vehicles.php"232425def _num(value) -> float | None:26    try:27        v = float(value)28        return v if v > 0 else None29    except (TypeError, ValueError):30        return None313233class ClubAutoZone(BaseConnector):34    source_id = "clubautozone"35    base_url = "https://www.clubautozone.ca"36    dealer_name = "Club Auto Zone"37    city = "Longueuil"38    inventory_id = 2292              # globalVars.inventoryId du site (cp=…)39    request_delay: float = 1.04041    def _page(self, pg: int) -> dict:42        endpoint = (f"{_VMS_API}filtering/?cp={self.inventory_id}"43                    f"&ln=fr&sc=used&pg={pg}&pc=100")44        url = (f"{self.base_url}{_PROXY}"45               f"?endpoint={urllib.parse.quote(endpoint, safe='')}"46               f"&action=vms_data")47        return self.get(url).json()4849    def fetch(self) -> list[Vehicle]:50        vehicles: list[Vehicle] = []51        seen: set[str] = set()52        pg = 153        while pg <= 40:54            data = self._page(pg)55            results = data.get("results") or []56            new = 057            for d in results:58                veh = self._to_vehicle(d)59                if veh is not None and veh.external_id not in seen:60                    seen.add(veh.external_id)61                    vehicles.append(veh)62                    new += 163            if not results or new == 0:   # page vide ou déjà tout vu64                break65            pg += 166        return vehicles6768    def _to_vehicle(self, d: dict) -> Vehicle | None:69        ext_id = str(d.get("vehicle_id") or d.get("ad_id") or "")70        if not ext_id:71            return None72        sale_class = str(d.get("sale_class") or "")73        if sale_class and "occasion" not in sale_class.lower() \74                and "used" not in sale_class.lower():75            return None7677        year = d.get("year") if isinstance(d.get("year"), int) else None78        title = " ".join(str(x) for x in79                         (year, d.get("make"), d.get("model"),80                          d.get("search_trim") or d.get("trim")) if x)8182        price = (_num(d.get("final_price")) or _num(d.get("internet_price"))83                 or _num(d.get("asking_price")))84        km = _num(d.get("odometer"))8586        image = d.get("image") or {}87        images = []88        if isinstance(image, dict) and image.get("image_original") \89                and not d.get("placeholder_image"):90            images = [image["image_original"]]9192        try:93            doors = int(d.get("doors")) or None94        except (TypeError, ValueError):95            doors = None96        try:97            seats = int(d.get("passenger")) or None98        except (TypeError, ValueError):99            seats = None100101        details = {k: d[k] for k in ("days_on_lot", "certified", "cylinder",102                                     "engine_displacement", "vehicle_class")103                   if d.get(k) not in (None, "", 0)}104105        return Vehicle(106            source=self.source_id,107            external_id=ext_id,108            url=str(d.get("vdp_url") or f"{self.base_url}/vehicles/used/"),109            title=title,110            make=str(d.get("make") or ""),111            model=str(d.get("model") or ""),112            trim=str(d.get("search_trim") or d.get("trim") or ""),113            year=year,114            price=price,115            price_label=(f"{price:,.0f} $".replace(",", " ") if price else ""),116            mileage_km=km,117            mileage_label=(f"{km:,.0f} km".replace(",", " ") if km else ""),118            transmission=str(d.get("transmission") or ""),119            fuel=str(d.get("fuel_type") or ""),120            drivetrain=str(d.get("drive_train") or ""),121            body_type=str(d.get("body_style") or ""),122            exterior_color=str(d.get("exterior_color") or ""),123            interior_color=str(d.get("interior_color") or ""),124            engine=str(d.get("engine") or ""),125            doors=doors,126            seats=seats,127            vin=str(d.get("vin") or ""),128            stock_number=str(d.get("stock_number") or ""),129            dealer_name=self.dealer_name,130            city=self.city,131            details=details,132            images=images,133        )134