docs: README à jour avec screenshot
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 | − | |
| 16 | − | |
| 17 | − | |
| 18 | − | |
| 19 | − | |
| 20 | − | |
| 21 | − | |
| 22 | − | |
| 23 | − | |
| 24 | − | |
| 25 | − | |
| 26 | − | |
| 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 | + | |
| 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.