# Sorti·Ka ### **Toutes les sorties et tous les événements du Québec. Un seul endroit.** **[www.sorti-ka.com](https://www.sorti-ka.com)**  --- ## Description **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. 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. 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**. ## Fonctionnalités - **Recherche filtrée** — région, catégorie, période, **gratuité**, texte libre, tri et pagination - **Fiche par événement** — dates, lieu, prix, carte, événements similaires, **SSR + JSON-LD Event** pour le SEO - **8 connecteurs actifs** — **~15 000+ événements actifs**, **428 villes**, **17/17 régions** : | Source | Type / accès | Extraction | |---|---|---| | **Le point de vente** | billetterie QC — robots `Allow: *` | listes HTML paginées (~2 500 événements, toute la province) | | **SIT Québec** (Ministère du Tourisme) | données ouvertes CC 4.0 | API JSON Tourinsoft (~1 600 événements, toutes les régions) | | **Ville de Montréal** | données ouvertes CC BY 4.0 | API CKAN datastore (~5 600 événements) | | **evenko** | promoteur — robots `Allow: /api/search*` | proxy Algolia (~460 grands concerts : Centre Bell, MTELUS…) | | **La Vitrine** | Content-Signal `search=yes` | sitemap + fiches HTML, cache incrémental (~5 100 représentations) | | **Atuvu.ca** | Cloudflare → **Scrapfly** | sitemaps lastmod + fiches, rattrapage progressif | | **Ville de Sherbrooke** | calendrier municipal public | JSON embarqué — 1 requête (~270 événements, heures locales) | | **Ville de Laval** | données ouvertes CC 4.0 | JSON officiel (~290 activités, heures locales) | | **Ville de Brossard** | API REST WordPress publique | `/wp-json/wp/v2/city-event` + fiches (bannière ou JSON-LD, ~400 événements) | | **Bandsintown** | API publique par artiste | dates QC des artistes connus de la base (rotation 100 artistes/sync) | | **Ticketmaster** | Discovery API officielle | **prêt — clé requise** (`TICKETMASTER_API_KEY`, gratuite) | - **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 - **Répertoire des municipalités MAMH** — **1 250 villes** + arrondissements + alias, homonymes résolus par population - **Persistance robuste** — upsert par hash de contenu, **délai de grâce 2 syncs**, alerte de dérive, historique de syncs - **Page /stats** — tableau de bord analytique + **rapport PDF Groupe-KA** - **Widget KA Agent** — bulle de chat Groupe KA intégrée au frontend - **Heure précise (`start_time`) et artistes (`artists`)** — captés quand la source les publie (billetteries, calendriers municipaux), jamais inventés - **32 tests** sur fixtures réelles, **zéro réseau** ## Stack technique - **Python 3.14** + **FastAPI** — API JSON + rendu SSR SEO - **SQLite** — stockage, diff et historique de synchronisation - **Frontend SPA sans build** — HTML/JS, routeur History API, design system **Groupe KA « éditorial sharp »** (ka-ui) - **PM2** + **ngrok** — exécution résiliente et exposition publique - **Scrapfly** — contournement Cloudflare pour Atuvu (clé `SCRAPFLY_API_KEY` dans `.env`) ## Structure du projet ``` connecteurs (1 module / source) normalisation (Event) SQLite (diff + grâce) sortika/connectors/*.py → schema.py + normalize.py → db.py ▲ auto-découverts + regions.py (MAMH) │ │ ▼ watcher horaire (PM2) frontend SPA + fiches ← API FastAPI + SSR SEO ingest.py watch frontend/index.html web.py + seo.py ``` | Couche | Rôle | Fichiers | |---|---|---| | **Connecteurs** | 1 module Python par source : API JSON internes (Algolia evenko, Tourinsoft, CKAN), listes HTML, Scrapfly, caches incrémentaux | `sortika/connectors/*.py` | | **Schéma** | `Event` standardisé : titre, catégories canoniques, lieu, ville, **région (17)**, lat/lng, dates ISO + **heure locale**, **artistes**, gratuité/prix avec `price_label` source | `sortika/schema.py` | | **Normalisation** | dates FR/ISO, prix, **14 catégories**, villes, entités HTML | `sortika/normalize.py` | | **Régions** | Répertoire MAMH : 1 250 villes + arrondissements + alias | `sortika/regions.py` + `data/villes_regions.json` | | **Persistance** | upsert par hash, délai de grâce, alerte de dérive | `sortika/db.py` | | **Dédup** | empreinte titre + ville + date | `schema.dedup_key()` + `web.py` | | **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` avec `
` unique + **JSON-LD Event**, robots, sitemaps | `sortika/web.py` + `sortika/seo.py` | | **Frontend** | SPA sans build, design system **Groupe KA** | `frontend/index.html` | ### API | Route | Description | |---|---| | `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; **dédupliquée inter-sources** | | `GET /api/events/{uid}` | fiche complète d'un événement | | `GET /api/stats` | volumes par région et par catégorie | | `GET /api/sources` | registre des sources + événements actifs + dernière synchro | | `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) | | `GET /sitemap.xml` · `/robots.txt` | référencement | | `GET /docs` | Swagger | ## Démarrage local ```bash python3 -m venv .venv && .venv/bin/pip install -r requirements.txt .venv/bin/python -m pytest tests/ -q # 32 tests (fixtures réelles, zéro réseau) .venv/bin/python run.py sync # ingestion des 10 sources actives .venv/bin/python run.py web --port 8120 # API + frontend + SSR .venv/bin/python run.py watch --interval 3600 # boucle d'ingestion horaire ``` **Secrets** (gabarit : `.env.example`) : `SCRAPFLY_API_KEY` (connecteur Atuvu) ; `TICKETMASTER_API_KEY` (active le connecteur Ticketmaster, clé gratuite). ## Déploiement - **Nœud** : **M3U96a** (MacLustr) — répertoire `~/apps/sorti-ka` - **Port** : **8120** - **Domaine** : **[www.sorti-ka.com](https://www.sorti-ka.com)** (tunnel ngrok) - **PM2** (3 processus) : - **`sorti-ka-web`** — uvicorn sur le port **8120** (API + frontend + SSR) - **`sorti-ka-sync`** — watcher **horaire** (mise à jour continue des 10 sources actives) - **`sorti-ka-ngrok`** — tunnel vers **www.sorti-ka.com** - **Registre** : `~/Desktop/cluster-skill/cluster-deployments.json` ## Développement remote-first (IMPORTANT) 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**. - **`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). - Référence complète : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`. ## Principes 1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec `price_label` original conservé et **attribution systématique**. 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. 3. **Travail de moine** — un connecteur robuste à la fois, testé sur fixtures réelles, avec **alerte de dérive** quand une source casse. 4. **Cohérence ·Ka** — architecture Lou·Ka, design system **Groupe KA**. --- Un service **Groupe Ka** — © 2026 Sorti·Ka — Simon-Pierre Boucher \