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