Python 67%
TypeScript 18.2%
CSS 14.4%
1# -----------------------------------------------------------------------------2# Immo-Ka — Agrégateur de maisons à vendre (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# mortgage/providers/desjardins.py : Desjardins — JSON server-rendered5# window.dcomProducts sur la page des taux. Clé = cfPath COMPLET (le même6# id existe dans les familles « taux-hypothecaires » (affichés) et7# « taux-hypothecaires-promotionnels » (spéciaux) avec des valeurs8# différentes — ne jamais confondre).9# -----------------------------------------------------------------------------10from __future__ import annotations1112import json13import re1415from .base import RateProvider1617PAGE_URL = "https://www.desjardins.com/fr/hypotheque/taux-hypothecaires.html"1819# id Desjardins -> (rate_type, terme mois, nom, purpose)20ID_MAP: dict[str, tuple] = {21 "6000_6M": ("fixed", 6, "Fixe fermé 6 mois"),22 "6000_1A": ("fixed", 12, "Fixe fermé 1 an"),23 "6000_2A": ("fixed", 24, "Fixe fermé 2 ans"),24 "6000_3A": ("fixed", 36, "Fixe fermé 3 ans"),25 "6000_4A": ("fixed", 48, "Fixe fermé 4 ans"),26 "6000_5A": ("fixed", 60, "Fixe fermé 5 ans"),27 "6000_6A": ("fixed", 72, "Fixe fermé 6 ans"),28 "6000_7A": ("fixed", 84, "Fixe fermé 7 ans"),29 "6000_10A": ("fixed", 120, "Fixe fermé 10 ans"),30 "6000_6MO": ("fixed", 6, "Fixe ouvert 6 mois"),31 "6000_1AO": ("fixed", 12, "Fixe ouvert 1 an"),32 "6004_5AR": ("variable", 60, "Variable réduit 5 ans"),33 "tvp": ("variable", 60, "Variable protégé 5 ans"),34 "tvred": ("variable", 60, "Variable réduit"),35 "tvreg": ("variable", 60, "Variable régulier"),36 "tra": ("fixed", 12, "Révisable annuellement"),37}38PRIME_ID = "tpcad"394041class DesjardinsProvider(RateProvider):42 provider_id = "desjardins"43 institution = "Desjardins"44 source_url = PAGE_URL45 request_delay = 1.54647 def fetch(self) -> list[dict]:48 return self.parse(self.get(self.source_url).text)4950 def parse(self, payload: str) -> list[dict]:51 m = re.search(r"window\.dcomProducts\s*=\s*(\{.*?\});", payload, re.S)52 if not m:53 raise ValueError("dcomProducts introuvable (structure changée)")54 products = json.loads(m.group(1))55 out: list[dict] = []56 for cf_path, cell in products.items():57 if "/hypotheque/" not in cf_path or not isinstance(cell, dict):58 continue59 pid = cell.get("id") or ""60 try:61 rate = float(cell.get("rate"))62 except (TypeError, ValueError):63 continue64 if rate <= 0:65 continue66 promo = "taux-hypothecaires-promotionnels" in cf_path67 if pid == PRIME_ID:68 out.append(self.make_product(69 rate=rate, rate_type="other", term_months=12,70 kind="posted", product_name="Taux préférentiel Desjardins",71 purpose="unknown", raw={"cfPath": cf_path, "rate": rate}))72 continue73 if pid not in ID_MAP:74 continue # id inconnu : jamais deviné75 rtype, term, label = ID_MAP[pid]76 out.append(self.make_product(77 rate=rate, rate_type=rtype, term_months=term,78 kind="special" if promo else "posted",79 product_name=label + (" (promotion)" if promo else ""),80 conditions="Taux promotionnel Desjardins" if promo81 else "Taux affiché Desjardins",82 raw={"cfPath": cf_path, "id": pid, "rate": cell.get("rate")}))83 return out84