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(monitoring+data): houseka et rentka deviennent les 10e et 11e services KA

- SERVICES += houseka, rentka : houseka avait deja son collecteur/modele/env
  (commit eb543c9) mais restait hors de la boucle de supervision et des
  routes ; rentka est integre de bout en bout (RentkaData, rentka_collector
  offset/2000 comme lou-ka, RENTKA_SOURCE_URL dans .env -> M4M36:8125,
  cadence attendue 1 h).
- connector_health poll desormais les 11 apps (houseka 17 sources,
  rentka ~680 sources au premier run).
- Docstrings 9 -> 11 services.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 27 days ago (Aug 28, 2026) parent eb543c9

7 changed files +43 −8

modified src/collectors/__init__.py +4 −2
@@ -6,7 +6,7 @@
6 6 # Contact : contact@spboucher.ai
7 7 # Date : 2026-08-16
8 8 # ============================================
9 −"""Collecteurs des 9 services KA et fabriques associées."""
9 +"""Collecteurs des 11 services KA et fabriques associées."""
10 10
11 11 from __future__ import annotations
12 12
@@ -19,6 +19,7 @@ from src.collectors.houseka_collector import HousekaCollector
19 19 from src.collectors.immoka_collector import ImmokaCollector
20 20 from src.collectors.jobka_collector import JobkaCollector
21 21 from src.collectors.louka_collector import LoukaCollector
22 +from src.collectors.rentka_collector import RentkaCollector
22 23 from src.collectors.restoka_collector import RestokaCollector
23 24 from src.collectors.sortika_collector import SortikaCollector
24 25
@@ -33,6 +34,7 @@ COLLECTORS: dict[str, type[BaseCollector]] = {
33 34 "sortika": SortikaCollector,
34 35 "creaka": CreakaCollector,
35 36 "jobka": JobkaCollector,
37 + "rentka": RentkaCollector,
36 38 }
37 39
38 40
@@ -49,5 +51,5 @@ def get_collector(service: str) -> BaseCollector:
49 51
50 52
51 53 def build_collectors() -> list[BaseCollector]:
52 − """Instancie les 9 collecteurs, dans l'ordre canonique des services."""
54 + """Instancie les 11 collecteurs, dans l'ordre canonique des services."""
53 55 return [cls() for cls in COLLECTORS.values()]
added src/collectors/rentka_collector.py +22 −0
@@ -0,0 +1,22 @@
1 +# ============================================
2 +# Projet : API-KA
3 +# Fichier : src/collectors/rentka_collector.py
4 +# Node : m3u96b
5 +# Author : Simon-Pierre Boucher
6 +# Contact : contact@spboucher.ai
7 +# Date : 2026-08-28
8 +# ============================================
9 +"""Collecteur quotidien du service rent-ka (table rentka_data)."""
10 +
11 +from __future__ import annotations
12 +
13 +from src.collectors.base_collector import BaseCollector
14 +
15 +
16 +class RentkaCollector(BaseCollector):
17 + """Collecte les données quotidiennes de rent-ka depuis RENTKA_SOURCE_URL."""
18 +
19 + service = "rentka"
20 + items_key = "listings"
21 + pagination = "offset"
22 + page_size = 2000 # même API que lou-ka (fork)
modified src/config.py +3 −0
@@ -30,6 +30,8 @@ SERVICES: tuple[str, ...] = (
30 30 "sortika",
31 31 "creaka",
32 32 "jobka",
33 + "houseka",
34 + "rentka",
33 35 )
34 36
35 37 SERVICE_TABLES: dict[str, str] = {service: f"{service}_data" for service in SERVICES}
@@ -45,6 +47,7 @@ SERVICE_SOURCE_ENV: dict[str, str] = {
45 47 "creaka": "CREAKA_SOURCE_URL",
46 48 "jobka": "JOBKA_SOURCE_URL",
47 49 "houseka": "HOUSEKA_SOURCE_URL",
50 + "rentka": "RENTKA_SOURCE_URL",
48 51 }
49 52
50 53 BASE_DIR = Path(__file__).resolve().parents[1]
modified src/database/models.py +7 −0
@@ -125,6 +125,12 @@ class HousekaData(DataRecordMixin, Base):
125 125 __tablename__ = "houseka_data"
126 126
127 127
128 +class RentkaData(DataRecordMixin, Base):
129 + """Données quotidiennes du service rent-ka."""
130 +
131 + __tablename__ = "rentka_data"
132 +
133 +
128 134 class JobkaData(DataRecordMixin, Base):
129 135 """Données quotidiennes du service job-ka."""
130 136
@@ -142,6 +148,7 @@ DATA_MODELS: dict[str, type[Base]] = {
142 148 "sortika": SortikaData,
143 149 "creaka": CreakaData,
144 150 "jobka": JobkaData,
151 + "rentka": RentkaData,
145 152 }
146 153
147 154
modified src/monitoring/connector_health.py +3 −2
@@ -6,7 +6,7 @@
6 6 # Contact : contact@spboucher.ai
7 7 # Date : 2026-08-18
8 8 # ============================================
9 −"""Supervision centralisée des connecteurs des 9 apps de l'écosystème KA.
9 +"""Supervision centralisée des connecteurs des 11 apps de l'écosystème KA.
10 10
11 11 Toutes les 2 h, le job lit le ``GET /api/stats`` de chaque app sœur (LAN,
12 12 lecture seule, timeout 5 s) et en déduit un état par source quand le détail
@@ -100,6 +100,7 @@ EXPECTED_CADENCE_HOURS: dict[str, float] = {
100 100 "creaka": 24,
101 101 "restoka": 168,
102 102 "jobka": 1,
103 + "rentka": 1,
103 104 }
104 105
105 106
@@ -571,7 +572,7 @@ def _load_rows(session: Any, service: str) -> dict[str, ConnectorHealth]:
571 572
572 573
573 574 def run_connector_health_check(now: float | None = None) -> dict[str, Any]:
574 − """Point d'entrée du job (toutes les 2 h) : vérifie les 9 apps.
575 + """Point d'entrée du job (toutes les 2 h) : vérifie les 11 apps.
575 576
576 577 L'échec d'une app (réseau, payload) ne fait jamais échouer le job.
577 578 """
modified src/scheduler/daily_job.py +3 −3
@@ -6,7 +6,7 @@
6 6 # Contact : contact@spboucher.ai
7 7 # Date : 2026-08-16
8 8 # ============================================
9 −"""Job quotidien (02:00, heure du node m3u96b) orchestrant les 9 collecteurs KA.
9 +"""Job quotidien (02:00, heure du node m3u96b) orchestrant les 11 collecteurs KA.
10 10
11 11 Les collecteurs s'exécutent en parallèle mais de façon indépendante :
12 12 l'échec d'un service ne bloque jamais les autres. Le backfill des 7 derniers
@@ -34,7 +34,7 @@ from src.utils.logger import alert, get_logger
34 34
35 35
36 36 def run_all(date_key: datetime.date | None = None) -> list[dict[str, Any]]:
37 − """Exécute les 9 collecteurs en parallèle puis le backfill des 7 derniers jours.
37 + """Exécute les 11 collecteurs en parallèle puis le backfill des 7 derniers jours.
38 38
39 39 Returns:
40 40 Résumés des runs du jour (un par service).
@@ -132,7 +132,7 @@ def main() -> None:
132 132 misfire_grace_time=3600,
133 133 )
134 134 # Supervision des connecteurs de l'écosystème (lecture seule des
135 − # /api/stats des 9 apps sœurs) : toutes les 2 h, premier passage 90 s
135 + # /api/stats des 11 apps sœurs) : toutes les 2 h, premier passage 90 s
136 136 # après le démarrage du scheduler.
137 137 scheduler.add_job(
138 138 run_connector_health_check,
modified src/utils/backup.py +1 −1
@@ -121,7 +121,7 @@ def backup_service(service: str, date_key: datetime.date | None = None) -> Path:
121 121
122 122
123 123 def backup_all(date_key: datetime.date | None = None) -> list[Path]:
124 − """Sauvegarde les 9 services ; un échec n'interrompt pas les autres."""
124 + """Sauvegarde les 11 services ; un échec n'interrompt pas les autres."""
125 125 paths: list[Path] = []
126 126 for service in SERVICES:
127 127 try:
128 128