SPB Git

spb/sorti-ka

Public

Toutes les sorties et tous les événements du Québec, un seul endroit — 7 connecteurs, fiches SSR, design Groupe KA.

docs: README à jour avec screenshot

Simon-Pierre Boucher committed (Aug 18, 2026) parent d614ca6 Browse files

2 changed files +79 −90

modified README.md +79 −90
@@ -4,61 +4,57 @@
4 Desc: Documentation du dépôt Sorti-Ka — agrégateur de sorties & événements 4 Desc: Documentation du dépôt Sorti-Ka — agrégateur de sorties & événements
5 --> 5 -->
6 6
7−<div align="center">
8−
9 # Sorti·Ka 7 # Sorti·Ka
10 8
11−### Toutes les sorties et tous les événements du Québec. Un seul endroit. 9+### **Toutes les sorties et tous les événements du Québec. Un seul endroit.**
12 10
13 **[www.sorti-ka.com](https://www.sorti-ka.com)** 11 **[www.sorti-ka.com](https://www.sorti-ka.com)**
14 12
15−![Python](https://img.shields.io/badge/Python-3.14-141814?style=for-the-badge&logo=python&logoColor=d9f26b) 13+![Aperçu de Sorti-Ka](docs/screenshot.png)
16−![FastAPI](https://img.shields.io/badge/FastAPI-API_+_SSR-141814?style=for-the-badge&logo=fastapi&logoColor=d9f26b)
17−![SQLite](https://img.shields.io/badge/SQLite-storage-141814?style=for-the-badge&logo=sqlite&logoColor=d9f26b)
18−![Frontend](https://img.shields.io/badge/Frontend-SPA_sans_build-141814?style=for-the-badge&logo=html5&logoColor=d9f26b)
19−
20−![Événements](https://img.shields.io/badge/%C3%A9v%C3%A9nements_actifs-15_231-1c5c41?style=flat-square)
21−![À venir](https://img.shields.io/badge/%C3%A0_venir-11_559-1c5c41?style=flat-square)
22−![Gratuits](https://img.shields.io/badge/gratuits-3_159-1c5c41?style=flat-square)
23−![Connecteurs](https://img.shields.io/badge/connecteurs_actifs-8-1c5c41?style=flat-square)
24−![Régions](https://img.shields.io/badge/r%C3%A9gions-17%2F17-1c5c41?style=flat-square)
25−![Villes](https://img.shields.io/badge/villes-428-1c5c41?style=flat-square)
26−![Tests](https://img.shields.io/badge/tests-19_pass-1c5c41?style=flat-square)
27−
28−*Agrégateur indépendant de sorties & événements — concerts, festivals, spectacles,
29−expositions, sport, famille — dans les 17 régions administratives du Québec, avec
30−dates, lieux, gratuité, fiche par événement et lien direct vers la billetterie ou
31−la source originale. Mise à jour horaire, automatiquement.*
32−
33−<img src="docs/screenshots/accueil.png" alt="Page d'accueil Sorti-Ka" width="920">
34−
35−</div>
36 14
37 --- 15 ---
38 16
39−## Pourquoi Sorti·Ka ? 17+## Description
18+
19+**Sorti·Ka** est un **agrégateur indépendant de sorties et d'événements** — concerts, festivals, spectacles, expositions, sport, famille — couvrant les **17 régions administratives du Québec**, avec dates, lieux, gratuité, fiche par événement et **lien direct vers la billetterie ou la source originale**. Mise à jour **horaire**, automatiquement.
40 20
41−Trouver quoi faire au Québec, c'est ouvrir dix sites : les billetteries ne montrent 21+Trouver quoi faire au Québec, c'est ouvrir dix sites : les billetteries ne montrent que leurs propres spectacles, les calendriers municipaux que leur ville, les sites touristiques que leurs membres. **Sorti·Ka retourne le problème** : un **connecteur dédié par source** visite chaque calendrier, normalise chaque événement vers un **schéma unique**, **déduplique les doublons inter-sources** et détecte les changements en continu.
42−que leurs propres spectacles, les calendriers municipaux que leur ville, les sites
43−touristiques que leurs membres. **Sorti·Ka retourne le problème** : un connecteur
44−dédié par source visite chaque calendrier, normalise chaque événement vers un
45−schéma unique, déduplique les doublons inter-sources et détecte les changements
46−en continu.
47 22
48−Sorti·Ka n'est pas une billetterie : c'est un **index fidèle**. Aucun prix inventé, 23+Sorti·Ka **n'est pas une billetterie** : c'est un **index fidèle**. Aucun prix inventé, aucune date devinée — chaque fiche est **attribuée à sa source** et renvoie vers la fiche originale pour les billets. Membre de la famille **·Ka** ([Lou·Ka](https://www.lou-ka.com), Immo·Ka, Auto·Ka, Food·Ka, Fabri·Ka, Resto·Ka…), architecture calquée sur le **patron de référence Lou·Ka**.
49−aucune date devinée — chaque fiche est attribuée à sa source et renvoie vers la
50−fiche originale pour les billets. Membre de la famille **·Ka**
51−([Lou·Ka](https://www.lou-ka.com), Immo·Ka, Auto·Ka, Food·Ka, Fabri·Ka, Resto·Ka),
52−architecture calquée sur le patron de référence Lou·Ka.
53 24
54−## Visite guidée 25+## Fonctionnalités
55 26
56−| | | 27+- **Recherche filtrée** — région, catégorie, période, **gratuité**, texte libre, tri et pagination
57−|:---:|:---:| 28+- **Fiche par événement** — dates, lieu, prix, carte, événements similaires, **SSR + JSON-LD Event** pour le SEO
58−| **Recherche filtrée** — région, catégorie, période, gratuité, texte libre | **Fiche par événement** — dates, lieu, prix, carte, similaires, SSR + JSON-LD | 29+- **8 connecteurs actifs** — **~15 000+ événements actifs**, **428 villes**, **17/17 régions** :
59−| <img src="docs/screenshots/accueil.png" alt="Accueil" width="440"> | <img src="docs/screenshots/fiche.png" alt="Fiche événement" width="440"> |
60 30
61−## L'architecture en 30 secondes 31+| Source | Type / accès | Extraction |
32+|---|---|---|
33+| **Le point de vente** | billetterie QC — robots `Allow: *` | listes HTML paginées (~2 500 événements, toute la province) |
34+| **SIT Québec** (Ministère du Tourisme) | données ouvertes CC 4.0 | API JSON Tourinsoft (~1 600 événements, toutes les régions) |
35+| **Ville de Montréal** | données ouvertes CC BY 4.0 | API CKAN datastore (~5 600 événements) |
36+| **evenko** | promoteur — robots `Allow: /api/search*` | proxy Algolia (~460 grands concerts : Centre Bell, MTELUS…) |
37+| **La Vitrine** | Content-Signal `search=yes` | sitemap + fiches HTML, cache incrémental (~5 100 représentations) |
38+| **Atuvu.ca** | Cloudflare → **Scrapfly** | sitemaps lastmod + fiches, rattrapage progressif |
39+| **Ville de Sherbrooke** | calendrier municipal public | JSON embarqué — 1 requête (~270 événements) |
40+| **Ville de Laval** | données ouvertes CC 4.0 | JSON officiel (~290 activités) |
41+
42+- **Déduplication inter-sources** — empreinte titre + ville + date : un même concert publié par 3 sources = **une seule carte**, la fiche la plus riche gagne
43+- **Répertoire des municipalités MAMH** — **1 250 villes** + arrondissements + alias, homonymes résolus par population
44+- **Persistance robuste** — upsert par hash de contenu, **délai de grâce 2 syncs**, alerte de dérive, historique de syncs
45+- **Page /stats** — tableau de bord analytique + **rapport PDF Groupe-KA**
46+- **Widget KA Agent** — bulle de chat Groupe KA intégrée au frontend
47+- **19 tests** sur fixtures réelles, **zéro réseau**
48+
49+## Stack technique
50+
51+- **Python 3.14** + **FastAPI** — API JSON + rendu SSR SEO
52+- **SQLite** — stockage, diff et historique de synchronisation
53+- **Frontend SPA sans build** — HTML/JS, routeur History API, design system **Groupe KA « éditorial sharp »** (ka-ui)
54+- **PM2** + **ngrok** — exécution résiliente et exposition publique
55+- **Scrapfly** — contournement Cloudflare pour Atuvu (clé `SCRAPFLY_API_KEY` dans `.env`)
56+
57+## Structure du projet
62 58
63 ``` 59 ```
64 connecteurs (1 module / source) normalisation (Event) SQLite (diff + grâce) 60 connecteurs (1 module / source) normalisation (Event) SQLite (diff + grâce)
@@ -71,32 +67,28 @@ architecture calquée sur le patron de référence Lou·Ka.
71 67
72 | Couche | Rôle | Fichiers | 68 | Couche | Rôle | Fichiers |
73 |---|---|---| 69 |---|---|---|
74−| **Connecteurs** | 1 module Python par source : API JSON internes (Algolia evenko, Tourinsoft, CKAN), listes HTML, Scrapfly (Cloudflare), caches incrémentaux | `sortika/connectors/*.py` | 70+| **Connecteurs** | 1 module Python par source : API JSON internes (Algolia evenko, Tourinsoft, CKAN), listes HTML, Scrapfly, caches incrémentaux | `sortika/connectors/*.py` |
75 | **Schéma** | `Event` standardisé : titre, catégories canoniques, lieu, ville, **région (17)**, lat/lng, dates ISO, gratuité/prix avec `price_label` source | `sortika/schema.py` | 71 | **Schéma** | `Event` standardisé : titre, catégories canoniques, lieu, ville, **région (17)**, lat/lng, dates ISO, gratuité/prix avec `price_label` source | `sortika/schema.py` |
76−| **Normalisation** | dates FR/ISO, prix, catégories (14), villes, entités HTML | `sortika/normalize.py` | 72+| **Normalisation** | dates FR/ISO, prix, **14 catégories**, villes, entités HTML | `sortika/normalize.py` |
77−| **Régions** | Répertoire des municipalités MAMH : 1 250 villes + arrondissements + alias, homonymes résolus par population | `sortika/regions.py` + `data/villes_regions.json` | 73+| **Régions** | Répertoire MAMH : 1 250 villes + arrondissements + alias | `sortika/regions.py` + `data/villes_regions.json` |
78−| **Persistance** | upsert par hash de contenu, délai de grâce 2 syncs, alerte de dérive, historique de syncs | `sortika/db.py` | 74+| **Persistance** | upsert par hash, délai de grâce, alerte de dérive | `sortika/db.py` |
79−| **Dédup** | empreinte titre + ville + date : un même concert publié par 3 sources = **une carte**, la fiche la plus riche gagne | `schema.dedup_key()` + `web.py` | 75+| **Dédup** | empreinte titre + ville + date | `schema.dedup_key()` + `web.py` |
80−| **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` servies avec `<head>` unique + **JSON-LD Event**, robots, sitemaps | `sortika/web.py` + `sortika/seo.py` | 76+| **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` avec `<head>` unique + **JSON-LD Event**, robots, sitemaps | `sortika/web.py` + `sortika/seo.py` |
81−| **Frontend** | SPA sans build, routeur History API, design system **Groupe KA « éditorial sharp »** | `frontend/index.html` | 77+| **Frontend** | SPA sans build, design system **Groupe KA** | `frontend/index.html` |
82− 78+
83−## Les 8 connecteurs actifs 79+### API
84− 80+
85−| Source | Type / accès | Extraction | Volume | 81+| Route | Description |
86−|---|---|---|---| 82+|---|---|
87−| **Le point de vente** | billetterie QC — robots `Allow: *` | listes HTML paginées (~65 pages) | ~2 500 concerts, humour, théâtre — toute la province | 83+| `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; **dédupliquée inter-sources** |
88−| **SIT Québec** (Ministère du Tourisme) | données ouvertes CC 4.0 | API JSON Tourinsoft | ~1 600 événements touristiques, toutes les régions | 84+| `GET /api/events/{uid}` | fiche complète d'un événement |
89−| **Ville de Montréal** | données ouvertes CC BY 4.0 | API CKAN datastore | ~5 600 événements municipaux | 85+| `GET /api/stats` | volumes par région et par catégorie |
90−| **evenko** | promoteur — robots `Allow: /api/search*` | proxy Algolia percé (`body=base64({params:[q,opts],lang})`) | ~460 grands concerts (Centre Bell, Place Bell, MTELUS…) | 86+| `GET /api/sources` | registre des sources + événements actifs + dernière synchro |
91−| **La Vitrine** | Content-Signal `search=yes, use=reference` | sitemap + fiches HTML (dates de tournée dédupliquées par id de représentation), cache incrémental | ~5 100 représentations | 87+| `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) |
92−| **Atuvu.ca** | Cloudflare → **Scrapfly**, robots `Allow: /` | sitemaps lastmod + fiches, cache incrémental plafonné | rattrapage progressif | 88+| `GET /sitemap.xml` · `/robots.txt` | référencement |
93−| **Ville de Sherbrooke** | calendrier municipal public | JSON embarqué `TRANSFER_CACHE` — 1 requête | ~270 événements avec images et gratuité | 89+| `GET /docs` | Swagger |
94−| **Ville de Laval** | données ouvertes CC 4.0 | JSON officiel | ~290 activités | 90+
95− 91+## Démarrage local
96−Sources écartées après vérification (jeux périmés) : Longueuil (2017), Saguenay (2022),
97−Repentigny (2021) — consignées au registre `data/sources.json` avec leur statut.
98−
99−## Démarrage rapide
100 92
101 ```bash 93 ```bash
102 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt 94 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
@@ -106,36 +98,33 @@ python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
106 .venv/bin/python run.py watch --interval 3600 # boucle d'ingestion horaire 98 .venv/bin/python run.py watch --interval 3600 # boucle d'ingestion horaire
107 ``` 99 ```
108 100
109−Secrets : `SCRAPFLY_API_KEY` dans `.env` (requis seulement pour Atuvu). 101+**Secrets** : `SCRAPFLY_API_KEY` dans `.env` (requis seulement pour le connecteur Atuvu).
110 102
111−## API 103+## Déploiement
112 104
113−| Route | Description | 105+- **Nœud** : **M3U96a** (MacLustr) — répertoire `~/apps/sorti-ka`
114−|---|---| 106+- **Port** : **8120**
115−| `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; dédupliquée inter-sources | 107+- **Domaine** : **[www.sorti-ka.com](https://www.sorti-ka.com)** (tunnel ngrok)
116−| `GET /api/events/{uid}` | fiche complète d'un événement | 108+- **PM2** (3 processus) :
117−| `GET /api/stats` | volumes par région et par catégorie | 109+ - **`sorti-ka-web`** — uvicorn sur le port **8120** (API + frontend + SSR)
118−| `GET /api/sources` | registre des sources + événements actifs + dernière synchro | 110+ - **`sorti-ka-sync`** — watcher **horaire** (mise à jour continue des 8 sources)
119−| `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) | 111+ - **`sorti-ka-ngrok`** — tunnel vers **www.sorti-ka.com**
120−| `GET /sitemap.xml` · `/robots.txt` | référencement | 112+- **Registre** : `~/Desktop/cluster-skill/cluster-deployments.json`
121−| `GET /docs` | Swagger | 113+
114+## Développement remote-first (IMPORTANT)
122 115
123−## Production 116+La **source de vérité est le repo git sur le nœud M3U96a** (`~/apps/sorti-ka`), **PAS une copie laptop**. Toute modification se fait **sur le nœud via SSH** : édition, tests, `pm2 restart`, puis commit/push **depuis le nœud**.
124 117
125−Nœud **M3U96a** (MacLustr) — PM2 : `sorti-ka-web` (uvicorn :8120), 118+- **`origin` = spbgit** (git perso, git.spboucher.ai) — sur **M3U96a**, le remote est le **chemin local** `/Users/simon-pierreboucher/srv/git/sorti-ka.git` (les bare repos vivent sur ce même nœud).
126−`sorti-ka-sync` (watch **horaire** — mise à jour continue), `sorti-ka-ngrok` 119+- Référence complète : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`.
127−(`www.sorti-ka.com`). Registre : `~/Desktop/cluster-skill/cluster-deployments.json`.
128 120
129 ## Principes 121 ## Principes
130 122
131−1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec 123+1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec `price_label` original conservé et **attribution systématique**.
132− `price_label` original conservé et attribution systématique. 124+2. **Sources légitimes d'abord** — données ouvertes officielles et sites au robots.txt permissif ; CGU/licences consignées en tête de chaque connecteur.
133−2. **Sources légitimes d'abord** — données ouvertes officielles et sites au 125+3. **Travail de moine** — un connecteur robuste à la fois, testé sur fixtures réelles, avec **alerte de dérive** quand une source casse.
134− robots.txt permissif ; CGU/licences consignées en tête de chaque connecteur. 126+4. **Cohérence ·Ka** — architecture Lou·Ka, design system **Groupe KA**.
135−3. **Travail de moine** — un connecteur robuste à la fois, testé sur fixtures
136− réelles, avec alerte de dérive quand une source casse.
137−4. **Cohérence ·Ka** — architecture Lou·Ka, design system Groupe KA.
138 127
139 --- 128 ---
140 129
141−© 2026 Sorti·Ka — Simon-Pierre Boucher \<contact@spboucher.ai\> 130+Un service **Groupe Ka** — © 2026 Sorti·Ka — Simon-Pierre Boucher \<contact@spboucher.ai\>
added docs/screenshot.png +0 −0

Binary file not shown.

Menu

Theme