SPB Git

Repository Public

Commit

[ka2] fix connecteur gestion_isr: la table listings passée derrière RLS — lecture dynamique de LISTINGS_URL (vue listings_public) + retry anti-timeout. Depuis le run du 2026-10-20 15:03 le connecteur rapportait found=0 « ok » (alerte dérive, retraits suspendus) : le portail location.gestion-isr.com a migré ses annonces publiques vers la VUE expurgée /rest/v1/listings_public (migration 145-listings-vue-publique.sql visible dans le JS inline) et l'ancienne table /rest/v1/listings renvoie désormais 200 [] à la clé anon (RLS) — zéro silencieux classique. Fix : le connecteur lit maintenant le chemin REST dans la const LISTINGS_URL du HTML du portail (même philosophie que l'URL/clé Supabase déjà lues dynamiquement — se répare seul au prochain renommage), erreur FRANCHE si la const disparaît. Piège rencontré en route : la vue est coûteuse côté Postgres — « canceling statement due to statement timeout » (57014 → 500) sur 2 runs sur 4 → retry ×3 (pause 4 s/8 s, pattern solaris 8eff094), même URL (timeout serveur, pas un cache). Schéma de la vue identique à l'ancienne table pour tous les champs consommés (pk, id_app, titre, id, loyer, adresse_geo, units, photos…) : _parse_row inchangé, external_id stables. Fixture ré-enregistrée (2 requêtes, 20 annonces), pytest gestion_isr 2 passed. Testé : fetch() venv → 20 annonces (médiane 20 ; 21 lignes dans la vue, 1 exclue par les filtres), run.py sync gestion_isr → found=20 ok (updated=1, 2,5 s), pm2 restart lou-ka-sync, site :8095 → 200.

Simon-Pierre Boucher committed · Oct 20, 2026 parent 8eff094
6 changed files +1,764 −347 Browse files
modified louka/connectors/gestion_isr.py +34 −8
@@ -3,15 +3,18 @@
3 3 # Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 4 # connectors/gestion_isr.py : connecteur Gestion ISR (gestion-isr.com)
5 5 # Le site vitrine (SPA Vite/React) renvoie vers le portail public
6 −# location.gestion-isr.com, propulsé par Supabase. On lit l'URL et la clé
7 −# anonyme publiques embarquées dans le HTML du portail, puis l'API REST
8 −# /rest/v1/listings?statut=eq.Actif livre tout le parc actif en JSON
9 −# structuré (loyer, type, ville, GPS, photos, unités par immeuble).
6 +# location.gestion-isr.com, propulsé par Supabase. On lit l'URL, la clé
7 +# anonyme ET le chemin REST des annonces (const LISTINGS_URL) embarqués dans
8 +# le HTML du portail — depuis 2026-10 c'est la vue publique expurgée
9 +# /rest/v1/listings_public?statut=eq.Actif (la table listings est passée
10 +# derrière RLS et renvoie 200 []). JSON structuré (loyer, type, ville, GPS,
11 +# photos, unités par immeuble).
10 12 # 2 requêtes par sync ; lien profond ?fiche=<id_app> par annonce.
11 13 # -----------------------------------------------------------------------------
12 14 from __future__ import annotations
13 15
14 16 import re
17 +import time
15 18
16 19 from ..schema import Listing, normalize_unit_type, strip_accents
17 20 from .base import BaseConnector
@@ -20,6 +23,9 @@ PORTAL = "https://location.gestion-isr.com/"
20 23
21 24 _URL_RE = re.compile(r"SUPABASE_URL\s*=\s*'([^']+)'")
22 25 _KEY_RE = re.compile(r"SUPABASE_ANON\s*=\s*'([^']+)'")
26 +# Chemin REST que le portail requête lui-même (vue publique) — suivi
27 +# dynamiquement pour survivre aux renommages de table/vue côté Supabase.
28 +_LIST_RE = re.compile(r"LISTINGS_URL\s*=\s*`\$\{SUPABASE_URL\}([^`]+)`")
23 29
24 30
25 31 def _pets_value(raw: str) -> str | None:
@@ -48,10 +54,30 @@ class GestionIsrConnector(BaseConnector):
48 54 raise RuntimeError("clé/URL Supabase introuvables dans le portail")
49 55 base, key = m_url.group(1).rstrip("/"), m_key.group(1)
50 56
51 − rows = self.get(
52 − f"{base}/rest/v1/listings?select=*&statut=eq.Actif",
53 − headers={"apikey": key, "Authorization": f"Bearer {key}"},
54 − ).json()
57 + m_list = _LIST_RE.search(html)
58 + if not m_list:
59 + # erreur FRANCHE plutôt qu'un found=0 silencieux (cf. 2026-10-20 :
60 + # listings → listings_public, l'ancienne table renvoyait 200 [])
61 + raise RuntimeError("const LISTINGS_URL introuvable dans le portail")
62 +
63 + # La vue listings_public est coûteuse côté Postgres : « canceling
64 + # statement due to statement timeout » (57014 → 500) observé 2 runs
65 + # sur 4 le 2026-10-20. Retry avant l'erreur franche (pattern solaris
66 + # 8eff094) ; même URL — timeout serveur, pas un problème de cache.
67 + last_err: Exception | None = None
68 + for attempt in range(3):
69 + if attempt:
70 + time.sleep(4 * attempt)
71 + try:
72 + rows = self.get(
73 + base + m_list.group(1),
74 + headers={"apikey": key, "Authorization": f"Bearer {key}"},
75 + ).json()
76 + break
77 + except Exception as err:
78 + last_err = err
79 + else:
80 + raise last_err
55 81
56 82 listings: list[Listing] = []
57 83 for row in rows:
modified tests/fixtures/gestion_isr/b04f0e8896bb41e1dffc.html +1638 −233
Large diff (2,563 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/gestion_isr/c4ec1dc08a7fc775fe37.json +21 −0
Large diff (22 lines) is not loaded by default to keep the page fast.Load diff
deleted tests/fixtures/gestion_isr/cdc93d52342c23a37e76.json +0 −21
Large diff (22 lines) is not loaded by default to keep the page fast.Load diff
modified tests/fixtures/gestion_isr/expected.json +68 −82
Large diff (243 lines) is not loaded by default to keep the page fast.Load diff
modified tests/fixtures/gestion_isr/index.json +3 −3
Large diff (16 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme