SPB Git forge

spb/ka-ui

Public
30commits 1branches 0releases
145.7 MBsize
maindefault branch
27 days agolast push
Python 33.5% JavaScript 30.1% TypeScript 25% CSS 10% Shell 1.4%
14.1 KB

# ka-stats — module Stats commun Groupe KA (spec v3)

Contrat partagé par les plateformes pour leurs pages /stats (tableau de bord analytique) et les exports PDF estampillés Groupe-KA. Le visuel suit le design system ka-ui (tokens.css) avec l'accent de la marque.

v2 (2026-08-19) : sparklines dans les KPI, jauges, multi-courbes, barres empilées, distributions (histogrammes), heatmap horaire 7×24, deltas sur les répartitions, statistiques de séries (min/max/moy/méd/σ), et 5 rapports PDF au lieu de 2. Tous les nouveaux champs sont optionnels : un dashboard v1 reste valide et se rend tel quel.

v3 (2026-08-23) : rapports personnalisés — l'utilisateur compose son propre rapport PDF bloc par bloc : choix des données (catalogue dérivé du dashboard), du rendu par bloc (courbe/aire/barres/anneau/histogramme/ heatmap/tableau…), de l'ordre, avec modèles sauvegardés (localStorage du site). Deux endpoints (/api/stats/catalog, POST /api/stats/report/custom, voir §3bis) + un constructeur dans la page /stats (ReportBuilder du kit — bouton « 🛠 Rapport personnalisé » à côté du menu PDF). Le PDF garde le gabarit estampillé Groupe-KA avec l'accent de la plateforme. v2 inchangée.

# 1. Page /stats — structure obligatoire (dans cet ordre)

  1. Bandeau KPI : 6–10 grandes cartes (KpiCard) — valeur, libellé, variation vs période précédente (▲/▼ + %, vert --green / rouge --danger), sparkline de tendance quand une série existe.
  2. Sélecteur de période global (PeriodSelector) : aujourd'hui · 7 j · 30 j · 3 m · 6 m · 12 m · année en cours · tout + plage personnalisée (2 champs date). Toute la page se recalcule (state → refetch dashboard).
  3. Jauges (GaugeCard) quand pertinent : taux, couvertures, complétude.
  4. Graphiques d'évolution : courbes/aires (LineChart, survol = infobulle, légende cliquable, comparaison N vs N-1 en pointillé), barres verticales (VBarChart — volumes quotidiens), multi-courbes ≤ 4 séries (MultiLineChart — motifs de trait distincts, jamais la couleur seule), barres empilées (StackedBarChart — composition dans le temps). Sous les courbes clés : StatSummary (min/max/moyenne/médiane/écart-type).
  5. Répartitions : barres horizontales (BarChart, deltas optionnels), anneaux (Donut), distributions/histogrammes (Histogram).
  6. Répartition géographique (par ville/région) — barres horizontales triées (pas besoin de vraie carte).
  7. Calendriers : CalendarHeatmap (26 semaines) et, quand l'activité horaire est journalisée, HourHeatmap (7 jours × 24 h).
  8. Tableaux détaillés (DataTable) : tri par colonne, recherche interne, pagination (25/pg), débordement horizontal propre sur mobile (.tbl-wrap). Viser 3 à 5 tableaux par plateforme.
  9. Records & faits marquants : générés depuis les données (jour record, plus forte croissance, meilleure entrée…) — cartes compactes, 6–12.
  10. Fraîcheur : « Mis à jour le {date heure} » + bouton Rafraîchir.
  11. Bouton PDF bien visible en haut (PdfButton) : bouton principal « Rapport PDF complet » + menu « Autres rapports ▾ » listant les 5 rapports (voir §3). Indicateur de progression si > 2 s.

Responsive : KPI empilés < 768 px, graphiques pleine largeur redimensionnés (SVG viewBox), tableaux en défilement horizontal contenu, tactile ≥ 44 px. AUCUNE donnée inventée : une stat indisponible = bloc « Pas encore mesuré » (carte grise propre), jamais un faux chiffre.

Accessibilité (règles fermes) : un seul axe Y par graphique (jamais de double échelle) ; ≥ 2 séries ⇒ légende obligatoire ; l'identité d'une série multi-courbes passe par le motif de trait en plus de la couleur ; le texte reste en encre (jamais coloré à la couleur de série) ; chaque graphique a son infobulle de survol et un équivalent tableau existe.

# 2. API — contrat commun

GET /api/stats/dashboard?period=7j|30j|3m|6m|12m|annee|tout|auj&from=YYYY-MM-DD&to=YYYY-MM-DD

jsonc
{
  "updated": "2026-08-19T01:00:00-04:00",
  "period": { "from": "2026-07-20", "to": "2026-08-19", "label": "30 jours" },
  "kpis": [ { "id": "total", "label": "Annonces actives", "value": 33744,
              "unit": "", "delta_pct": 4.2, "direction": "up",
              "spark": [{ "t": "2026-08-01", "v": 31200 }] } ],   // spark optionnel
  "gauges": [ { "id": "geo", "label": "Fiches géolocalisées", "value": 92,
                "max": 100, "unit": "%" } ],                       // optionnel
  "series": [ { "id": "vol", "title": "Annonces actives par jour", "unit": "annonces",
                "kind": "line",                    // line | bar | area
                "points": [{ "t": "2026-07-20", "v": 31200 }],
                "compare": [{ "t": "2025-07-20", "v": 24100 }] } ],
  "multiseries": [ { "id": "seg", "title": "Prix médian par taille", "unit": "$",
                     "series": [ { "label": "3½", "points": [/* … */] },
                                 { "label": "4½", "points": [/* … */] } ] } ], // ≤ 4
  "stacked": [ { "id": "src", "title": "Ajouts par source", "unit": "ajouts",
                 "keys": ["Kijiji", "Centris", "Autres"],
                 "points": [{ "t": "2026-08-01", "values": [120, 80, 30] }] } ],
  "breakdowns": [ { "id": "types", "title": "Par type", "kind": "donut",  // donut | bars
                    "items": [{ "label": "4½", "value": 9120, "delta_pct": 2.1 }] } ],
  "distributions": [ { "id": "prix", "title": "Distribution des loyers", "unit": "annonces",
                       "bins": [{ "label": "800-1000$", "value": 3120 }] } ],
  "geo": { "title": "Par région", "items": [{ "label": "Montréal", "value": 15680 }] },
  "heatmap": { "title": "Activité", "cells": [{ "date": "2026-08-01", "value": 210 }] },
  "hourly": { "title": "Activité par heure",
              "cells": [{ "dow": 0, "hour": 9, "value": 40 }] },   // dow 0=lun … 6=dim
  "tables": [ { "id": "top", "title": "Top villes", "columns": ["Ville", "Annonces", "Δ 30 j"],
                "rows": [["Montréal", 15680, "+3,1 %"]] } ],
  "records": [ { "label": "Jour record d'ajouts", "value": "412 annonces", "date": "2026-08-09" } ]
}

Champs absents = section masquée. Cache serveur recommandé (≥ 5 min par période). Les valeurs proviennent des données réelles (DB de la plateforme, journaux de sync des connecteurs, /api/v1/runs d'API-KA…). Arrondir les delta_pct à 1 décimale côté serveur.

# 3. Rapports PDF — 5 modes

GET /api/stats/report?period=…&from=&to=&mode=complet|synthese|tendances|repartitions|donnees → application/pdf, en-tête Content-Disposition: attachment; filename= groupe-ka_<plateforme>_stats_<periode>[_<mode>]_<YYYY-MM-DD>.pdf (pas de suffixe pour complet — rétrocompatible v1 ; kapdf.filename() accepte maintenant mode en 3e argument).

Mode Contenu
complet tout : sommaire, KPI, jauges, séries + stats de séries, multi-séries, empilées, répartitions, distributions, géo, heatmap horaire, tableaux (200 lignes), records
synthese couverture + KPI + jauges + records (2–3 pages)
tendances KPI + toutes les séries temporelles + min/max/moy/méd/σ + records
repartitions breakdowns, distributions, géo, activité horaire
donnees tous les tableaux en version longue (400 lignes)

Un mode inconnu retombe sur complet. Le gabarit (implémentations : kapdf.py fpdf2 pour les apps Python ; les apps Next portent le même gabarit en pdfkit) :

  • Couverture : cadre encre, kicker « GROUPE KA · RAPPORT STATISTIQUE », wordmark de la plateforme (boîte encre + accent), type de rapport, période couverte, date/heure de génération, bande encre au pied avec « par Groupe KA — groupe-ka.com ».
  • Sommaire avec numéros de pages (modes complet et donnees).
  • KPI : grille de cartes (bordure encre, valeur en gros, delta coloré arrondi à 1 décimale). Jauges : demi-arcs accent.
  • Graphiques VECTORIELS (dessinés en primitives, jamais de capture) : courbes/aires, barres verticales, multi-courbes (motifs distincts), empilées (nuances d'accent), anneaux, heatmap horaire — accent de la plateforme, axes/graduations encre.
  • Tableaux paginés proprement (lignes zébrées --surface-2, jamais coupés en deux à cheval sur une ligne).
  • Records puis page de fin : coordonnées Groupe KA (3 courriels + rôles d'ecosystem.json, groupe-ka.com), avertissement d'agrégateur, mentions légales courtes.
  • Chaque page : en-tête discret (« Groupe KA · {Plateforme} », filet encre) + pied (« © Groupe-KA — {année} — groupe-ka.com · {période} · p. X/Y »).
  • A4 portrait, marges 18 mm, typo : Helvetica (fallback sûr) ou fonts TTF du DS si présentes.

# 3bis. Rapports personnalisés (v3)

# Catalogue

GET /api/stats/catalog?period=…&from=&to= →

jsonc
{ "updated": "…", "period": { … },
  "blocks": [ { "key": "series:ajouts",      // section[:id] — clé stable
                "section": "series",
                "title": "Événements ajoutés par jour",
                "renders": ["line","area","bar","table"], // rendus compatibles
                "default_render": "line",
                "count": 30 } ] }             // taille indicative (optionnel)

Sections → rendus : kpis cards|table · gauges gauges|table · series:<id> line|area|bar|table · multiseries:<id> lines|table · stacked:<id> stacked|table · breakdowns:<id> donut|bars|table · distributions:<id> histogram|table · geo bars|table · heatmap heatmap|table · hourly heatmap|table · tables:<id> table · records cards|table. Toute donnée a un équivalent tableau. Le catalogue est dérivé du dashboard (implémentation : kapdf.catalog(dash) / catalogFromDashboard() en TS) — zéro maintenance quand une métrique s'ajoute.

# Génération

POST /api/stats/report/custom — corps JSON :

jsonc
{ "title": "Revue mensuelle",                 // ≤ 80 car., affiché en couverture
  "period": "30j", "from": "", "to": "",     // mêmes règles que le dashboard
  "blocks": [ { "key": "kpis", "render": "cards" },
              { "key": "series:ajouts", "render": "bar" } ] }  // ordre = ordre du PDF

→ application/pdf, filename groupe-ka_<plateforme>_stats_<periode>_personnalise_<date>.pdf. Clés inconnues ignorées ; rendu incompatible → rendu par défaut ; aucun bloc valide → 400. Maximum 40 blocs. Couverture : type = « Rapport personnalisé — {title} » ; sommaire ; page de fin habituelle. Un même bloc peut apparaître plusieurs fois (ex. graphique + tableau).

# Constructeur (front, kit)

ReportBuilder (kacharts.tsx ; port vanilla pour les SPA sans React) : panneau modal 2 colonnes — catalogue groupé par section à gauche, composition ordonnée à droite (↑ ↓ ✕, sélecteur de rendu par bloc, titre). Modèles : sauvegarde/chargement/suppression nommés en localStorage (clé ka-stats-rapports, propre à l'origine du site). Bouton « Générer le PDF » → POST + téléchargement blob. États busy/erreur propres, tactile ≥ 44 px, z-index var(--z-modal, 900).

# 4. Spécifique par plateforme (sections métier attendues)

  • groupe-ka : tableau de bord maître — consolidation des plateformes (volume total, croissance), classement, bloc résumé par plateforme + lien vers sa page /stats ; « Rapport écosystème complet » = PDF consolidé.
  • lou-ka : annonces actives/nouvelles/retirées, loyers moyens/médians par ville & taille (multiseries), distribution des loyers, évolution, répartition par type, top villes, sources (stacked).
  • immo-ka : annonces actives/nouvelles/vendues-retirées, prix moyen/médian par ville/région/type (multiseries), distribution des prix, délai de présence, top villes, tension du marché.
  • vrai-prix : couverture du rôle (unités, valeur totale), estimations servies si journalisées, répartitions par municipalité/type, distribution des valeurs, indices marché.
  • auto-ka : volume par marque/modèle/année/carburant/boîte, prix moyens et km moyens par segment (multiseries), distributions prix/km/année, top marques/modèles.
  • fabri-ka : produits par catégorie/région/boutique, fourchettes de prix (distribution), nouveautés par période, top catégories.
  • food-ka : produits suivis, relevés de prix, soldes détectés (baisses/hausses, amplitude — stacked), top produits en solde, prix moyens par catégorie, distribution des rabais.
  • resto-ka : restos par cuisine/ville/gamme, menus & plats, distribution des prix de plats, nouveautés/fermetures détectées, top établissements.
  • sorti-ka : événements à venir/passés par catégorie/ville, gratuits vs payants (stacked), heatmap calendrier + horaire, top lieux.
  • crea-ka : créateurs par plateforme/niche/tier, comptes reliés (stacked par plateforme), distribution des audiences, top créateurs, croissance du répertoire.
  • trouve-ka : pages indexées, domaines, rythme de crawl (indexées/h — hourly), erreurs, file frontier, tendances des requêtes journalisées.
  • api-ka : appels par endpoint/jour/heure (hourly), latences moyennes + p95 (multiseries), taux d'erreur, top endpoints/clés, uptime.
  • job-ka : offres actives/nouvelles/expirées par catégorie/ville/ entreprise, distribution des salaires affichés, top employeurs.
  • Transverse (tous) : volume total agrégé + croissance, connecteurs actifs et éléments ajoutés/mis à jour par période (journaux de sync — stacked par source quand possible), complétude/fraîcheur moyenne des fiches quand mesurable (jauges). Trafic web : seulement si des journaux d'accès existent — sinon état vide propre.

# 5. Ajouter une métrique / un graphique / une plateforme

1 métrique = 1 entrée kpis[], series[], multiseries[], stacked[], distributions[] ou gauges[] côté API (requête SQL agrégée + cache) — le front la rend automatiquement. 1 plateforme = implémenter les 2 endpoints du contrat + une page /stats montée sur les composants du kit + kapdf.py (ou gabarit pdfkit) branché sur le même JSON de dashboard.