feat(house-ka): 10e service supervise + agent + CORS
- collecteur houseka (table houseka_data, HOUSEKA_SOURCE_URL M4M64b:8098) - supervision connector_health : cadence 4 h - KA Agent : catalogue de sites, DETAIL_URLS/FICHE_URLS (/property), recherche transversale (maisons Canada hors Quebec) - CORS : house-ka.com
7 changed files +39 −1
modified
src/api/main.py
+1 −1
@@ -78,7 +78,7 @@ app = FastAPI( | ||
| 78 | 78 | # CORS — le widget KA Agent appelle cette API depuis les 13 sites |
| 79 | 79 | app.add_middleware( |
| 80 | 80 | CORSMiddleware, |
| 81 | − allow_origins=['https://www.groupe-ka.com', 'https://groupe-ka.com', 'https://www.trouve-ka.com', 'https://trouve-ka.com', 'https://www.lou-ka.com', 'https://lou-ka.com', 'https://www.immo-ka.com', 'https://immo-ka.com', 'https://www.vrai-prix.com', 'https://vrai-prix.com', 'https://www.auto-ka.com', 'https://auto-ka.com', 'https://www.fabri-ka.com', 'https://fabri-ka.com', 'https://www.food-ka.com', 'https://food-ka.com', 'https://www.resto-ka.com', 'https://resto-ka.com', 'https://www.sorti-ka.com', 'https://sorti-ka.com', 'https://www.crea-ka.com', 'https://crea-ka.com', 'https://www.api-ka.com', 'https://api-ka.com', 'https://www.job-ka.com', 'https://job-ka.com'], | |
| 81 | + allow_origins=['https://www.groupe-ka.com', 'https://groupe-ka.com', 'https://www.trouve-ka.com', 'https://trouve-ka.com', 'https://www.lou-ka.com', 'https://lou-ka.com', 'https://www.immo-ka.com', 'https://immo-ka.com', 'https://www.house-ka.com', 'https://house-ka.com', 'https://www.vrai-prix.com', 'https://vrai-prix.com', 'https://www.auto-ka.com', 'https://auto-ka.com', 'https://www.fabri-ka.com', 'https://fabri-ka.com', 'https://www.food-ka.com', 'https://food-ka.com', 'https://www.resto-ka.com', 'https://resto-ka.com', 'https://www.sorti-ka.com', 'https://sorti-ka.com', 'https://www.crea-ka.com', 'https://crea-ka.com', 'https://www.api-ka.com', 'https://api-ka.com', 'https://www.job-ka.com', 'https://job-ka.com'], | |
| 82 | 82 | allow_methods=["GET", "POST"], |
| 83 | 83 | allow_headers=["content-type"], |
| 84 | 84 | ) |
modified
src/api/routes/agent.py
+5 −0
@@ -52,6 +52,7 @@ SITES: dict[str, dict[str, str]] = { | ||
| 52 | 52 | "trouve-ka": {"wordmark": "Trouve·Ka", "domain": "www.trouve-ka.com", "role": "le moteur de recherche du web québécois"}, |
| 53 | 53 | "lou-ka": {"wordmark": "Lou·Ka", "domain": "www.lou-ka.com", "role": "l'agrégateur des logements à louer"}, |
| 54 | 54 | "immo-ka": {"wordmark": "Immo·Ka", "domain": "www.immo-ka.com", "role": "l'agrégateur des propriétés à vendre"}, |
| 55 | + "house-ka": {"wordmark": "House·Ka", "domain": "www.house-ka.com", "role": "l'agrégateur des maisons à vendre du Canada hors Québec (site anglophone)"}, | |
| 55 | 56 | "vrai-prix": {"wordmark": "Vrai-Prix", "domain": "www.vrai-prix.com", "role": "l'estimateur de valeur résidentielle"}, |
| 56 | 57 | "auto-ka": {"wordmark": "Auto·Ka", "domain": "www.auto-ka.com", "role": "l'agrégateur des voitures usagées"}, |
| 57 | 58 | "fabri-ka": {"wordmark": "Fabri·Ka", "domain": "www.fabri-ka.com", "role": "le répertoire des produits québécois"}, |
@@ -167,6 +168,7 @@ async def _search_fuzzy(url: str, params: dict, keys: tuple[str, ...], | ||
| 167 | 168 | DETAIL_URLS: dict[str, str] = { |
| 168 | 169 | "lou-ka": "https://www.lou-ka.com/api/listings/{uid}", |
| 169 | 170 | "immo-ka": "https://www.immo-ka.com/api/listings/{uid}", |
| 171 | + "house-ka": "https://www.house-ka.com/api/listings/{uid}", | |
| 170 | 172 | "auto-ka": "https://www.auto-ka.com/api/vehicles/{uid}", |
| 171 | 173 | "food-ka": "https://www.food-ka.com/api/products/{uid}", |
| 172 | 174 | "fabri-ka": "https://www.fabri-ka.com/api/products/{uid}", |
@@ -186,6 +188,7 @@ def _uid(uid: str) -> str: | ||
| 186 | 188 | FICHE_URLS: dict[str, str] = { |
| 187 | 189 | "lou-ka": "https://www.lou-ka.com/logement/{uid}", |
| 188 | 190 | "immo-ka": "https://www.immo-ka.com/propriete/{uid}", |
| 191 | + "house-ka": "https://www.house-ka.com/property/{uid}", | |
| 189 | 192 | "auto-ka": "https://www.auto-ka.com/vehicule/{uid}", |
| 190 | 193 | "food-ka": "https://www.food-ka.com/produit/{uid}", |
| 191 | 194 | "fabri-ka": "https://www.fabri-ka.com/produits/{uid}", |
@@ -736,6 +739,8 @@ async def _run_tool(name: str, args: dict) -> Any: | ||
| 736 | 739 | ("items", "listings"), "https://www.lou-ka.com", 3, "lou-ka")), |
| 737 | 740 | ("immo-ka", "propriétés", _search_fuzzy("https://www.immo-ka.com/api/listings", {"q": q, "limit": 3}, |
| 738 | 741 | ("items", "listings"), "https://www.immo-ka.com", 3, "immo-ka")), |
| 742 | + ("house-ka", "maisons (Canada hors Québec)", _search_fuzzy("https://www.house-ka.com/api/listings", {"q": q, "limit": 3}, | |
| 743 | + ("items", "listings"), "https://www.house-ka.com", 3, "house-ka")), | |
| 739 | 744 | ("auto-ka", "véhicules", _search_fuzzy("https://www.auto-ka.com/api/vehicles", {"q": q, "limit": 3}, |
| 740 | 745 | ("items", "vehicles"), "https://www.auto-ka.com", 3, "auto-ka")), |
| 741 | 746 | ("job-ka", "emplois", _search_fuzzy("https://www.job-ka.com/api/jobs", {"q": q, "limit": 3}, |
modified
src/collectors/__init__.py
+2 −0
@@ -15,6 +15,7 @@ from src.collectors.base_collector import BaseCollector | ||
| 15 | 15 | from src.collectors.creaka_collector import CreakaCollector |
| 16 | 16 | from src.collectors.fabrika_collector import FabrikaCollector |
| 17 | 17 | from src.collectors.foodka_collector import FoodkaCollector |
| 18 | +from src.collectors.houseka_collector import HousekaCollector | |
| 18 | 19 | from src.collectors.immoka_collector import ImmokaCollector |
| 19 | 20 | from src.collectors.jobka_collector import JobkaCollector |
| 20 | 21 | from src.collectors.louka_collector import LoukaCollector |
@@ -24,6 +25,7 @@ from src.collectors.sortika_collector import SortikaCollector | ||
| 24 | 25 | COLLECTORS: dict[str, type[BaseCollector]] = { |
| 25 | 26 | "louka": LoukaCollector, |
| 26 | 27 | "immoka": ImmokaCollector, |
| 28 | + "houseka": HousekaCollector, | |
| 27 | 29 | "foodka": FoodkaCollector, |
| 28 | 30 | "autoka": AutokaCollector, |
| 29 | 31 | "fabrika": FabrikaCollector, |
added
src/collectors/houseka_collector.py
+22 −0
@@ -0,0 +1,22 @@ | ||
| 1 | +# ============================================ | |
| 2 | +# Projet : API-KA | |
| 3 | +# Fichier : src/collectors/houseka_collector.py | |
| 4 | +# Node : m3u96b | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Date : 2026-08-27 | |
| 8 | +# ============================================ | |
| 9 | +"""Collecteur quotidien du service house-ka (table houseka_data).""" | |
| 10 | + | |
| 11 | +from __future__ import annotations | |
| 12 | + | |
| 13 | +from src.collectors.base_collector import BaseCollector | |
| 14 | + | |
| 15 | + | |
| 16 | +class HousekaCollector(BaseCollector): | |
| 17 | + """Collecte les données quotidiennes de house-ka depuis HOUSEKA_SOURCE_URL.""" | |
| 18 | + | |
| 19 | + service = "houseka" | |
| 20 | + items_key = "listings" | |
| 21 | + pagination = "offset" | |
| 22 | + page_size = 2000 # même API que immo-ka (fork) | |
modified
src/config.py
+1 −0
@@ -44,6 +44,7 @@ SERVICE_SOURCE_ENV: dict[str, str] = { | ||
| 44 | 44 | "sortika": "SORTIKA_SOURCE_URL", |
| 45 | 45 | "creaka": "CREAKA_SOURCE_URL", |
| 46 | 46 | "jobka": "JOBKA_SOURCE_URL", |
| 47 | + "houseka": "HOUSEKA_SOURCE_URL", | |
| 47 | 48 | } |
| 48 | 49 | |
| 49 | 50 | BASE_DIR = Path(__file__).resolve().parents[1] |
modified
src/database/models.py
+7 −0
@@ -119,6 +119,12 @@ class CreakaData(DataRecordMixin, Base): | ||
| 119 | 119 | __tablename__ = "creaka_data" |
| 120 | 120 | |
| 121 | 121 | |
| 122 | +class HousekaData(DataRecordMixin, Base): | |
| 123 | + """Données quotidiennes du service house-ka.""" | |
| 124 | + | |
| 125 | + __tablename__ = "houseka_data" | |
| 126 | + | |
| 127 | + | |
| 122 | 128 | class JobkaData(DataRecordMixin, Base): |
| 123 | 129 | """Données quotidiennes du service job-ka.""" |
| 124 | 130 | |
@@ -128,6 +134,7 @@ class JobkaData(DataRecordMixin, Base): | ||
| 128 | 134 | DATA_MODELS: dict[str, type[Base]] = { |
| 129 | 135 | "louka": LoukaData, |
| 130 | 136 | "immoka": ImmokaData, |
| 137 | + "houseka": HousekaData, | |
| 131 | 138 | "foodka": FoodkaData, |
| 132 | 139 | "autoka": AutokaData, |
| 133 | 140 | "fabrika": FabrikaData, |
modified
src/monitoring/connector_health.py
+1 −0
@@ -92,6 +92,7 @@ STALE_FACTOR = 2 | ||
| 92 | 92 | EXPECTED_CADENCE_HOURS: dict[str, float] = { |
| 93 | 93 | "louka": 1, |
| 94 | 94 | "immoka": 4, |
| 95 | + "houseka": 4, | |
| 95 | 96 | "autoka": 2, |
| 96 | 97 | "foodka": 6, |
| 97 | 98 | "fabrika": 6, |
| 98 | 99 | |