SPEC — Transformation lou-ka → Rent-Ka (LOCKED DECISIONS)
Rent-Ka = English rental-listings aggregator for ALL of Canada EXCEPT Québec (Ontario first — 5,861 ON listings already seeded in the node DB; national expansion via platform client registries comes right after launch). Sister sites: Lou-Ka (QC rentals), House-Ka (ROC homes for sale).
Identity
- Site name: Rent-Ka · wordmark Rent·Ka · domain www.rent-ka.com
BASE_URL = "https://www.rent-ka.com",SITE_NAME = "Rent-Ka"- FastAPI: title "Rent-Ka API", description "Rental listings aggregator — Canada (outside Québec)"
- Tagline (frontend + SEO): "Apartments and homes for rent across Canada."
- UA:
RentKaBot/1.0 (+https://www.rent-ka.com/bot; contact@spboucher.ai)(replaces every LouKaBot) - Language: en-CA everywhere (backend SEO strings, frontend copy,
<html lang="en-CA">) - Python package: rename
louka/→rentka/(all imports,uvicorn.run("rentka.web:app", ...)) - DB path:
data/rentka.db(db.py DB_PATH) - Env prefix:
LOUKA_*→RENTKA_*everywhere.LOUKA_ONTARIOgate is DELETED (not renamed).LOUKA_BASE_URL→RENTKA_BASE_URL. - kaid site id:
kaid.init("rent-ka"); hubfav URLhttps://www.rent-ka.com/listing/{uid}; KA_CLIENT_ID default "rent-ka". - ecosystem.json (frontend/src/ka/ecosystem.json): ADD entry (backend web.py:940/983 looks it up — required):
{"id":"rent-ka","wordmark":"Rent·Ka","domain":"www.rent-ka.com","accent":"#2456e6","accentSoft":"#e9efff","accentDeep":"#1738a8","onAccent":"#ffffff","tagline":"Rentals across Canada"}
Routes (English — backend seo.py/web.py AND frontend App.tsx/KAID_CONF must match)
- Listing page:
/listing/{uid}(was/logement/{uid}) - Cities index:
/cities(was/villes); city page:/city/{slug}and/city/{slug}/{type_slug} - Keep:
/stats,/sources,/contact,/bot,/g/{sourceId} /confidentialite→/privacy,/conditions→/terms/profil→/profile,/favoris→/favorites,/bienvenue→/welcome,/gestion→/manage,/passerelle/:uid→/gateway/:uid,/juste-valeur→/fair-value,/ka-scoresstays/ka-scores- DELETED routes/pages:
/court-terme*(short-term subsystem),/demenageurs(movers, QC dataset) - web.py
_CLIENT_ROUTES = {"profile","favorites","manage","welcome","bot","contact","fair-value","ka-scores"},_CLIENT_PREFIXES = ("u/","gateway/") - Sitemaps:
sitemap-pages.xml,sitemap-cities.xml,sitemap-listings-{n}.xml(drop sitemap-ct-*) - index.html KAID_CONF:
{card:"a[href^='/listing/']",detail:"^/listing/(.+)$",entity:"listing"}
Canonical unit types (English enum — replaces the QC "n½" system)
Studio | 1 bedroom | 2 bedrooms | 3 bedrooms | 4 bedrooms | 5+ bedrooms | Loft | Penthouse | Condo | Townhouse | House | Room
- normalize.normalize_unit_type: map French AND English input INTO this enum
(reverse of today:
3½→ "1 bedroom",4½→ "2 bedrooms",n½→(n-2) bedrooms,6½+→ "4 bedrooms"; "bachelor" → Studio; "maison de ville|townhouse|mdv" → Townhouse; "chambre|room" → Room) - bedrooms_from_unit_type: Studio/Loft→0, Room→1, "n bedroom(s)"→n, "5+ bedrooms"→5, Penthouse/Condo/Townhouse/House→None
- Slugs (seo.slugify): "1-bedroom", "2-bedrooms", "5-plus-bedrooms", "studio", "room", … (kill the
½ → -1-2special case) - quality.py rule 5:
unit != "Room"(was "Chambre")
Geography / scope
- Scope = Canada minus QC. quality.evaluate: DELETE
pause-ontariorule; ADDif d.get("province") == "QC": reasons.append("outside-scope-quebec"). - schema.Listing default province:
"ON"(connectors should set explicitly; platform registries gain an optional per-client"province"key, default "ON"). - schema.finalize + geocode: per-province bbox dict for all 12 (BC,AB,SK,MB,ON,NB,NS,PE,NL,YT,NT,NU), fallback = Canada bbox (41.6–83.2 N, −141.0…−52.5 E).
- geocode.py: REMOVE all Adresses Québec (MERN) code,
_BBOX_QUEBEC,_VILLES_QUEBEC; keep Nominatim (1 r/s) as primary;run_batch()becomes a loop over Nominatimresolve()(keep the batch CLI signature working). state= full province name from a PROVINCE_NAMES dict. Cache keys: prefix"{prov.lower()}|"for every province. - web.py
/api/stats: replace the_VILLES_GMMontréal buckets with per-province counts: response gains"provinces": {"ON": n, "BC": n, ...}(keep "total"/existing top-level keys the frontend Ticker needs — frontend Ticker will readprovinces). - web.py: expose
provincein listing JSON (list + detail + geojson props) and as a filter paramprovince=on /api/listings & /api/search; add it to /api/facets.
Features KEPT (translate UI strings to English where user-facing)
core pipeline (schema/db/ingest/normalize/dedup/quality/seo/web), fairvalue, kascores + environment + poi (pure OSM, national), building ("Building passport"), recycled ("Re-listed units"), history, imgcheck, managers (+serpapi_client), marketstats, statsdash, statsfiche (prune deleted-layer panels), kapdf (shared, keep verbatim), hiver.py → keep, rename user-facing label to "Winter Score" (works Canada-wide), KA ID/accounts/auth/profile/hubfav/hubprofile (site id rent-ka), fixtures.py, textmine.py → KEEP and make BILINGUAL: keep French regexes, add English equivalents for the high-value facts (heat/hydro/electricity/water included, parking, pets, furnished, laundry, A/C, elevator, balcony, dishwasher, no smoking, lease length, available date).
Features DELETED (backend module + web.py route + pdfgen section + statsextra block + frontend component)
- hydro.py (/api/hydro, HydroEstimation.tsx) — Hydro-Québec
- rdl.py (/api/rdl, RegistreLoyers.tsx) — QC rent registry
- tal.py (/api/tal, HistoriqueTAL.tsx) — QC tribunal
- gaz.py (/api/gaz, EssenceProche.tsx) — gazquebec
- air.py (/api/air, QualiteAir.tsx) — RSQAQ (maybe AQHI later)
- inondation.py (/api/inondation, RisqueInondation.tsx) — BDZI
- quartier.py (quartier block in /api/listings/{uid}, QuartierBlock.tsx) — QC census DB (rebuild later)
- commerces.py (/api/commerces, CommercesProches.tsx) + transit.db — QC banners/transit
- coutreel.py (/api/listings/{uid}/cout-reel, CoutReel.tsx) — backbone was Hydro-Québec
- demenageurs.py (+ /demenageurs SSR + Demenageurs.tsx + data/demenageurs.json)
- shortterm/ ENTIRELY (+ data/sources_ct.json, data/hotels_engines.json, ct_web router, seo ctdb import + /court-terme SSR + sitemap-ct, frontend Ct*.tsx, CourtTerme*.tsx, ctapi.ts, kamaps/ctAdapter.ts)
- MetroLignes.tsx usage in MapView.tsx + MapSearch.tsx (Montréal métro overlay)
- ingest.watch: remove hydro.precompute and tal.precompute steps (keep the rest incl. quartier→REMOVE too since module deleted)
Connectors
- KEEP the 65 ON connectors: remove the
_ONTARIO = ...line and everydisabled = not _ONTARIO/"disabled": not _ONTARIO(connector simply enabled). Platform generators (rentcafe/appfolio/rentsyncgw/buildium/liftsystem) keep reading their data/*.json registries; add support for optional per-client"province"(default "ON"). - KEEP + WIDEN the 11 national connectors (akelius, boardwalk, capreit, centurion, cogir, hazelview, interrent, metcap, minto, morguard, realstar): invert the gate — ROC is the default, DELETE the QC branches/city lists entirely; cover every non-QC province the landlord operates in (their sites list cities — keep existing ON lists, extend with the connector's own city discovery when it has one; set province per city).
- KEEP national portals (not gated): apartments_com, rentals_ca, zumper, kijiji, fb_marketplace + actors/. Change their default regions/cities env fallbacks from QC to ROC metros: Toronto, Ottawa, Mississauga, Hamilton, London, Kitchener-Waterloo, Windsor, Vancouver, Victoria, Surrey, Burnaby, Calgary, Edmonton, Winnipeg, Saskatoon, Regina, Halifax, Moncton, Fredericton, Charlottetown, St. John's. (env names now RENTKA_*)
- DELETE all other (~300) QC-only connector modules AND their data/sources.json entries.
sources.json keeps: the 65 ON entries (status back to "actif", note_pause removed),
the national/portal entries, minus all QC ones. Update
_comment. - KEEP helpers: base.py, _resilient.py, _detailutil.py; DELETE _livya.py/_planpoint.py if only QC connectors import them (check first).
- tests/: keep test_{normalize,quality,search,fairvalue,kascores,textmine}.py UPDATED to the new enum/rules; delete test fixtures + recorded cassettes of deleted connectors (tests/fixtures is 842 MB — purge everything belonging to deleted sources).
Frontend design (DIFFERENT from Lou-Ka orange and House-Ka pine/Fraunces)
- Keep the shared KA token system (frontend/src/ka/tokens.css UNTOUCHED — shared asset).
- styles.css overrides: accent = cobalt
--accent:#2456e6; --accent-soft:#e9efff; --accent-deep:#1738a8; --on-accent:#ffffff - Display font: "Bricolage Grotesque" (Google Fonts, weights 400..800) replaces Space Grotesk
in index.html +
--font-displayoverride in styles.css. Body stays Inter. - Header wordmark
Rent·Ka(the ·Ka styled with accent), tagline "Apartments and homes for rent across Canada." - The frontend should feel like its OWN site, not a translated Lou-Ka: new Home hero
(search + "Browse by province" section with the province counts from /api/stats.provinces,
featured cities grid), reordered nav, English MobileTabBar (Search/Map/Saved/Profile).
Keep the detail-page section ORDER standard (gallery → price/address → description →
amenities → practical details → analyses → map → KA Scores → footer; DOM order = visual
order on ALL breakpoints, NEVER
order:/column-reverse — Groupe KA standard 2026-08-19). - Listing page keeps (in order): gallery, price+FairValueBadge, description, amenities, practical details, PriceAnalysis, HistoriqueLouka→"Listing history", ImmeubleBloc→"Building passport", GestionnaireBloc→"Property manager", recycled block, ListingMap3D, KaScoresBlock, HiverScore→"Winter Score". All QC blocks removed (list above).
- Ticker: per-province counts (ON · BC · AB · …) from /api/stats.provinces.
- Footer: keep KaFooter with siteId="rent-ka" (entry added to ecosystem.json).
- ALL user-facing text in English (en-CA). Currency format
$1,495. Dates en-CA.
Deployment (for reference — done on node M4M36 afterwards)
- pm2: rent-ka-web (
run.py serve 8125), rent-ka-sync (run.py watch 60), rent-ka-ngrok (ngrok http --url=www.rent-ka.com 8125) - git origin gitsrv:srv/git/rent-ka.git