SPB Git forge

spb/api-ka

Public

API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com

48commits 1branches 0releases
5.9 MBsize
maindefault branch
19 days agolast push
Python 60.9% HTML 21% TypeScript 7.3% JavaScript 5.2% CSS 4.8% Shell 0.8%

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
Simon-Pierre Boucher committed 27 days ago (Aug 28, 2026) parent ef1992b

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