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.
91 changed files +20,001 −0
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.