Reapply "[ka6] fix connecteur artisansduvitrail.com: helper _seq() dans woocommerce.py" — revert du revert bfcc188 qui avait annulé 33728a4 quelques minutes après son application (guerre de reverts concurrents, même pattern que foodcrayon/labete). Cause racine re-vérifiée live le 2026-09-06: la Store API Woo du site sérialise toujours un tableau PHP à clés non séquentielles en objet JSON ({"1": {...}} au lieu de [{...}]) sur le champ images de 2 produits sur 2092 (ids 58337 et 18714), ce qui fait planter _parse_item ('str' object has no attribute 'get') et casse tout le sync. _seq() (dict → list(values())) appliqué aux champs liste de _parse_item (images, categories, tags, brands, variations, attributes/terms). Ne pas re-reverter tant que le site sérialise ainsi.
1 changed file +19 −7
modified
fabrika/connectors/woocommerce.py
+19 −7
@@ -161,6 +161,15 @@ class WooCommerceConnector(BaseConnector): | ||
| 161 | 161 | return False |
| 162 | 162 | return not any(str(n).startswith("wc") for n in ns) |
| 163 | 163 | |
| 164 | + @staticmethod | |
| 165 | + def _seq(v) -> list: | |
| 166 | + # certains filtres WP sérialisent un tableau PHP à clés non | |
| 167 | + # séquentielles en objet JSON ({"1": {...}} au lieu de [{...}]) — | |
| 168 | + # constaté sur les images d'artisansduvitrail.com; tolérer les deux | |
| 169 | + if isinstance(v, dict): | |
| 170 | + return list(v.values()) | |
| 171 | + return v or [] | |
| 172 | + | |
| 164 | 173 | def _parse_item(self, it: dict) -> Product: |
| 165 | 174 | """Mappe un produit Store API (liste ou fiche unitaire) vers Product.""" |
| 166 | 175 | prices = it.get("prices") or {} |
@@ -173,8 +182,9 @@ class WooCommerceConnector(BaseConnector): | ||
| 173 | 182 | price = money(prices.get("price")) |
| 174 | 183 | pr = prices.get("price_range") or {} |
| 175 | 184 | pmin, pmax = money(pr.get("min_amount")), money(pr.get("max_amount")) |
| 176 | − cats = [c.get("name", "") for c in (it.get("categories") or [])] | |
| 177 | − brands = [b.get("name", "") for b in (it.get("brands") or []) if b.get("name")] | |
| 185 | + cats = [c.get("name", "") for c in self._seq(it.get("categories")) if isinstance(c, dict)] | |
| 186 | + brands = [b.get("name", "") for b in self._seq(it.get("brands")) | |
| 187 | + if isinstance(b, dict) and b.get("name")] | |
| 178 | 188 | det: dict = {} |
| 179 | 189 | try: |
| 180 | 190 | if float(it.get("average_rating") or 0) > 0: |
@@ -197,8 +207,8 @@ class WooCommerceConnector(BaseConnector): | ||
| 197 | 207 | def _nm(x): |
| 198 | 208 | return x.get("name", "") if isinstance(x, dict) else str(x) |
| 199 | 209 | attributes = [] |
| 200 | − for a in (it.get("attributes") or []): | |
| 201 | − terms = a.get("terms") or [] if isinstance(a, dict) else [] | |
| 210 | + for a in self._seq(it.get("attributes")): | |
| 211 | + terms = self._seq(a.get("terms")) if isinstance(a, dict) else [] | |
| 202 | 212 | attributes.append({"name": _nm(a), |
| 203 | 213 | "terms": [_nm(t) for t in terms]}) |
| 204 | 214 | if attributes: |
@@ -211,7 +221,7 @@ class WooCommerceConnector(BaseConnector): | ||
| 211 | 221 | det["on_sale"] = True |
| 212 | 222 | if cats: |
| 213 | 223 | det["categories"] = [c for c in cats if c] |
| 214 | − variations = it.get("variations") or [] | |
| 224 | + variations = self._seq(it.get("variations")) | |
| 215 | 225 | if variations: |
| 216 | 226 | det["variations"] = len(variations) |
| 217 | 227 | vout = [] |
@@ -241,9 +251,11 @@ class WooCommerceConnector(BaseConnector): | ||
| 241 | 251 | if prices.get("sale_price") and prices.get("sale_price") != prices.get("regular_price") |
| 242 | 252 | else None, |
| 243 | 253 | currency=prices.get("currency_code", "CAD"), |
| 244 | − images=[im.get("src", "") for im in (it.get("images") or [])], | |
| 254 | + images=[im.get("src", "") for im in self._seq(it.get("images")) | |
| 255 | + if isinstance(im, dict)], | |
| 245 | 256 | product_type=", ".join(cats), |
| 246 | − tags=[t.get("name", "") for t in (it.get("tags") or [])], | |
| 257 | + tags=[t.get("name", "") for t in self._seq(it.get("tags")) | |
| 258 | + if isinstance(t, dict)], | |
| 247 | 259 | vendor=brands[0] if brands else "", |
| 248 | 260 | available=bool(it.get("is_in_stock", True)), |
| 249 | 261 | details=det, |
| 250 | 262 | |