# ----------------------------------------------------------------------------- # Immo-Ka — Agrégateur de maisons à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # mortgage/providers/desjardins.py : Desjardins — JSON server-rendered # window.dcomProducts sur la page des taux. Clé = cfPath COMPLET (le même # id existe dans les familles « taux-hypothecaires » (affichés) et # « taux-hypothecaires-promotionnels » (spéciaux) avec des valeurs # différentes — ne jamais confondre). # ----------------------------------------------------------------------------- from __future__ import annotations import json import re from .base import RateProvider PAGE_URL = "https://www.desjardins.com/fr/hypotheque/taux-hypothecaires.html" # id Desjardins -> (rate_type, terme mois, nom, purpose) ID_MAP: dict[str, tuple] = { "6000_6M": ("fixed", 6, "Fixe fermé 6 mois"), "6000_1A": ("fixed", 12, "Fixe fermé 1 an"), "6000_2A": ("fixed", 24, "Fixe fermé 2 ans"), "6000_3A": ("fixed", 36, "Fixe fermé 3 ans"), "6000_4A": ("fixed", 48, "Fixe fermé 4 ans"), "6000_5A": ("fixed", 60, "Fixe fermé 5 ans"), "6000_6A": ("fixed", 72, "Fixe fermé 6 ans"), "6000_7A": ("fixed", 84, "Fixe fermé 7 ans"), "6000_10A": ("fixed", 120, "Fixe fermé 10 ans"), "6000_6MO": ("fixed", 6, "Fixe ouvert 6 mois"), "6000_1AO": ("fixed", 12, "Fixe ouvert 1 an"), "6004_5AR": ("variable", 60, "Variable réduit 5 ans"), "tvp": ("variable", 60, "Variable protégé 5 ans"), "tvred": ("variable", 60, "Variable réduit"), "tvreg": ("variable", 60, "Variable régulier"), "tra": ("fixed", 12, "Révisable annuellement"), } PRIME_ID = "tpcad" class DesjardinsProvider(RateProvider): provider_id = "desjardins" institution = "Desjardins" source_url = PAGE_URL request_delay = 1.5 def fetch(self) -> list[dict]: return self.parse(self.get(self.source_url).text) def parse(self, payload: str) -> list[dict]: m = re.search(r"window\.dcomProducts\s*=\s*(\{.*?\});", payload, re.S) if not m: raise ValueError("dcomProducts introuvable (structure changée)") products = json.loads(m.group(1)) out: list[dict] = [] for cf_path, cell in products.items(): if "/hypotheque/" not in cf_path or not isinstance(cell, dict): continue pid = cell.get("id") or "" try: rate = float(cell.get("rate")) except (TypeError, ValueError): continue if rate <= 0: continue promo = "taux-hypothecaires-promotionnels" in cf_path if pid == PRIME_ID: out.append(self.make_product( rate=rate, rate_type="other", term_months=12, kind="posted", product_name="Taux préférentiel Desjardins", purpose="unknown", raw={"cfPath": cf_path, "rate": rate})) continue if pid not in ID_MAP: continue # id inconnu : jamais deviné rtype, term, label = ID_MAP[pid] out.append(self.make_product( rate=rate, rate_type=rtype, term_months=term, kind="special" if promo else "posted", product_name=label + (" (promotion)" if promo else ""), conditions="Taux promotionnel Desjardins" if promo else "Taux affiché Desjardins", raw={"cfPath": cf_path, "id": pid, "rate": cell.get("rate")})) return out