[ka2] fix connecteur louis14: retry (3 essais, backoff 5/10 s) de la page Livya — un 500 transitoire de app.livya.com (vu 2026-08-30 ~13:42) faisait échouer tout le sync au premier essai (médiane 19); désormais retry court puis erreur franche si le flux RSC ne livre aucune unité après 3 essais (garde contre un 200 vide qui dépublierait l'inventaire en « 0 trouvé ok »). Testé: run.py sync louis14 → 19 annonces (= médiane 19) + tests fixtures OK
1 changed file +29 −8
modified
louka/connectors/louis14.py
+29 −8
@@ -16,6 +16,7 @@ from __future__ import annotations | ||
| 16 | 16 | |
| 17 | 17 | import json |
| 18 | 18 | import re |
| 19 | +import time | |
| 19 | 20 | |
| 20 | 21 | from ..schema import Listing, normalize_unit_type |
| 21 | 22 | from .base import BaseConnector |
@@ -65,15 +66,35 @@ class Louis14Connector(BaseConnector): | ||
| 65 | 66 | |
| 66 | 67 | def fetch(self) -> list[Listing]: |
| 67 | 68 | client, project, entity, lang = self._module_params() |
| 68 | − html = self.get( | |
| 69 | − f"{LIVYA}/{lang}/{client}/projects/{project}/plans/{entity}").text | |
| 69 | + url = f"{LIVYA}/{lang}/{client}/projects/{project}/plans/{entity}" | |
| 70 | 70 | |
| 71 | − # flux RSC : les fragments sont des corps de chaînes JSON — les | |
| 72 | − # recoller puis décoder donne le JSON des unités en clair | |
| 73 | − blob = "".join(json.loads(f'"{c}"') for c in _FLIGHT_RE.findall(html)) | |
| 74 | − | |
| 75 | − buildings = self._buildings(blob) | |
| 76 | − units = self._units(blob) | |
| 71 | + # Un raté transitoire de la page Livya (500, vu 2026-08-30 ~13:42) | |
| 72 | + # faisait échouer tout le sync au premier essai : retry court | |
| 73 | + # (backoff 5/10 s), puis erreur franche si le flux RSC ne livre | |
| 74 | + # aucune unité (plutôt qu'un [] qui dépublierait l'inventaire). | |
| 75 | + units: list[dict] = [] | |
| 76 | + buildings: dict[str, str] = {} | |
| 77 | + for attempt in range(3): | |
| 78 | + try: | |
| 79 | + html = self.get(url).text | |
| 80 | + # flux RSC : les fragments sont des corps de chaînes JSON — | |
| 81 | + # les recoller puis décoder donne le JSON des unités en clair | |
| 82 | + blob = "".join( | |
| 83 | + json.loads(f'"{c}"') for c in _FLIGHT_RE.findall(html)) | |
| 84 | + buildings = self._buildings(blob) | |
| 85 | + units = self._units(blob) | |
| 86 | + except Exception: | |
| 87 | + if attempt == 2: | |
| 88 | + raise | |
| 89 | + else: | |
| 90 | + if units: | |
| 91 | + break | |
| 92 | + if attempt < 2: | |
| 93 | + time.sleep(5 * (attempt + 1)) | |
| 94 | + if not units: | |
| 95 | + raise RuntimeError( | |
| 96 | + f"aucune unité dans le flux RSC après 3 essais ({url}) — " | |
| 97 | + "format Livya changé ?") | |
| 77 | 98 | |
| 78 | 99 | listings: list[Listing] = [] |
| 79 | 100 | for u in units[: self.max_units]: |
| 80 | 101 | |