SPB Git forge

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.

58commits 1branches 0releases
13.7 MBsize
maindefault branch
17 days agolast push
HTML 82.9% Python 15.2% TypeScript 0.9% JavaScript 0.7%

docs: README à jour avec screenshot

Simon-Pierre Boucher committed 1 mo ago (Aug 18, 2026) parent d614ca6

2 changed files +79 −90

modified README.md +79 −90
@@ -4,61 +4,57 @@
4 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 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 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)
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>
13 +![Aperçu de Sorti-Ka](docs/screenshot.png)
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
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.
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.
47 22
48 −Sorti·Ka n'est pas une billetterie : c'est un **index fidèle**. Aucun prix inventé,
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.
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**.
53 24
54 −## Visite guidée
25 +## Fonctionnalités
55 26
56 −| | |
57 −|:---:|:---:|
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 |
59 −| <img src="docs/screenshots/accueil.png" alt="Accueil" width="440"> | <img src="docs/screenshots/fiche.png" alt="Fiche événement" width="440"> |
27 +- **Recherche filtrée** — région, catégorie, période, **gratuité**, texte libre, tri et pagination
28 +- **Fiche par événement** — dates, lieu, prix, carte, événements similaires, **SSR + JSON-LD Event** pour le SEO
29 +- **8 connecteurs actifs** — **~15 000+ événements actifs**, **428 villes**, **17/17 régions** :
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 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 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 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` |
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` |
78 −| **Persistance** | upsert par hash de contenu, délai de grâce 2 syncs, alerte de dérive, historique de syncs | `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` |
80 −| **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` servies 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` |
82 −
83 −## Les 8 connecteurs actifs
84 −
85 −| Source | Type / accès | Extraction | Volume |
86 −|---|---|---|---|
87 −| **Le point de vente** | billetterie QC — robots `Allow: *` | listes HTML paginées (~65 pages) | ~2 500 concerts, humour, théâtre — toute la province |
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 |
89 −| **Ville de Montréal** | données ouvertes CC BY 4.0 | API CKAN datastore | ~5 600 événements municipaux |
90 −| **evenko** | promoteur — robots `Allow: /api/search*` | proxy Algolia percé (`body=base64({params:[q,opts],lang})`) | ~460 grands concerts (Centre Bell, Place Bell, MTELUS…) |
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 |
92 −| **Atuvu.ca** | Cloudflare → **Scrapfly**, robots `Allow: /` | sitemaps lastmod + fiches, cache incrémental plafonné | rattrapage progressif |
93 −| **Ville de Sherbrooke** | calendrier municipal public | JSON embarqué `TRANSFER_CACHE` — 1 requête | ~270 événements avec images et gratuité |
94 −| **Ville de Laval** | données ouvertes CC 4.0 | JSON officiel | ~290 activités |
95 −
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
72 +| **Normalisation** | dates FR/ISO, prix, **14 catégories**, villes, entités HTML | `sortika/normalize.py` |
73 +| **Régions** | Répertoire MAMH : 1 250 villes + arrondissements + alias | `sortika/regions.py` + `data/villes_regions.json` |
74 +| **Persistance** | upsert par hash, délai de grâce, alerte de dérive | `sortika/db.py` |
75 +| **Dédup** | empreinte titre + ville + date | `schema.dedup_key()` + `web.py` |
76 +| **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` avec `<head>` unique + **JSON-LD Event**, robots, sitemaps | `sortika/web.py` + `sortika/seo.py` |
77 +| **Frontend** | SPA sans build, design system **Groupe KA** | `frontend/index.html` |
78 +
79 +### API
80 +
81 +| Route | Description |
82 +|---|---|
83 +| `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; **dédupliquée inter-sources** |
84 +| `GET /api/events/{uid}` | fiche complète d'un événement |
85 +| `GET /api/stats` | volumes par région et par catégorie |
86 +| `GET /api/sources` | registre des sources + événements actifs + dernière synchro |
87 +| `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) |
88 +| `GET /sitemap.xml` · `/robots.txt` | référencement |
89 +| `GET /docs` | Swagger |
90 +
91 +## Démarrage local
100 92
101 93 ```bash
102 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 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 |
114 −|---|---|
115 −| `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; dédupliquée inter-sources |
116 −| `GET /api/events/{uid}` | fiche complète d'un événement |
117 −| `GET /api/stats` | volumes par région et par catégorie |
118 −| `GET /api/sources` | registre des sources + événements actifs + dernière synchro |
119 −| `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) |
120 −| `GET /sitemap.xml` · `/robots.txt` | référencement |
121 −| `GET /docs` | Swagger |
105 +- **Nœud** : **M3U96a** (MacLustr) — répertoire `~/apps/sorti-ka`
106 +- **Port** : **8120**
107 +- **Domaine** : **[www.sorti-ka.com](https://www.sorti-ka.com)** (tunnel ngrok)
108 +- **PM2** (3 processus) :
109 + - **`sorti-ka-web`** — uvicorn sur le port **8120** (API + frontend + SSR)
110 + - **`sorti-ka-sync`** — watcher **horaire** (mise à jour continue des 8 sources)
111 + - **`sorti-ka-ngrok`** — tunnel vers **www.sorti-ka.com**
112 +- **Registre** : `~/Desktop/cluster-skill/cluster-deployments.json`
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),
126 −`sorti-ka-sync` (watch **horaire** — mise à jour continue), `sorti-ka-ngrok`
127 −(`www.sorti-ka.com`). Registre : `~/Desktop/cluster-skill/cluster-deployments.json`.
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).
119 +- Référence complète : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`.
128 120
129 121 ## Principes
130 122
131 −1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec
132 − `price_label` original conservé et attribution systématique.
133 −2. **Sources légitimes d'abord** — données ouvertes officielles et sites au
134 − robots.txt permissif ; CGU/licences consignées en tête de chaque connecteur.
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.
123 +1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec `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.
125 +3. **Travail de moine** — un connecteur robuste à la fois, testé sur fixtures réelles, avec **alerte de dérive** quand une source casse.
126 +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.