SPB Git

spb/house-ka

Public

House-Ka v1 — English homes-for-sale aggregator for Canada outside Québec (fork of Immo-Ka)

- Backend: RealtyPress/CREA DDF connectors only (15 ON sources), Canada-wide
  bbox, English canonical property types, relaxed publication rule
  (price+city), English SEO (/property, /for-sale, /type), QC-only modules
  removed (Hydro-Québec, BDZI, RSQAQ, gaz, RDL, quartier, Vrai-Prix,
  movers/inspectors, PDF sheets, QC connectors).
- Frontend: new English skin — pine/cream palette, Fraunces serif display,
  custom footer, Ka Maps themed house-ka, mortgage engine UI in English.
- Data: 202 190 Ontario listings imported from immo-ka (pause-ontario moved
  here), types derived from DDF details, IMMOKA_RP_DETAIL_LIMIT=1200.
Simon-Pierre Boucher committed (Aug 27, 2026) Browse files

91 changed files +20,001 −0

91 changed files
added .claude/settings.local.json +5 −0
@@ -0,0 +1,5 @@
1+{
2+ "enabledMcpjsonServers": [
3+ "cluster"
4+ ]
5+}
added .env.example +27 −0
@@ -0,0 +1,27 @@
1+# -----------------------------------------------------------------------------
2+# Immo-Ka — variables d'environnement (modèle ; copier vers .env, NE PAS COMMITTER .env)
3+# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4+# -----------------------------------------------------------------------------
5+
6+# --- Général ------------------------------------------------------------------
7+IMMOKA_BASE_URL=https://www.immo-ka.com
8+
9+# --- Connecteurs immobiliers (scraping) ----------------------------------------
10+FIRECRAWL_API_KEY=
11+SCRAPFLY_KEY= # anti-bot — aussi utilisé par les providers de taux (dernier recours)
12+HQ_CAPTCHA_KEY=
13+HQ_CAPTCHA_PROVIDER=
14+
15+# --- Compte KA (SSO groupe-ka.com) ---------------------------------------------
16+KA_SSO_SECRET=
17+KA_HUB_URL=
18+AUTH_SECRET=
19+
20+# --- Vrai-Prix -----------------------------------------------------------------
21+VRAIPRIX_DB=
22+
23+# --- Moteur hypothécaire (immoka/mortgage) ---------------------------------------
24+# Aucun taux n'est jamais inventé : ces variables ne règlent que la collecte.
25+IMMOKA_MORTGAGE_INTERVAL_MIN=180 # fréquence de collecte (minutes) — watch/maybe_run
26+IMMOKA_MORTGAGE_RETRIES=3 # tentatives par provider avant échec
27+IMMOKA_MORTGAGE_BACKOFF=5 # backoff exponentiel de base (secondes)
added .gitignore +11 −0
@@ -0,0 +1,11 @@
1+.venv/
2+__pycache__/
3+*.pyc
4+data/*.db
5+data/*.db-shm
6+data/*.db-wal
7+frontend/node_modules/
8+frontend/dist/
9+frontend/tsconfig.tsbuildinfo
10+.env
11+.DS_Store
added README.md +67 −0
@@ -0,0 +1,67 @@
1+# House-Ka
2+
3+**www.house-ka.com** — Homes-for-sale aggregator for Canada **outside Québec**,
4+Ontario first. A Groupe KA service, sister site of
5+[Immo-Ka](https://www.immo-ka.com) (Québec).
6+
7+House-Ka continuously aggregates homes publicly listed by Canadian real-estate
8+brokerages and teams whose sites run the **RealtyPress** WordPress plugin on
9+the **CREA DDF** feed. Each site exposes its board's full inventory; a single
10+generic connector (`immoka/connectors/realtypress.py`) covers them all, and
11+cross-site duplicates are masked by DDF number (`external_id = ddf<id>`).
12+
13+## Architecture
14+
15+Forked from Immo-Ka on 2026-08-27 (the Ontario expansion paused there moved
16+here). The Python package keeps its historical name `immoka`.
17+
18+- **Backend** — FastAPI + SQLite (`data/immoka.db`), same pipeline as Immo-Ka:
19+ connectors → `ingest` → dedup → `quality` (relaxed publication rule: price +
20+ city; type/description enrich over time via detail passes) → API.
21+- **Frontend** — React/Vite, **English**, pine/cream/serif skin (deliberately
22+ different from Immo-Ka's cherry). Routes: `/`, `/property/{uid}[/{slug}]`,
23+ `/for-sale/{city}[/{type}]`, `/type/{type}`, `/rates`, `/agencies`, `/stats`.
24+ Map = Ka Maps (`@groupe-ka/ka-maps`, expected at `../../ka-maps`).
25+- **SEO** — `immoka/seo.py` renders server-side HTML (meta, JSON-LD, sitemaps)
26+ in English.
27+- **Mortgage engine** — shared with Immo-Ka (`immoka/mortgage/`), national
28+ Canadian rates. See `docs/mortgage-engine.md`.
29+- **Removed vs Immo-Ka** — everything Québec-only: Hydro-Québec estimates,
30+ BDZI flood, RSQAQ air, gazquebec, rental registry, quartier (census),
31+ Vrai-Prix, movers/inspectors directories, PDF listing sheets, all QC
32+ connectors.
33+
34+## Canonical property types (English)
35+
36+`House, Condo, Townhouse, Semi-detached, Duplex, Triplex, Multi-family,
37+Cottage, Mobile home, Land, Farm, Commercial, Parking` —
38+see `immoka/normalize.py`. DDF list cards carry no type: most listings get
39+their type when their detail page is fetched (`IMMOKA_RP_DETAIL_LIMIT` per
40+source per sync).
41+
42+## Run
43+
44+```bash
45+python run.py sync [source ...] # sync listings
46+python run.py watch [minutes] # sync loop (default 60 min)
47+python run.py serve [port] # API + frontend (default 8098)
48+python run.py list # registered connectors
49+python run.py geocode [n] # geocode listings missing coordinates
50+python run.py mortgage-sync # collect mortgage rates
51+```
52+
53+`.env`: `IMMOKA_BASE_URL=https://www.house-ka.com`,
54+`IMMOKA_RP_DETAIL_LIMIT=<n>` (detail pages fetched per source per sync).
55+
56+## Deployment
57+
58+M4M64b, `~/apps/house-ka`, PM2 (`house-ka-web` :8098, `house-ka-sync`,
59+`house-ka-ngrok` → www.house-ka.com). Remote-first: the repo on the node is
60+the source of truth, `origin` = spbgit (`gitsrv:house-ka.git`).
61+
62+## Adding sources (rest of Canada)
63+
64+RealtyPress sites exist across Canada. Census & instructions:
65+`docs/ontario-agencies.md` (method transposes to any province). Add the site
66+to `data/ontario_agencies.json` + an entry in `data/sources.json`, then
67+`python run.py sync <id>`. The coordinate guard covers all of Canada.
added data/ontario_agencies.json +96 −0
@@ -0,0 +1,96 @@
1+[
2+ {
3+ "id": "rp_ag_revelrealty",
4+ "name": "Revel Realty (Niagara & provincial)",
5+ "site": "https://revelrealty.ca",
6+ "archive": "listings",
7+ "max_pages": 1300,
8+ "note": "Recensement ON 2026-08-27 : ~110 145 fiches — pool DDF quasi provincial. Archive /listings/ (⚠ /listing/ = carousel 8 cartes), 108 cartes/page avec posts_per_page=100. RealtyPress/DDF."
9+ },
10+ {
11+ "id": "rp_ag_codygroup",
12+ "name": "The Cody Group (London/ITSO)",
13+ "site": "https://codygroup.ca",
14+ "archive": "all-regional-listings",
15+ "max_pages": 700,
16+ "note": "Recensement ON 2026-08-27 : ~58 062 fiches (London + ITSO élargi). Archive /all-regional-listings/, fiches sous le même chemin. RealtyPress/DDF."
17+ },
18+ {
19+ "id": "rp_ag_suttonottawa",
20+ "name": "Sutton Group — Ottawa Realty",
21+ "site": "https://suttonottawa.ca",
22+ "note": "Recensement ON 2026-08-27 : ~10 080 fiches (OREB+). RealtyPress/DDF."
23+ },
24+ {
25+ "id": "rp_ag_helensteam",
26+ "name": "Helen's Team (Kitchener-Waterloo)",
27+ "site": "https://helensteam.ca",
28+ "note": "Recensement ON 2026-08-27 : ~9 910 fiches (Kitchener-Waterloo). RealtyPress/DDF."
29+ },
30+ {
31+ "id": "rp_ag_greybruce",
32+ "name": "Grey Bruce Real Estate",
33+ "site": "https://greybrucerealestate.ca",
34+ "note": "Recensement ON 2026-08-27 : ~8 268 fiches (Grey-Bruce/Georgian Bay). RealtyPress/DDF. Même feed que collaborativerealestate.ca (fallback)."
35+ },
36+ {
37+ "id": "rp_ag_remaxfinest",
38+ "name": "RE/MAX Finest Realty (Kingston)",
39+ "site": "https://remaxfinestrealty.com",
40+ "note": "Recensement ON 2026-08-27 : ~8 217 fiches (Kingston). RealtyPress/DDF."
41+ },
42+ {
43+ "id": "rp_ag_riouxbaker",
44+ "name": "Rioux Baker Real Estate Team (Collingwood)",
45+ "site": "https://riouxbakerteam.com",
46+ "note": "Recensement ON 2026-08-27 : ~7 716 fiches (Collingwood/South Georgian Bay). RealtyPress/DDF."
47+ },
48+ {
49+ "id": "rp_ag_countyguys",
50+ "name": "The County Guys (Prince Edward County)",
51+ "site": "https://thecountyguys.com",
52+ "note": "Recensement ON 2026-08-27 : ~6 878 fiches (Prince Edward County/Quinte). RealtyPress/DDF."
53+ },
54+ {
55+ "id": "rp_ag_labrosse",
56+ "name": "Labrosse Real Estate (Ottawa/Orléans)",
57+ "site": "https://labrosserealestate.com",
58+ "note": "Recensement ON 2026-08-27 : ~6 873 fiches (Ottawa/Orléans, équipe FRANCOPHONE). RealtyPress/DDF."
59+ },
60+ {
61+ "id": "rp_ag_ryanpattinson",
62+ "name": "Ryan Pattinson (Pembroke/Renfrew)",
63+ "site": "https://ryanpattinson.com",
64+ "note": "Recensement ON 2026-08-27 : ~6 601 fiches (Pembroke/vallée de l'Outaouais ON). RealtyPress/DDF."
65+ },
66+ {
67+ "id": "rp_ag_grapevine",
68+ "name": "Grapevine (Ottawa)",
69+ "site": "https://grapevine.ca",
70+ "note": "Recensement ON 2026-08-27 : ~6 550 fiches (Ottawa). RealtyPress/DDF. Suivre les redirections www/apex."
71+ },
72+ {
73+ "id": "rp_ag_rlpheartland",
74+ "name": "Royal LePage Heartland Realty",
75+ "site": "https://rlpheartland.ca",
76+ "note": "Recensement ON 2026-08-27 : ~5 577 fiches (Midwestern Ontario — Huron/Perth). RealtyPress/DDF."
77+ },
78+ {
79+ "id": "rp_ag_signaturenorth",
80+ "name": "Signature North Realty (Thunder Bay)",
81+ "site": "https://signaturenorthrealty.ca",
82+ "note": "Recensement ON 2026-08-27 : ~1 023 fiches (Thunder Bay). RealtyPress/DDF."
83+ },
84+ {
85+ "id": "rp_ag_saultstemarie",
86+ "name": "Sault Ste. Marie Real Estate (Century 21 Choice)",
87+ "site": "https://saultstemarierealestate.com",
88+ "note": "Recensement ON 2026-08-27 : ~864 fiches (Sault Ste. Marie). RealtyPress/DDF."
89+ },
90+ {
91+ "id": "rp_ag_cbnorthbay",
92+ "name": "Coldwell Banker Peter Minogue (North Bay)",
93+ "site": "https://cbnorthbay.com",
94+ "note": "Recensement ON 2026-08-27 : ~307 fiches (North Bay). RealtyPress/DDF."
95+ }
96+]
added data/sources.json +169 −0
@@ -0,0 +1,169 @@
1+{
2+ "sources": [
3+ {
4+ "id": "rp_ag_revelrealty",
5+ "name": "Revel Realty (Niagara & provincial)",
6+ "url": "https://revelrealty.ca",
7+ "listing_url": "https://revelrealty.ca/listings/",
8+ "coverage": "Niagara + pool DDF quasi provincial — ~110 000 fiches",
9+ "connector": "realtypress",
10+ "status": "actif",
11+ "type": "agence",
12+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
13+ },
14+ {
15+ "id": "rp_ag_codygroup",
16+ "name": "The Cody Group (London/ITSO)",
17+ "url": "https://codygroup.ca",
18+ "listing_url": "https://codygroup.ca/all-regional-listings/",
19+ "coverage": "London + ITSO élargi — ~58 000 fiches DDF",
20+ "connector": "realtypress",
21+ "status": "actif",
22+ "type": "agence",
23+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
24+ },
25+ {
26+ "id": "rp_ag_suttonottawa",
27+ "name": "Sutton Group — Ottawa Realty",
28+ "url": "https://suttonottawa.ca",
29+ "listing_url": "https://suttonottawa.ca/listing/",
30+ "coverage": "Ottawa et l'Est ontarien (board OREB) — ~10 000 fiches DDF",
31+ "connector": "realtypress",
32+ "status": "actif",
33+ "type": "agence",
34+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
35+ },
36+ {
37+ "id": "rp_ag_helensteam",
38+ "name": "Helen's Team (Kitchener-Waterloo)",
39+ "url": "https://helensteam.ca",
40+ "listing_url": "https://helensteam.ca/listing/",
41+ "coverage": "Kitchener-Waterloo et région (ITSO) — ~9 900 fiches DDF",
42+ "connector": "realtypress",
43+ "status": "actif",
44+ "type": "agence",
45+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
46+ },
47+ {
48+ "id": "rp_ag_greybruce",
49+ "name": "Grey Bruce Real Estate",
50+ "url": "https://greybrucerealestate.ca",
51+ "listing_url": "https://greybrucerealestate.ca/listing/",
52+ "coverage": "Grey-Bruce / Georgian Bay (ITSO) — ~8 300 fiches DDF",
53+ "connector": "realtypress",
54+ "status": "actif",
55+ "type": "agence",
56+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
57+ },
58+ {
59+ "id": "rp_ag_remaxfinest",
60+ "name": "RE/MAX Finest Realty (Kingston)",
61+ "url": "https://remaxfinestrealty.com",
62+ "listing_url": "https://remaxfinestrealty.com/listing/",
63+ "coverage": "Kingston et région (KAREA) — ~8 200 fiches DDF",
64+ "connector": "realtypress",
65+ "status": "actif",
66+ "type": "agence",
67+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
68+ },
69+ {
70+ "id": "rp_ag_riouxbaker",
71+ "name": "Rioux Baker Real Estate Team (Collingwood)",
72+ "url": "https://riouxbakerteam.com",
73+ "listing_url": "https://riouxbakerteam.com/listing/",
74+ "coverage": "Collingwood / South Georgian Bay — ~7 700 fiches DDF",
75+ "connector": "realtypress",
76+ "status": "actif",
77+ "type": "agence",
78+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
79+ },
80+ {
81+ "id": "rp_ag_countyguys",
82+ "name": "The County Guys (Prince Edward County)",
83+ "url": "https://thecountyguys.com",
84+ "listing_url": "https://thecountyguys.com/listing/",
85+ "coverage": "Prince Edward County / Quinte — ~6 900 fiches DDF",
86+ "connector": "realtypress",
87+ "status": "actif",
88+ "type": "agence",
89+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
90+ },
91+ {
92+ "id": "rp_ag_labrosse",
93+ "name": "Labrosse Real Estate (Ottawa/Orléans)",
94+ "url": "https://labrosserealestate.com",
95+ "listing_url": "https://labrosserealestate.com/listing/",
96+ "coverage": "Ottawa / Orléans (équipe francophone, OREB) — ~6 900 fiches DDF",
97+ "connector": "realtypress",
98+ "status": "actif",
99+ "type": "agence",
100+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
101+ },
102+ {
103+ "id": "rp_ag_ryanpattinson",
104+ "name": "Ryan Pattinson (Pembroke/Renfrew)",
105+ "url": "https://ryanpattinson.com",
106+ "listing_url": "https://ryanpattinson.com/listing/",
107+ "coverage": "Pembroke / vallée de l'Outaouais ontarienne — ~6 600 fiches DDF",
108+ "connector": "realtypress",
109+ "status": "actif",
110+ "type": "agence",
111+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
112+ },
113+ {
114+ "id": "rp_ag_grapevine",
115+ "name": "Grapevine (Ottawa)",
116+ "url": "https://grapevine.ca",
117+ "listing_url": "https://grapevine.ca/listing/",
118+ "coverage": "Ottawa (OREB) — ~6 600 fiches DDF",
119+ "connector": "realtypress",
120+ "status": "actif",
121+ "type": "agence",
122+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
123+ },
124+ {
125+ "id": "rp_ag_rlpheartland",
126+ "name": "Royal LePage Heartland Realty",
127+ "url": "https://rlpheartland.ca",
128+ "listing_url": "https://rlpheartland.ca/listing/",
129+ "coverage": "Midwestern Ontario (Huron-Perth) — ~5 600 fiches DDF",
130+ "connector": "realtypress",
131+ "status": "actif",
132+ "type": "agence",
133+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
134+ },
135+ {
136+ "id": "rp_ag_signaturenorth",
137+ "name": "Signature North Realty (Thunder Bay)",
138+ "url": "https://signaturenorthrealty.ca",
139+ "listing_url": "https://signaturenorthrealty.ca/listing/",
140+ "coverage": "Thunder Bay — ~1 000 fiches DDF",
141+ "connector": "realtypress",
142+ "status": "actif",
143+ "type": "agence",
144+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
145+ },
146+ {
147+ "id": "rp_ag_saultstemarie",
148+ "name": "Sault Ste. Marie Real Estate (Century 21 Choice)",
149+ "url": "https://saultstemarierealestate.com",
150+ "listing_url": "https://saultstemarierealestate.com/listing/",
151+ "coverage": "Sault Ste. Marie — ~860 fiches DDF",
152+ "connector": "realtypress",
153+ "status": "actif",
154+ "type": "agence",
155+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
156+ },
157+ {
158+ "id": "rp_ag_cbnorthbay",
159+ "name": "Coldwell Banker Peter Minogue (North Bay)",
160+ "url": "https://cbnorthbay.com",
161+ "listing_url": "https://cbnorthbay.com/listing/",
162+ "coverage": "North Bay / Nipissing — ~300 fiches DDF",
163+ "connector": "realtypress",
164+ "status": "actif",
165+ "type": "agence",
166+ "note": "House-Ka — generic RealtyPress connector (registry data/ontario_agencies.json). WordPress RealtyPress plugin on the CREA DDF feed. external_id ddf<id>: cross-site dedup by MIN(uid)."
167+ }
168+ ]
169+}
\ No newline at end of file
added docs/mortgage-engine.md +163 −0
@@ -0,0 +1,163 @@
1+# Moteur hypothécaire Immo-Ka (Mortgage Intelligence Engine)
2+
3+Moteur natif de collecte, d'historisation et de calcul des taux hypothécaires
4+canadiens, intégré au backend FastAPI d'Immo-Ka. **Aucun taux n'est jamais
5+inventé, codé en dur ni estimé** : tout taux affiché provient d'une page
6+officielle d'une institution financière, avec provenance (URL source) et
7+fraîcheur (horodatage de collecte).
8+
9+## Architecture
10+
11+```
12+immoka/mortgage/
13+├── providers/ # 1 connecteur indépendant par institution (12)
14+│ ├── base.py # RateProvider : fetch() → produits normalisés
15+│ └── README.md # comment ajouter une banque
16+├── validate.py # garde-fous anti-aberration (419 % ≠ 4,19 %…)
17+├── store.py # SQLite annexe data/mortgage.db — historisation
18+├── scheduler.py # orchestration : retries, backoff, santé, isolation
19+├── calc.py # mathématiques hypothécaires canadiennes
20+├── cmhc.py # assurance prêt (SCHL) + taxe de vente QC
21+└── api.py # routes /api/mortgage/* (montées dans web.py)
22+```
23+
24+Le flux : `provider.fetch()` → `validate_batch()` → `store.record_observations()`.
25+Les providers ne touchent jamais la base ; le calculateur ne touche jamais les
26+scrapers — seule l'API interne les relie.
27+
28+## Institutions couvertes (12 connecteurs)
29+
30+| slug | Institution | Type de source |
31+|---|---|---|
32+| `bank_of_canada` | Banque du Canada | Valet API (JSON officiel) — taux de référence |
33+| `bmo` | BMO | JSON embarqué |
34+| `cibc` | CIBC | JSON |
35+| `desjardins` | Desjardins | HTML |
36+| `eq_bank` | Banque EQ | HTML |
37+| `first_national` | First National | HTML |
38+| `mcap` | MCAP | HTML (taux préférentiel) |
39+| `national_bank` | Banque Nationale | HTML |
40+| `rbc` | RBC | JSON |
41+| `scotiabank` | Banque Scotia | JSON (posted + promos) |
42+| `tangerine` | Tangerine | JSON |
43+| `td` | TD Canada Trust | JSON |
44+
45+Chaque produit est normalisé par `RateProvider.make_product()` :
46+`provider, institution, product_name, rate_type (fixed|variable|other),
47+term_months, kind (posted|special), rate, apr, insured_status
48+(insured|insurable|uninsured|unknown), purpose (purchase|renewal|refinance|unknown),
49+amortization_max_years, conditions, source_url, confidence, raw`.
50+
51+Les taux **préférentiels/prime** sont stockés en `rate_type="other"` +
52+`purpose="unknown"` : ils ne peuvent jamais contaminer un classement
53+« meilleur taux d'achat ».
54+
55+## Validation (validate.py)
56+
57+Rejette avant enregistrement :
58+- taux hors bornes plausibles (0,5 %–24 %) — attrape `4.19 → 419` ;
59+- champs requis manquants, enums invalides, termes hors 3–120 mois ;
60+- APR incohérent (APR < taux − 0,02 pt) ou aberrant ;
61+- doublons exacts dans un même lot (silencieusement dédupliqués).
62+
63+Un lot partiellement invalide n'est pas jeté : les produits sains sont
64+enregistrés, les problèmes journalisés.
65+
66+## Historisation (store.py — data/mortgage.db, WAL)
67+
68+- `rate_observations` : périodes de validité (`valid_from`/`valid_to`,
69+ `is_current`). Taux inchangé → simple mise à jour de `last_checked` ;
70+ taux changé → clôture de la période + nouvelle ligne. Un saut > 2,5 pts en
71+ < 48 h est rejeté **sans écraser** la donnée existante (garde anti-aberration
72+ au niveau BD).
73+- `provider_runs` : journal de chaque collecte (statut, durée, produits,
74+ changements, rejets) → santé OK / WARNING (> 24 h) / ERROR.
75+- `product_key` : sha1 tronqué de
76+ `provider|rate_type|term|kind|insured|purpose|name` — identité stable d'un
77+ produit à travers le temps.
78+
79+En cas de panne d'une source, **les derniers taux valides restent servis**,
80+avec leur âge affiché (mention « stale » au-delà de 24 h).
81+
82+## Collecte (scheduler.py)
83+
84+- `run_provider(slug)` : retries (défaut 3) avec backoff exponentiel ;
85+ une exception d'un provider n'affecte jamais les autres.
86+- `run()` : séquentiel et poli (`request_delay` par provider — jamais de
87+ martèlement des sites bancaires).
88+- `watch(min)` : boucle autonome ; `maybe_run()` est appelé depuis la boucle
89+ d'ingestion existante (**process PM2 `immo-ka-sync`**) et ne collecte que si
90+ la dernière passe date de plus de `IMMOKA_MORTGAGE_INTERVAL_MIN` minutes
91+ (défaut 180).
92+
93+CLI :
94+
95+```bash
96+python run.py mortgage-sync [slug…] # collecte (toutes ou certaines banques)
97+python run.py mortgage-watch [min] # boucle autonome
98+python run.py mortgage-status # santé des providers
99+```
100+
101+Variables d'environnement (voir `.env.example`) :
102+`IMMOKA_MORTGAGE_INTERVAL_MIN`, `IMMOKA_MORTGAGE_RETRIES`,
103+`IMMOKA_MORTGAGE_BACKOFF`, `SCRAPFLY_KEY` (anti-bot, dernier recours).
104+
105+## Calculateur canadien (calc.py + cmhc.py)
106+
107+- **Composition semestrielle** pour les taux fixes (norme légale canadienne) :
108+ taux périodique = `(1 + r/2)^(2/f) − 1`. Valeur étalon vérifiée par test :
109+ 100 000 $ à 6 % sur 25 ans = **639,81 $/mois** (≠ 644,30 $ en composition
110+ mensuelle américaine — testé aussi, pour prouver qu'on n'utilise pas la
111+ mauvaise formule). Taux variables : composition mensuelle.
112+- 6 fréquences : mensuelle, bimensuelle, aux 2 semaines, hebdomadaire,
113+ accélérée aux 2 semaines (mensualité ÷ 2), accélérée hebdo (÷ 4).
114+- **Test de résistance** fédéral : qualification à `max(taux + 2, 5,25 %)`.
115+- **SCHL** (cmhc.py) : mise de fonds légale minimale (5 % / 10 % / 20 %),
116+ primes par tranche RPV (0,60 % → 4,00 %), surprime +0,20 % amortissement
117+ 30 ans (premier acheteur), plafond assurable 1,5 M$, **TVQ 9,975 % sur la
118+ prime payable comptant** (spécificité québécoise) — la prime s'ajoute au
119+ prêt, la taxe non.
120+- Tableau d'amortissement, résumé de terme (solde au renouvellement),
121+ scénarios de renouvellement (+0/+1/+2/+3 pts), ratios ABD/ATD informatifs,
122+ inverses (prêt max pour un versement, taux requis).
123+
124+## API interne (`/api/mortgage/*`)
125+
126+| Route | Rôle |
127+|---|---|
128+| `GET /rates` | taux courants filtrables (type, terme, kind, provider…) |
129+| `GET /rates/best` | meilleur taux comparable + classement par institution |
130+| `GET /rates/history` | périodes de validité (historique réel, jamais extrapolé) |
131+| `GET /providers` | santé des sources (OK/WARNING/ERROR, âge, produits) |
132+| `GET /market` | vue marché (meilleur/médiane/variations 7-30 j) |
133+| `GET /intelligence` | market + taux préférentiels (page /taux-hypothecaires) |
134+| `POST /calculate` | calcul complet (SCHL, stress, terme, renouvellement…) |
135+| `POST /affordability` | capacité d'emprunt (ABD/ATD + stress test) |
136+
137+Règle absolue : **jamais de comparaison de produits incomparables** — affiché
138+vs offre spéciale, assuré vs non assuré — sans l'indiquer. Le comparateur ne
139+garde qu'un produit comparable par institution (l'offre spéciale prime).
140+
141+## Frontend
142+
143+- **Fiche propriété** (`Financement.tsx`, section « Financer cette propriété »,
144+ ventes seulement) : prix prérempli, mise de fonds $/% synchronisée,
145+ versement + taux utilisé avec provenance/fraîcheur, SCHL détaillée,
146+ coût réel mensuel (+ taxes municipales/scolaires de la fiche), stress test,
147+ renouvellement, comparateur banques, historique SVG, amortissement.
148+- **Page `/taux-hypothecaires`** (`Taux.tsx`) : vue marché cliquable,
149+ comparateur par institution (nature + fraîcheur + source officielle),
150+ historique, santé des sources. Référencée (seo.py + sitemap).
151+
152+## Tests
153+
154+```bash
155+PYTHONPATH=. .venv/bin/python -P -m unittest discover -s tests
156+```
157+
158+61 tests : `test_mortgage_calc.py` (valeurs étalons, fréquences accélérées,
159+inverses, stress), `test_mortgage_cmhc.py` (primes, TVQ, éligibilité),
160+`test_mortgage_validate.py` (anti-aberration), `test_mortgage_store.py`
161+(historisation, garde 2,5 pts, meilleur taux), `test_mortgage_providers.py`
162+(chaque parseur sur fixtures HTML/JSON committées dans
163+`tests/fixtures/mortgage/` — aucun réseau).
added docs/ontario-agencies.md +87 −0
@@ -0,0 +1,87 @@
1+# Extension Ontario — liste des agences & sous-agents connectables
2+
3+Recensement du 2026-08-27 (~150 sites vérifiés live par curl : plateforme, rendu, volume, code HTTP).
4+Doctrine immo-ka : connecteurs par site d'agence/équipe (éviter les portails durs type realtor.ca).
5+Constat structurel ON : contrairement au Québec, un site d'agence expose souvent l'**IDX/DDF complet de son board** (TRREB, OREB, RAHB, ITSO, LSTAR, WECAR, KAREA…) → quelques connecteurs bien choisis ≈ toute la province. Dédupliquer par **MLS#**.
6+
7+## Priorité 1 — RealtyPress (plugin WordPress branché CREA DDF)
8+
9+Signature : `wp-content/plugins/realtypress-premium`, archive `/listing/` ou `/listings/`, SSR paginé `?paged=N`, MLS# dans l'URL de détail. **Un connecteur générique = tous ces sites.** Aucun anti-bot (200 en curl nu).
10+
11+| Site | Zone | Volume vérifié |
12+|---|---|---|
13+| revelrealty.ca | Niagara (multi-bureaux) | **110 171** — pool DDF quasi complet ⭐ |
14+| codygroup.ca | London | **58 074** ⭐ |
15+| suttonottawa.ca | Ottawa | 10 080 (OREB+) |
16+| helensteam.ca | Kitchener-Waterloo | 9 910 |
17+| greybrucerealestate.ca | Grey-Bruce/Georgian Bay | 8 268 |
18+| collaborativerealestate.ca | Blue Mountains | 8 268 (même feed que Grey-Bruce → fallback) |
19+| remaxfinestrealty.com | Kingston | 8 217 |
20+| riouxbakerteam.com | Collingwood | 7 716 |
21+| thecountyguys.com | Prince Edward County | 6 878 |
22+| labrosserealestate.com | Ottawa/Orléans (**francophone**) | 6 873 |
23+| ryanpattinson.com | Pembroke/Renfrew | 6 601 |
24+| grapevine.ca | Ottawa | 6 550 |
25+| rlpheartland.ca | Midwestern Ontario | 5 577 |
26+| pennyblake.com / dynamickingston.com / thehintonteam.com | Kingston | 1 760 / 1 756 / 1 666 |
27+| signaturenorthrealty.ca | Thunder Bay | 1 023 |
28+| saultstemarierealestate.com | Sault Ste Marie | 864 |
29+| soldsmart.ca | Cornwall (CDREB) | 682 |
30+| 401homes.ca | corridor 401 | 662 |
31+| tcrealty.ca | Thunder Bay | 564 |
32+| cbnorthbay.com | North Bay | 307 |
33+| thebrollygroup.ca / teamkate.ca | Brantford | 231+ / régional |
34+| boldtrealty.ca | St. Catharines | bureau |
35+| Autres confirmés | — | thewillsteam.ca, dyerrealty.ca, muskoka-realestate.ca, barriehome.net, morrishometeam.com, dalebryant.ca, kenpipher.ca, greatermuskoka.ca, claimpostrealty.com, paulrushforth.com, liamswords.com |
36+
37+→ Couverture : Niagara, London, Ottawa, KW, Kingston, Georgian Bay, PEC, Nord — **quasi toute la province avec un seul parseur**. Démo publique : demo.realtypress.ca.
38+
39+## Priorité 2 — myRealPage « recip.html » (réciprocité TRREB complète)
40+
41+SSR paginé `?_pg=N` (~12 annonces/page, prix dans le HTML), hôte central `idx.myrealpage.com`. Un connecteur = toute la grappe.
42+
43+- goldenhouserealty.com, remaxpluscity.com, foresthillcentral.com, baystreetcondos.ca, topgan.ca (Toronto — TRREB complet)
44+- jarrodarmstrong.com (condos TRREB), condominiums.ca (IDX condos GTA), yourmarkhamrealestate.ca (76 prix SSR)
45+- dottedline.ca (Tillsonburg, SSR 112) ; variantes JS : detailsrealty.ca, chellteam.com, maguireteam.ca, powerofsaleplus.ca
46+
47+## Priorité 3 — Sierra Interactive (SSR, API JSON connue)
48+
49+- robgolfi.com — RE/MAX Escarpment : **2 678 (RAHB complet)** + 924 Oakville ⭐
50+- weknowottawa.com (Hamre, Ottawa 634+), feelyrealestate.com (Ottawa), niagarahomes.com (McGarr, 474 St. Catharines), teamgoran.com (Windsor/Chatham), viewbrantfordhomes.com, goodmanors.ca (Sudbury), anuraghomes.ca (KW)
51+
52+## Priorité 4 — AgentLocator (plateforme ontarienne, SSR + JSON `TotalIDX/TotalVOW` embarqué)
53+
54+- daverealty.ca (Cambridge, 1 272 IDX ITSO), ateamlondon.ca (London LSTAR), kitchenerwaterloo-realestate.com, stjeanrealty.com (Hamilton RAHB)
55+- Grappe Durham/York : miragerealestate.ca (103 prix), lighthouserealtygroup.ca (96), shawnlepp.com, buyselllove.ca, itsanna.ca, realtorsunnyg.com, teamarora.com
56+- jancsiks.com (Kawartha), pairofkings.ca (Dufferin) ; JS-only : seguinrealtyltd.com (**franco Hawkesbury**), agentinottawa.com
57+
58+## Priorité 5 — autres grappes mutualisées
59+
60+- **Plateforme Windsor commune** (custom SSR, URLs `/ville-properties` identiques) : buckinghamrealty.ca (**4 943 = WECAR complet + Chatham-Kent**), deerbrookproperty.com, nkrealestate.ca — 1 connecteur = 3 sites, zone mal desservie ailleurs ⭐
61+- **Real Estate Webmasters (REW)** : danplowman.com (Whitby, 529 prix SSR, TRREB large) ⭐, londonontariorealestate.com (Team Forster ~2 000 LSTAR), gordwaites.com + rlpmuskoka.com (Muskoka, cap 500), sudburyrealestate.ca
62+- **EZ Media** (vendeur régional, WP SSR) : performancerealty.ca (RLP Ottawa ~700 agents), remaxdeltahometeam.com (**franco Embrun/Prescott-Russell**), brockvillesutton.com, briangraham.ca (North Bay)
63+- **InCom** (JS — API centrale `/mapsearchapp/search?json=true` à reverser une fois) : chestnutpark.com, foresthill.com, dhesirealestate.ca, royallepagepremiumone.com, jasonyuteam.com, cbadvantage.ca, niagarapropertygroup.ca, suttongroupinnovative.com, kwhomegrouprealty.ca, brockvillehomes.com
64+- **Luxury Presence** (MLS TRREB dans les assets) : mcdadi.com (350+), goodalemillerteam.com, ppreteam.com, harveykalles.com, psrbrokerage.com, muskokacottagelistings.com, muskokacottagesforsale.com, lakelandsrealestate.ca
65+- **SoldPress / Team Marshall** (WP SSR, 1 parseur = 4 domaines) : teammarshall.ca, findingyourmuskoka.ca, findingyourparrysound.ca, findingyourmagnetawan.ca
66+- **c21.ca corporate** (API avec `company_uuid` — 1 connecteur = tous les bureaux C21 Canada) : heritagehouseniagara.c21.ca, c21firstcanadian.c21.ca, c21bluesky.com…
67+
68+## Sites custom individuels (SSR, un connecteur dédié chacun si ROI)
69+
70+- bushrealtysystems.com (Hamilton — moteur de recherche CREA board complet, `/search/listing/CREA/<mls>`)
71+- remaxquinte.com (Belleville, ColdFusion `listings.cfm`), thegrimeteam.com (Orangeville, ColdFusion, board interrogeable par params URL)
72+- exitrealtymatrix.com (Ottawa/Embrun franco-friendly, Onjax), therealtyfirm.ca (London/Woodstock, RealtyNinja SSR), m1wellington.com (Guelph, IDX Broker)
73+- trilliumwest.com (Guelph, 58 MLS refs), woolcott.ca (Waterdown DDF ~100+), peggyhill.com (Barrie, #1 RE/MAX Canada), tarteam.com (Markham, 31 prix), stacyvermeire.com (Cobourg, Ubertor SSR), troyausten.ca (Haliburton), homesincambridge.com (~108)
74+- JS-only à reverser si besoin : rightathomerealty.com (plus gros courtier indépendant du Canada — XHR probable Repliers/AMPRE), mls-sarnia.com + windsorrealestate.com (`__NEXT_DATA__`), phinneyrealestate.com, heyray.ca, elevatelondon.ca, housesforsaleottawa.ca, ngroup.ca (Kingston), kbbrokerage.ca, kawarthalife.com
75+
76+## Bloqués (403 Cloudflare — Scrapfly requis, faible priorité)
77+
78+teamrealty.ca, royalcity.com, royallepagetriland.com, wollerealty.com, remaxrecentre.ca, rlpbinder.ca, royallepagebrantrealty.com, viewhomes.ca, royallepagethunderbay.com, kormendytrott.com, rlpburloak.ca, remax-gc.ca, teamprestige.ca, estaterealty.ca, mullingroup.ca, royallepagequest.ca, faristeam.ca (429). Plusieurs partagent le même vendor (template RLP) → potentiel méga-connecteur via Scrapfly.
79+
80+## Plan de couverture minimal suggéré
81+
82+1. Connecteur **RealtyPress générique** + revelrealty/codygroup en sources primaires (≈ pool DDF provincial), sites régionaux en secours/dédup
83+2. Connecteur **myRealPage recip** (TRREB/GTA complet)
84+3. Connecteur **Sierra** (robgolfi = RAHB ; weknowottawa = OREB)
85+4. Connecteur **AgentLocator** (Durham/York + ITSO/LSTAR)
86+5. Connecteur **Windsor mutualisé** (WECAR) + **REW** (danplowman/Team Forster)
87+→ ~6-7 parseurs pour une couverture provinciale quasi complète, boards dédupliqués par MLS#.
added frontend/index.html +31 −0
@@ -0,0 +1,31 @@
1+<!doctype html>
2+<!-- ---------------------------------------------------------------------------
3+ House-Ka — Homes-for-sale aggregator (Canada outside Québec, Ontario first)
4+ Author: Simon-Pierre Boucher — contact@spboucher.ai
5+---------------------------------------------------------------------------- -->
6+<html lang="en-CA">
7+ <head>
8+ <meta charset="UTF-8" />
9+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
10+ <title>House-Ka — A Groupe KA service</title>
11+ <meta name="description" content="House-Ka, a Groupe KA service, aggregates homes for sale listed by Canadian real-estate brokerages on the CREA DDF feed — Ontario first, the rest of Canada next. Always up to date." />
12+ <meta name="theme-color" content="#14201a" />
13+ <meta name="mobile-web-app-capable" content="yes" />
14+ <meta name="apple-mobile-web-app-capable" content="yes" />
15+ <meta name="apple-mobile-web-app-title" content="House-Ka" />
16+ <link rel="preconnect" href="https://fonts.googleapis.com" />
17+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18+ <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
19+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
20+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
21+ <meta property="og:image" content="https://www.house-ka.com/og.png" />
22+ <meta property="og:image:width" content="1200" />
23+ <meta property="og:image:height" content="630" />
24+ <meta name="twitter:card" content="summary_large_image" />
25+ <meta name="twitter:image" content="https://www.house-ka.com/og.png" />
26+ </head>
27+ <body>
28+ <div id="root"></div>
29+ <script type="module" src="/src/main.tsx"></script>
30+ </body>
31+</html>
added frontend/package-lock.json +1874 −0
Large diff (1,874 lines) is not loaded by default to keep the page fast.Load diff
added frontend/package.json +26 −0
Large diff (27 lines) is not loaded by default to keep the page fast.Load diff
added frontend/public/apple-touch-icon.png +0 −0

Binary file not shown.

added frontend/public/favicon.svg +5 −0
Large diff (5 lines) is not loaded by default to keep the page fast.Load diff
added frontend/public/og.png +0 −0

Binary file not shown.

added frontend/scripts/check-order.mjs +41 −0
Large diff (41 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/App.tsx +238 −0
Large diff (238 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/api.ts +383 −0
Large diff (383 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/AmenityIco.tsx +112 −0
Large diff (112 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/Financing.tsx +369 −0
Large diff (369 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/Icons.tsx +188 −0
Large diff (188 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/ListingCard.tsx +57 −0
Large diff (57 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/MapView.tsx +321 −0
Large diff (321 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/NearbyPlaces.tsx +84 −0
Large diff (84 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/PropertyImg.tsx +45 −0
Large diff (45 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/PropertyMap.tsx +69 −0
Large diff (69 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/components/RateHistory.tsx +101 −0
Large diff (101 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/ka/ecosystem.json +206 −0
Large diff (207 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/ka/tokens.css +319 −0
Large diff (319 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/kamaps/adapter.ts +96 −0
Large diff (96 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/kamaps/config.ts +11 −0
Large diff (11 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/kamaps/theme.ts +49 −0
Large diff (49 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/main.tsx +20 −0
Large diff (20 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Agencies.tsx +91 −0
Large diff (91 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Contact.tsx +76 −0
Large diff (76 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Home.tsx +518 −0
Large diff (518 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Legal.tsx +114 −0
Large diff (114 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Listing.tsx +444 −0
Large diff (444 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Rates.tsx +192 −0
Large diff (192 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/pages/Stats.tsx +139 −0
Large diff (139 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/styles.css +1827 −0
Large diff (1,827 lines) is not loaded by default to keep the page fast.Load diff
added frontend/src/vite-env.d.ts +1 −0
Large diff (1 lines) is not loaded by default to keep the page fast.Load diff
added frontend/tsconfig.json +21 −0
Large diff (21 lines) is not loaded by default to keep the page fast.Load diff
added frontend/vite.config.ts +15 −0
Large diff (15 lines) is not loaded by default to keep the page fast.Load diff
added immoka/__init__.py +9 −0
Large diff (9 lines) is not loaded by default to keep the page fast.Load diff
added immoka/auth.py +306 −0
Large diff (306 lines) is not loaded by default to keep the page fast.Load diff
added immoka/commerces.py +294 −0
Large diff (294 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/__init__.py +30 −0
Large diff (30 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/_detailutil.py +219 −0
Large diff (219 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/_resilient.py +318 −0
Large diff (318 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/base.py +244 −0
Large diff (244 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/jsonld.py +51 −0
Large diff (51 lines) is not loaded by default to keep the page fast.Load diff
added immoka/connectors/realtypress.py +300 −0
Large diff (300 lines) is not loaded by default to keep the page fast.Load diff
added immoka/db.py +575 −0
Large diff (575 lines) is not loaded by default to keep the page fast.Load diff
added immoka/favorites.py +75 −0
Large diff (75 lines) is not loaded by default to keep the page fast.Load diff
added immoka/geocode.py +654 −0
Large diff (654 lines) is not loaded by default to keep the page fast.Load diff
added immoka/hubfav.py +100 −0
Large diff (100 lines) is not loaded by default to keep the page fast.Load diff
added immoka/hubprofile.py +79 −0
Large diff (79 lines) is not loaded by default to keep the page fast.Load diff
added immoka/imgaudit.py +293 −0
Large diff (293 lines) is not loaded by default to keep the page fast.Load diff
added immoka/ingest.py +119 −0
Large diff (119 lines) is not loaded by default to keep the page fast.Load diff
added immoka/kaid.py +449 −0
Large diff (449 lines) is not loaded by default to keep the page fast.Load diff
added immoka/kapdf.py +1267 −0
Large diff (1,267 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/__init__.py +7 −0
Large diff (7 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/api.py +373 −0
Large diff (373 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/calc.py +257 −0
Large diff (257 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/cmhc.py +123 −0
Large diff (123 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/README.md +85 −0
Large diff (85 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/__init__.py +25 −0
Large diff (25 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/bank_of_canada.py +77 −0
Large diff (77 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/base.py +133 −0
Large diff (133 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/bmo.py +135 −0
Large diff (135 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/cibc.py +144 −0
Large diff (144 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/desjardins.py +83 −0
Large diff (83 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/eq_bank.py +94 −0
Large diff (94 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/first_national.py +97 −0
Large diff (97 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/mcap.py +40 −0
Large diff (40 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/national_bank.py +131 −0
Large diff (131 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/rbc.py +112 −0
Large diff (112 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/scotiabank.py +104 −0
Large diff (104 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/tangerine.py +55 −0
Large diff (55 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/providers/td.py +115 −0
Large diff (115 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/scheduler.py +120 −0
Large diff (120 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/store.py +336 −0
Large diff (336 lines) is not loaded by default to keep the page fast.Load diff
added immoka/mortgage/validate.py +79 −0
Large diff (79 lines) is not loaded by default to keep the page fast.Load diff
added immoka/normalize.py +258 −0
Large diff (258 lines) is not loaded by default to keep the page fast.Load diff
added immoka/poi.py +248 −0
Large diff (248 lines) is not loaded by default to keep the page fast.Load diff
added immoka/quality.py +254 −0
Large diff (254 lines) is not loaded by default to keep the page fast.Load diff
added immoka/quartier.py +229 −0
Large diff (229 lines) is not loaded by default to keep the page fast.Load diff
added immoka/schema.py +153 −0
Large diff (153 lines) is not loaded by default to keep the page fast.Load diff
added immoka/seo.py +823 −0
Large diff (823 lines) is not loaded by default to keep the page fast.Load diff
added immoka/stats.py +1034 −0
Large diff (1,034 lines) is not loaded by default to keep the page fast.Load diff
added immoka/vraiprix_local.py +187 −0
Large diff (187 lines) is not loaded by default to keep the page fast.Load diff

Diff truncated — file too large.

Menu

Theme