SPB Git

spb/fabri-ka Public

Agrégateur de produits québécois — www.fabri-ka.com

HTML 57.9% Python 18.6% TypeScript 15.6% CSS 7.8%
3.5 KB · 77 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Fabri-Ka — Agrégateur de produits québécois3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/wix.py : connecteur générique Wix Stores.5#   1) GET /_api/v1/access-tokens  -> jeton d'instance public de l'app Wix Stores6#   2) GraphQL storefront getFilteredProducts (catalogue complet, paginé)7# -----------------------------------------------------------------------------8from __future__ import annotations910from ..schema import Product, parse_price11from .base import BaseConnector1213WIX_STORES_APP = "1380b703-ce81-ff05-f115-39571d94dfcd"14ALL_PRODUCTS_CATEGORY = "00000000-000000-000000-000000000001"1516GQL = """query getFilteredProducts($limit:Int!,$offset:Int!){17 catalog{category(categoryId:"%s"){numOfProducts18  productsWithMetaData(limit:$limit,offset:$offset,onlyVisible:true){19   list{id name urlPart price comparePrice formattedPrice description20        media{url} isInStock productType ribbon21        options{title selections{description}}}}}}}""" % ALL_PRODUCTS_CATEGORY222324class WixConnector(BaseConnector):25    platform = "wix"2627    def fetch(self) -> list[Product]:28        r = self.get(f"{self.base}/_api/v1/access-tokens")29        apps = r.json().get("apps", {})30        inst = (apps.get(WIX_STORES_APP) or {}).get("instance")31        if not inst:32            return []          # site Wix sans app Boutique — rien à agréger33        out: list[Product] = []34        offset, total = 0, None35        while offset < (total if total is not None else 1) and offset < 20000:36            resp = self.session.post(37                f"{self.base}/_api/wix-ecommerce-storefront-web/api",38                headers={"Authorization": inst, "Content-Type": "application/json"},39                json={"query": GQL, "variables": {"limit": 100, "offset": offset}},40                timeout=self.timeout)41            resp.raise_for_status()42            cat = (((resp.json().get("data") or {}).get("catalog") or {})43                   .get("category") or {})44            if total is None:45                total = int(cat.get("numOfProducts") or 0)46                if total == 0:47                    break48            items = ((cat.get("productsWithMetaData") or {}).get("list")) or []49            if not items:50                break51            for it in items:52                imgs = []53                for m in (it.get("media") or []):54                    u = m.get("url") or ""55                    if u and not u.startswith("http"):56                        u = "https://static.wixstatic.com/media/" + u.lstrip("/")57                    if u:58                        imgs.append(u)59                price = parse_price(it.get("price"))60                compare = parse_price(it.get("comparePrice"))61                out.append(Product(62                    store_id=self.store_id,63                    external_id=str(it.get("id", "")),64                    url=f"{self.base}/product-page/{it.get('urlPart', '')}",65                    title=it.get("name", ""),66                    description=it.get("description", "") or "",67                    price=price,68                    price_max=price,69                    compare_at_price=compare if compare and price and compare > price else None,70                    images=imgs,71                    product_type=it.get("productType", "") or "",72                    tags=[it["ribbon"]] if it.get("ribbon") else [],73                    available=bool(it.get("isInStock", True)),74                ))75            offset += 10076        return out77