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%

Phase 4 — 7 nouveaux connecteurs : régions manquantes (Montérégie, Centre-du-Québec, Chaudière-Appalaches, Gaspésie via Scrapfly) + billetteries QC (TicketAcces, Réseau Ovation, Tuxedo via acteur Apify ka-tuxedo)

- ATR : monteregie (WP REST cpt_event + JSON-LD fiche), centreduquebec (SSR),
  chaudiereappalaches (SSR paginé, JSON-LD Event complet), gaspesie (RÉACTIVÉE —
  WAF contourné via Scrapfly consigné et budgété)
- Billetteries : ticketacces (calendrier agrégé, 47 orgs QC dont 14 en Abitibi,
  ~1 320 séances, Crawl-delay 120 honoré PAR HÔTE + TTL 6 h), ovation (26 orgs
  100 % QC, artistes/adresses/heures, Crawl-delay 30 honoré + TTL 20 h),
  tuxedobillet (RÉACTIVÉE — 29 tenants QC, SPA Firebase rendue par l'acteur
  Apify maison gorgeous_thistle/ka-tuxedo, code versionné dans apify/ka-tuxedo/)
- Configs annuaires : data/ovation_orgs.json, data/tuxedo_tenants.json
- Registre 39 sources, CONFORMITE.md (robots.txt lus, crawl-delays honorés),
  fiches docs régénérées, 10 nouveaux tests sur fixtures réelles (69 verts)
- Couverture : +1 900 séances futures ; Abitibi +312, Gaspésie réamorcée,
  Centre-du-Québec première source

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 22, 2026) parent 9cfb700

72 changed files +3,957 −917

added apify/ka-tuxedo/.actor/actor.json +13 −0
@@ -0,0 +1,13 @@
1 +{
2 + "actorSpecification": 1,
3 + "name": "ka-tuxedo",
4 + "title": "KA Tuxedo Billet — spectacles des tenants québécois",
5 + "description": "Rend les billetteries Tuxedo (*.tuxedobillet.com, SPA Angular/Firebase) avec Playwright et extrait la liste des spectacles (titre, sous-titre, date, image, lien) par tenant. Sorti-Ka / Groupe Ka.",
6 + "version": "0.1",
7 + "buildTag": "latest",
8 + "meta": {
9 + "author": "Simon-Pierre Boucher <contact@spboucher.ai>"
10 + },
11 + "input": "./input_schema.json",
12 + "dockerfile": "../Dockerfile"
13 +}
added apify/ka-tuxedo/.actor/input_schema.json +28 −0
@@ -0,0 +1,28 @@
1 +{
2 + "title": "KA Tuxedo Billet — entrée",
3 + "type": "object",
4 + "schemaVersion": 1,
5 + "properties": {
6 + "tenants": {
7 + "title": "Tenants",
8 + "type": "array",
9 + "description": "Sous-domaines *.tuxedobillet.com à moissonner (ex. espacetheatre).",
10 + "editor": "stringList",
11 + "prefill": ["espacetheatre"]
12 + },
13 + "renderWaitMs": {
14 + "title": "Attente de rendu (ms)",
15 + "type": "integer",
16 + "description": "Délai supplémentaire après le chargement pour laisser Angular peindre la liste.",
17 + "default": 6000
18 + },
19 + "proxyConfiguration": {
20 + "title": "Proxy",
21 + "type": "object",
22 + "description": "Proxy Apify (résidentiel CA recommandé).",
23 + "editor": "proxy",
24 + "default": {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CA"}
25 + }
26 + },
27 + "required": ["tenants"]
28 +}
added apify/ka-tuxedo/Dockerfile +13 −0
@@ -0,0 +1,13 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: Dockerfile
4 +# Desc: Image de l'acteur Apify ka-tuxedo (Playwright + Python).
5 +# ==============================================================================
6 +FROM apify/actor-python-playwright:3.13
7 +
8 +COPY requirements.txt ./
9 +RUN pip install --no-cache-dir -r requirements.txt
10 +
11 +COPY . ./
12 +
13 +CMD ["python3", "-m", "src"]
added apify/ka-tuxedo/requirements.txt +7 −0
@@ -0,0 +1,7 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: requirements.txt
4 +# Desc: Dépendances de l'acteur ka-tuxedo (rien d'épinglé sauf nécessité —
5 +# gotcha de build Apify consigné 2026-08).
6 +# ==============================================================================
7 +apify
added apify/ka-tuxedo/src/__main__.py +10 −0
@@ -0,0 +1,10 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: src/__main__.py
4 +# Desc: Point d'entrée de l'acteur Apify ka-tuxedo.
5 +# ==============================================================================
6 +import asyncio
7 +
8 +from .main import main
9 +
10 +asyncio.run(main())
added apify/ka-tuxedo/src/main.py +105 −0
@@ -0,0 +1,105 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: src/main.py
4 +# Desc: Acteur Apify ka-tuxedo — billetteries Tuxedo (*.tuxedobillet.com).
5 +# Les tenants sont des SPA Angular sur Firebase (RTDB verrouillée,
6 +# 401 même avec jeton anonyme — vérifié 2026-08-21) : le rendu
7 +# navigateur est la seule voie. Playwright (Chromium) + proxy Apify
8 +# (résidentiel CA par défaut) rend chaque tenant et extrait les tuiles
9 +# .tuxCmsEdit__webShowTile : titre, sous-titre, date, image, lien
10 +# /main/<slug>. Un item de dataset par spectacle.
11 +# ==============================================================================
12 +from __future__ import annotations
13 +
14 +import asyncio
15 +from urllib.parse import urlparse
16 +
17 +from apify import Actor
18 +from playwright.async_api import async_playwright
19 +
20 +CONCURRENCY = 4 # pages simultanées (29 tenants ≈ 6 vagues)
21 +
22 +
23 +def _pw_proxy(url: str | None) -> dict | None:
24 + """URL de proxy Apify → dict Playwright (identifiants SÉPARÉS du server —
25 + Playwright ignore les credentials inline, cause de timeouts silencieux)."""
26 + if not url:
27 + return None
28 + p = urlparse(url)
29 + proxy = {"server": f"{p.scheme}://{p.hostname}:{p.port}"}
30 + if p.username:
31 + proxy["username"] = p.username
32 + proxy["password"] = p.password or ""
33 + return proxy
34 +
35 +TILE = ".tuxCmsEdit__webShowTile"
36 +
37 +EXTRACT_JS = """
38 +() => Array.from(document.querySelectorAll('.tuxCmsEdit__webShowTile')).map(t => {
39 + const q = (sel) => { const e = t.querySelector(sel); return e ? e.textContent.trim() : ''; };
40 + const a = t.querySelector('a[href^="/main/"]');
41 + const img = t.querySelector('img.tuxCmsEdit__webShowTile--showImage');
42 + return {
43 + slug: a ? a.getAttribute('href').replace('/main/', '') : '',
44 + title: q('.tuxCmsEdit__webShowTile--showTitle'),
45 + subtitle: q('.tuxCmsEdit__webShowTile--showSubtitle'),
46 + date_raw: q('.tuxCmsEdit__webShowTile--showDate'),
47 + image: img ? img.getAttribute('src') : '',
48 + };
49 +})
50 +"""
51 +
52 +
53 +async def main() -> None:
54 + async with Actor:
55 + inp = await Actor.get_input() or {}
56 + tenants = [t.strip().lower() for t in inp.get("tenants", []) if t.strip()]
57 + wait_ms = int(inp.get("renderWaitMs", 6000))
58 + proxy_cfg = await Actor.create_proxy_configuration(
59 + actor_proxy_input=inp.get("proxyConfiguration"))
60 + proxy_url = await proxy_cfg.new_url() if proxy_cfg else None
61 +
62 + async with async_playwright() as pw:
63 + browser = await pw.chromium.launch(
64 + headless=True, proxy=_pw_proxy(proxy_url))
65 + context = await browser.new_context(
66 + locale="fr-CA", viewport={"width": 1366, "height": 900})
67 + sem = asyncio.Semaphore(CONCURRENCY)
68 +
69 + async def scrape(tenant: str) -> None:
70 + url = f"https://{tenant}.tuxedobillet.com/"
71 + async with sem:
72 + page = await context.new_page()
73 + try:
74 + await page.goto(url, wait_until="domcontentloaded",
75 + timeout=45_000)
76 + try:
77 + await page.wait_for_selector(TILE, timeout=25_000)
78 + except Exception:
79 + Actor.log.warning(f"{tenant}: aucune tuile rendue")
80 + await page.wait_for_timeout(wait_ms)
81 + # défilement : certaines listes chargent paresseusement
82 + for _ in range(6):
83 + await page.mouse.wheel(0, 2400)
84 + await page.wait_for_timeout(300)
85 + shows = await page.evaluate(EXTRACT_JS)
86 + seen: set[str] = set()
87 + for s in shows:
88 + if (not s["title"] or not s["slug"]
89 + or s["slug"] in seen):
90 + continue
91 + seen.add(s["slug"])
92 + await Actor.push_data({
93 + "tenant": tenant,
94 + "url": (f"https://{tenant}.tuxedobillet.com"
95 + f"/main/{s['slug']}"),
96 + **s,
97 + })
98 + Actor.log.info(f"{tenant}: {len(seen)} spectacle(s)")
99 + except Exception as exc:
100 + Actor.log.exception(f"{tenant}: échec ({exc})")
101 + finally:
102 + await page.close()
103 +
104 + await asyncio.gather(*(scrape(t) for t in tenants))
105 + await browser.close()
added data/ovation_orgs.json +31 −0
@@ -0,0 +1,31 @@
1 +{
2 + "_comment": "Sorti-Ka — Organisations du Réseau Ovation (m.ovation.ca) à moissonner. Auteur : Simon-Pierre Boucher — contact@spboucher.ai. Codes base36 validés par balayage 000000-00002Z le 2026-08-21 (un code valide répond 200 sur /fr/Search/AllEvents, un invalide 302). Réseau 100 % québécois — ajouter une organisation = un bloc ici, 0 code. Les clubs sportifs à 0 événement hors saison restent listés (la saison les réactive).",
3 + "organisations": [
4 + {"code": "00000O", "nom": "Centre culturel de l'Université de Sherbrooke", "ville": "Sherbrooke"},
5 + {"code": "00000P", "nom": "Maison des Arts Desjardins Drummondville", "ville": "Drummondville"},
6 + {"code": "00000Q", "nom": "Salle Albert-Rousseau", "ville": "Québec"},
7 + {"code": "00000R", "nom": "Diffusion Saguenay", "ville": "Saguenay"},
8 + {"code": "00000V", "nom": "Salle Odyssée — Maison de la culture de Gatineau", "ville": "Gatineau"},
9 + {"code": "00000W", "nom": "Culture Trois-Rivières", "ville": "Trois-Rivières"},
10 + {"code": "00000Z", "nom": "Palace de Granby", "ville": "Granby"},
11 + {"code": "000011", "nom": "Ville de Gatineau — Spectacles et théâtre", "ville": "Gatineau"},
12 + {"code": "000012", "nom": "Théâtre du Vieux Terrebonne", "ville": "Terrebonne"},
13 + {"code": "000016", "nom": "Théâtre Granada", "ville": "Sherbrooke"},
14 + {"code": "00001A", "nom": "Festival Western de St-Tite", "ville": "Saint-Tite"},
15 + {"code": "00001D", "nom": "Saguenéens de Chicoutimi", "ville": "Saguenay"},
16 + {"code": "00001E", "nom": "Grand Prix de Trois-Rivières", "ville": "Trois-Rivières"},
17 + {"code": "00001F", "nom": "Diablos de Trois-Rivières", "ville": "Trois-Rivières"},
18 + {"code": "00001G", "nom": "Marquis de Jonquière", "ville": "Saguenay"},
19 + {"code": "00001H", "nom": "Le Memphis Cabaret", "ville": "Trois-Rivières"},
20 + {"code": "00001I", "nom": "Voltigeurs de Drummondville", "ville": "Drummondville"},
21 + {"code": "00001J", "nom": "Théâtre Petit Champlain", "ville": "Québec"},
22 + {"code": "00001K", "nom": "Vieux Clocher de Magog", "ville": "Magog"},
23 + {"code": "00001M", "nom": "Cataractes de Shawinigan", "ville": "Shawinigan"},
24 + {"code": "00001P", "nom": "Vieux Clocher de Waterloo", "ville": "Waterloo"},
25 + {"code": "00001Q", "nom": "Complexe Félix-Leclerc — CDAC Ville de La Tuque", "ville": "La Tuque"},
26 + {"code": "00001R", "nom": "Loisirs Longueuil", "ville": "Longueuil"},
27 + {"code": "00001T", "nom": "Centre des arts de la scène Jean-Besré", "ville": "Sherbrooke"},
28 + {"code": "00001Y", "nom": "Ville de Coteau-du-Lac", "ville": "Coteau-du-Lac"},
29 + {"code": "00001Z", "nom": "Théâtre de Rougemont", "ville": "Rougemont"}
30 + ]
31 +}
modified data/sources.json +457 −402
@@ -1,403 +1,458 @@
1 1 {
2 "_comment": "Sorti-Ka — Registre des sources d'événements (province de Québec). Auteur : Simon-Pierre Boucher — contact@spboucher.ai",
3 "sources": [
4 {
5 "id": "sitq",
6 "name": "SIT Québec — Ministère du Tourisme",
7 "url": "https://www.donneesquebec.ca/recherche/dataset/sit-quebec-evenements",
8 "type": "donnees-ouvertes",
9 "extraction": "API JSON (Tourinsoft, syndication publique) — dates, géoloc, thèmes, contacts (TelephonePrincipals/Courriels → description « Renseignements : … ») ; le flux ne publie ni Descriptions* ni tarifs (vérifié 2026-08)",
10 "acces_legal": "Licence Creative Commons 4.0 (Données Québec) — API officielle publiée par le Ministère du Tourisme",
11 "couverture": "Événements touristiques, toutes les régions du Québec",
12 "cadence": "horaire (watcher PM2)",
13 "statut": "actif"
14 },
15 {
16 "id": "montreal",
17 "name": "Ville de Montréal — Événements publics",
18 "url": "https://donnees.montreal.ca/dataset/evenements-publics",
19 "type": "donnees-ouvertes",
20 "extraction": "API CKAN datastore_search (JSON, paginée) — venue = emplacement (si vrai nom de lieu) ou titre_adresse ; address = adresse_principale + adresse_secondaire ; le jeu de données ne publie pas l'heure → heure locale captée dans le JSON-LD des fiches montreal.ca (« startDate »:\"…T19:30:00-04:00\") lors de l'enrichissement og:image incrémental (cache, plafond MTL_IMG_MAX/sync)",
21 "acces_legal": "Licence CC BY 4.0 (donnees.montreal.ca) — attribution : Ville de Montréal",
22 "couverture": "Événements municipaux, île de Montréal",
23 "cadence": "horaire (watcher PM2)",
24 "statut": "actif"
25 },
26 {
27 "id": "lepointdevente",
28 "name": "Le point de vente — billetterie québécoise",
29 "url": "https://lepointdevente.com/billets",
30 "type": "billetterie",
31 "extraction": "pages de liste HTML rendues serveur (paginées), parse des cartes (titre, date FR + heure « 17 août 2026, 20h00 » → start_time, salle, ville, image) + fiches détail /billets/<id> incrémentales (cache data/lepointdevente_cache.json, 150 fiches/sync, re-visite 7 j) : description og:description, heure précise, tarifs JSON réels via /plugins/rates/<id> → price_min/price_label/is_free ; artistes extraits des titres par patrons sûrs seulement (« Artiste - Salle », « X en concert »)",
32 "acces_legal": "robots.txt : « User-Agent: * / Allow: * » — GET throttlés, identification honnête, lien direct vers chaque billetterie",
33 "couverture": "Concerts, humour, théâtre, festivals — partout au Québec (~2 500 événements à venir)",
34 "cadence": "horaire (watcher PM2)",
35 "statut": "actif"
36 },
37 {
38 "id": "laval",
39 "name": "Ville de Laval — Calendrier des activités",
40 "url": "https://www.donneesquebec.ca/recherche/dataset/calendrier-des-activites",
41 "type": "donnees-ouvertes",
42 "extraction": "fichier JSON officiel (Données Québec) — EventDate/EndDate publiés en UTC (vérifié 2026-08) → convertis en date + heure locales Québec (minuit local = « toute la journée », pas d'heure) ; détection du préfixe « ANNULÉ - » du titre → statut cancelled (Phase 2)",
43 "acces_legal": "Licence CC 4.0 (Données Québec) — Ville de Laval",
44 "couverture": "Activités municipales, Laval",
45 "cadence": "horaire (watcher PM2)",
46 "statut": "morte-a-la-source",
47 "decision": "2026-08-19 (audit Phase 1 + Phase 2) : le jeu ouvert est MORT À LA SOURCE — 293 enregistrements, EventDate max 2025-12-09, plus aucun événement futur publié depuis fin 2025 alors que le connecteur tourne sans erreur (41 runs verts). Le connecteur reste branché (archivage naturel du stock + réveil automatique si la Ville republie), mais la source ne compte plus comme active. REMPLACEMENT prévu en Phase 3 : calendrier vivant laval.ca / Tourisme Laval (voir 04-SOURCES-CANDIDATES de l'audit).",
48 "note": " REMPLACÉE 2026-08-19 (Phase 3) par la source « lavalweb » (calendrier vivant laval.ca)."
49 },
50 {
51 "id": "sherbrooke",
52 "name": "Ville de Sherbrooke — Calendrier des événements",
53 "url": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites",
54 "type": "municipal",
55 "extraction": "la page rend TOUT le calendrier dans window['TRANSFER_CACHE'] (JSON embarqué, CMS Maruche) → 1 seule requête, ~270 événements (nom, périodes en UTC → date + heure locales Québec, gratuité `payant`, catégories, clientèles, images)",
56 "acces_legal": "page municipale publique (référencée sur Données Québec) — GET direct, identification honnête",
57 "couverture": "Événements municipaux et culturels, Sherbrooke (Estrie)",
58 "cadence": "horaire (watcher PM2)",
59 "statut": "actif"
60 },
61 {
62 "id": "longueuil",
63 "name": "Ville de Longueuil — Événements (nouveau portail)",
64 "url": "https://longueuil.quebec/fr/evenements",
65 "type": "municipal",
66 "extraction": "page liste rendue serveur (portail Nuxt) — cartes <article class=\"event__item\"> : <time datetime> (date + heure locales, début/fin), titre, lien, extrait ; enrichissement par fiche incrémental (cache data/longueuil_cache.json, 30 fiches/sync, re-visite 30 j) : adresse civique, code postal, municipalité depuis l'état Nuxt (address_line1/postal_code/locality) ; GPS via le géocodeur central",
67 "acces_legal": "portail municipal public, rendu serveur — GET throttlés 0,8 s, identification honnête",
68 "couverture": "Événements municipaux, Longueuil (Montérégie)",
69 "cadence": "horaire (watcher PM2, incrémental)",
70 "statut": "actif",
71 "note": "Remplace le jeu Données Québec « evenements » périmé (dernière date 2017-03, vérifié 2026-08-17) par le portail vivant longueuil.quebec."
72 },
73 {
74 "id": "eventbrite",
75 "name": "Eventbrite — billetterie libre-service (Québec)",
76 "url": "https://www.eventbrite.ca/d/canada--montreal/all-events/",
77 "type": "billetterie",
78 "extraction": "pages de découverte publiques /d/<lieu>/all-events/?page=N (7 lieux QC pondérés : Montréal, Québec, Laval, Gatineau, Sherbrooke, Longueuil, Trois-Rivières) — window.__SERVER_DATA__ livre 20 résultats/page : nom, résumé, date + heure locales, salle géocodée (lat/lng, adresse, code postal), image, catégories (tags Eventbrite*), URL ; prix par fiche détail /e/<slug> incrémentale (JSON-LD AggregateOffer lowPrice/highPrice CAD ; cache data/eventbrite_cache.json, 150 fiches/sync, re-visite 7 j) ; en ligne exclus, filtre region=QC strict",
79 "acces_legal": "robots.txt « User-agent: * » n'interdit ni /d/ ni /e/ (seuls /directory/, checkout et endpoints privés) — GET throttlés 0,8 s, identification honnête, lien direct vers chaque fiche",
80 "couverture": "Événements communautaires, ateliers, festivals, affaires — grandes villes du Québec",
81 "cadence": "horaire (watcher PM2, incrémental)",
82 "statut": "actif"
83 },
84 {
85 "id": "cantonsdelest",
86 "name": "Tourisme Cantons-de-l'Est — calendrier officiel (ATR Estrie)",
87 "url": "https://www.cantonsdelest.com/evenements",
88 "type": "tourisme-regional",
89 "extraction": "page liste rendue serveur (~25 fiches à venir) puis fiches détail incrémentales (cache data/cantonsdelest_cache.json, 40 fiches/sync, re-visite 7 j) : JSON-LD Event complet (nom, dates, description riche, adresse civique, images Tourinsoft mto.media.tourinsoft.eu) + GPS exact (data-lat/data-lng de la carte) ; même plateforme Tourinsoft que le SIT Québec → recoupement géré par dedup_key",
90 "acces_legal": "pages publiques de l'ATR, rendu serveur — GET throttlés 0,8 s, identification honnête, lien vers la fiche originale",
91 "couverture": "Festivals, spectacles, événements — Cantons-de-l'Est (Estrie)",
92 "cadence": "horaire (watcher PM2, incrémental)",
93 "statut": "actif"
94 },
95 {
96 "id": "saguenay",
97 "name": "Ville de Saguenay — Événements culturels",
98 "url": "https://www.donneesquebec.ca/recherche/dataset/sag_evenements-culturels",
99 "type": "donnees-ouvertes",
100 "extraction": "CSV (Données Québec)",
101 "acces_legal": "Licence CC 4.0 (Données Québec)",
102 "couverture": "Saguenay",
103 "cadence": "n/d",
104 "statut": "abandonnee",
105 "note": "Jeu de données périmé — dernière date d'événement : 2022-06 (vérifié 2026-08-17). REMPLACÉE 2026-08-19 (Phase 3) par la source « saguenaysorties » (portail sorties.saguenay.ca)."
106 },
107 {
108 "id": "repentigny",
109 "name": "Ville de Repentigny — Événements",
110 "url": "https://www.donneesquebec.ca/recherche/dataset/evenement-sur-le-territoire-de-la-ville",
111 "type": "donnees-ouvertes",
112 "extraction": "CSV (Données Québec)",
113 "acces_legal": "Licence CC 4.0 (Données Québec)",
114 "couverture": "Repentigny (Lanaudière)",
115 "cadence": "n/d",
116 "statut": "abandonnee",
117 "note": "Jeu de données périmé — dernière date d'événement : 2021-09 (vérifié 2026-08-17)"
118 },
119 {
120 "id": "evenko",
121 "name": "evenko — grands concerts et festivals",
122 "url": "https://evenko.ca/fr/evenements",
123 "type": "promoteur",
124 "extraction": "API /api/search (proxy Algolia) — format percé : GET ?body=base64({params:[query,options],lang:'fr-CA'}), filters entity_type:evenko_show, ~5 requêtes/sync ; heure locale du spectacle via show_time (epoch → America/Montreal, respect de hide_show_time) ; artistes = champ headliners explicite ; gratuité : champ free (sondé null sur 100 % des hits, 2026-08) + étiquettes « gratuit » (event_tag/promotional_tag) ; aucun montant exposé par l'index",
125 "acces_legal": "robots.txt : Allow /api/search* (explicite) — lien billets = URL Ticketmaster officielle",
126 "couverture": "Grands concerts (Centre Bell, Place Bell, MTELUS…), festivals (Osheaga, ÎleSoniq…)",
127 "cadence": "horaire (watcher PM2)",
128 "statut": "actif"
129 },
130 {
131 "id": "atuvu",
132 "name": "Atuvu.ca — calendrier culturel",
133 "url": "https://atuvu.ca/",
134 "type": "agregateur",
135 "extraction": "Scrapfly (Cloudflare) — sitemaps lastmod + fiches .event-play (date FR + heure « 20h00 » → start_time), cache incrémental data/atuvu_cache.json, plafond 120 fiches/sync",
136 "acces_legal": "robots.txt : Allow / + sitemap public",
137 "couverture": "Sorties culturelles, Grand Montréal",
138 "cadence": "horaire (watcher PM2, incrémental)",
139 "statut": "actif"
140 },
141 {
142 "id": "lavitrine",
143 "name": "La Vitrine culturelle",
144 "url": "https://www.lavitrine.com/",
145 "type": "billetterie",
146 "extraction": "GET directs — sitemap.xml (2 000 fiches evenement + 185 exposition) + fiches HTML (bloc event_calendar), cache incrémental, 150 fiches/sync, re-visite 7 j",
147 "acces_legal": "robots.txt : Allow / + Content-Signal search=yes, use=reference — usage index + lien vers la fiche originale",
148 "couverture": "Spectacles et sorties culturelles, Grand Montréal",
149 "cadence": "horaire (watcher PM2, incrémental)",
150 "statut": "actif"
151 },
152 {
153 "id": "brossard",
154 "name": "Ville de Brossard — Calendrier des événements",
155 "url": "https://brossard.ca/calendrier-des-evenements/",
156 "type": "municipal",
157 "extraction": "API REST WordPress publique /wp-json/wp/v2/city-event (JSON paginé, ~400 fiches) + dates/heures par fiche : bannière « Cet événement aura lieu le … » (fiches ville) ou JSON-LD Event (fiches biblio.brossard.ca) — cache incrémental data/brossard_cache.json, 80 fiches/sync, re-visite 7 j ; repli : date entre parenthèses dans le titre",
158 "acces_legal": "API REST WordPress publique (aucune authentification) — GET throttlés 0,8 s, identification honnête",
159 "couverture": "Événements municipaux et de bibliothèque, Brossard (Montérégie)",
160 "cadence": "horaire (watcher PM2, incrémental)",
161 "statut": "actif"
162 },
163 {
164 "id": "ticketmaster",
165 "name": "Ticketmaster — Discovery API (Québec)",
166 "url": "https://developer.ticketmaster.com/products-and-docs/apis/discovery-api/v2/",
167 "type": "billetterie",
168 "extraction": "SANS clé (mode par défaut) : scraping léger des pages découverte publiques www.ticketmaster.ca/discover/<ville>?page=N (montreal, quebec-city, gatineau) — l'état Next.js (__NEXT_DATA__, requête serveur cityEvents) livre 20 événements/page : titre, date UTC + fuseau → date/heure locales, salle géolocalisée (lat/lng), adresse, code postal, ville, URL billetterie, catégorie majeure, plan de salle (image) ; plafonds TM_MAX_EVENTS=360 / TM_PAGES_PER_CITY=8 ; Scrapfly ASP sans render_js en secours anti-bot ; aucun prix publié dans ce flux → jamais inventé. REPLI AUTOMATIQUE : si TICKETMASTER_API_KEY apparaît dans .env, bascule sur la Discovery API v2 officielle (priceRanges, attractions → artistes, images 16:9) — identifiants identiques, zéro doublon.",
169 "acces_legal": "pages découverte publiques rendues serveur — robots.txt n'interdit pas /discover/ (seuls /api/ismds/*) ; GET throttlés 1 s, lien direct vers la billetterie officielle ; API officielle (clé gratuite) utilisée dès qu'une clé est fournie",
170 "couverture": "Grands spectacles, sports et concerts — Montréal/Laval/Brossard, Québec, Gatineau",
171 "cadence": "horaire (watcher PM2)",
172 "statut": "actif",
173 "note": "Fonctionne sans clé (scraping découverte). TICKETMASTER_API_KEY optionnelle : active le mode API riche (prix, artistes, images)."
174 },
175 {
176 "id": "bandsintown",
177 "name": "Bandsintown — dates de concerts par artiste",
178 "url": "https://rest.bandsintown.com/",
179 "type": "agregateur",
180 "extraction": "API publique /artists/{artiste}/events (app_id format widget « js_www.sorti-ka.com », testé accepté 2026-08 ; un app_id arbitraire simple est refusé) — pas de recherche géographique : on interroge les artistes déjà extraits de la base (evenko/lepointdevente/ticketmaster, événements à venir), plafond 100 artistes/sync avec rotation et cache data/bandsintown_cache.json (re-visite 3 j) ; on ne garde que les dates au Québec (venue.region=QC) : datetime local (date+heure), salle géolocalisée, lineup → artistes, free → gratuité, lien billets",
181 "acces_legal": "API publique Bandsintown (app_id d'identification) — usage index + lien vers la fiche originale",
182 "couverture": "Concerts au Québec des artistes en tournée connus de la base",
183 "cadence": "horaire (watcher PM2, rotation d'artistes)",
184 "statut": "actif"
185 },
186 {
187 "id": "gatineau",
188 "name": "Ville de Gatineau — Événements",
189 "url": "https://www.gatineau.ca/",
190 "type": "municipal",
191 "extraction": "n/d",
192 "acces_legal": "n/d",
193 "couverture": "Gatineau (Outaouais)",
194 "cadence": "n/d",
195 "statut": "abandonnee",
196 "note": "Aucun jeu de données « événements » sur Données Québec (portail Gatineau : géomatique seulement) et site 100 % JavaScript sans API découverte (vérifié 2026-08-18)"
197 },
198 {
199 "id": "trois-rivieres",
200 "name": "Ville de Trois-Rivières — Événements",
201 "url": "https://www.v3r.net/",
202 "type": "municipal",
203 "extraction": "n/d",
204 "acces_legal": "n/d",
205 "couverture": "Trois-Rivières (Mauricie)",
206 "cadence": "n/d",
207 "statut": "abandonnee",
208 "note": "API REST WordPress verrouillée (401 rest_not_logged_in) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
209 },
210 {
211 "id": "levis",
212 "name": "Ville de Lévis — Calendrier",
213 "url": "https://www.ville.levis.qc.ca/",
214 "type": "municipal",
215 "extraction": "n/d",
216 "acces_legal": "n/d",
217 "couverture": "Lévis (Chaudière-Appalaches)",
218 "cadence": "n/d",
219 "statut": "abandonnee",
220 "note": "Site derrière un WAF (403 sur GET identifiés) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
221 },
222 {
223 "id": "terrebonne",
224 "name": "Ville de Terrebonne — Calendrier",
225 "url": "https://terrebonne.ca/calendrier/",
226 "type": "municipal",
227 "extraction": "n/d",
228 "acces_legal": "n/d",
229 "couverture": "Terrebonne (Lanaudière)",
230 "cadence": "n/d",
231 "statut": "abandonnee",
232 "note": "Cloudflare bloque tout GET non navigateur (403) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
233 },
234 {
235 "id": "lavalweb",
236 "name": "Ville de Laval — calendrier vivant laval.ca",
237 "url": "https://www.laval.ca/calendrier-activites/",
238 "type": "municipal",
239 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = WordPress rendu serveur, pagination ?findstr[group]=events&findstr[page]=N (58 pages × ~8 cartes ≈ 460 activités à venir mesurées) ; l'API REST /wp-json/wp/v2/events (764 fiches) ne publie PAS la date de l'événement → listes SSR retenues ; volume mesuré ~460 ; couverture = activités municipales/culturelles/sportives Laval ; robots.txt lu : Disallow /wp-admin/ seulement ; effort ●●.",
240 "extraction": "listes SSR paginées (cartes .event-item : date FR « 18 août 2026 | 18 h » → date + heure, titre, lieu, lien, image lazy) + fiches détail incrémentales (cache data/lavalweb_cache.json, LAVALWEB_FICHE_MAX/sync, re-visite 7 j) : og:description ; GPS via annuaire de salles + géocodeur",
241 "acces_legal": "robots.txt laval.ca : Disallow /wp-admin/ seulement — pages calendrier publiques, GET throttlés 0,5 s, identification honnête",
242 "couverture": "Activités municipales, Laval (remplace le jeu Données Québec mort à la source depuis 2025-12)",
243 "cadence": "horaire (watcher PM2, incrémental)",
244 "statut": "actif"
245 },
246 {
247 "id": "outaouais",
248 "name": "Tourisme Outaouais — calendrier des événements (ATR)",
249 "url": "https://tourismeoutaouais.com/calendrier-des-evenements",
250 "type": "tourisme-regional",
251 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = site Astro, la page calendrier embarque TOUT le calendrier dans les props sérialisées de l'îlot CalendrierDesEvenements (162 événements dont 25 futurs mesurés) — 1 GET/sync ; l'API GraphQL admin.tourismeoutaouais.com exige un jeton (403) ; robots.txt lu : Allow / ; effort ●.",
252 "extraction": "props JSON de l'îlot Astro (déballage [type, valeur]) : id, titre, url (interne ou site officiel), municipalité, territoire, photo CDN, startDate/endDate ISO ; ni heure ni tarif publiés → jamais inventés",
253 "acces_legal": "robots.txt : User-agent: * / Allow: / — GET direct throttlé 0,8 s, identification honnête, lien vers la fiche/site officiel",
254 "couverture": "Festivals et événements, Outaouais (337 fiches seulement au baseline régional)",
255 "cadence": "horaire (watcher PM2)",
256 "statut": "actif"
257 },
258 {
259 "id": "lanaudiere",
260 "name": "Tourisme Lanaudière — événements (ATR)",
261 "url": "https://lanaudiere.ca/fr/evenements-lanaudiere",
262 "type": "tourisme-regional",
263 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = CMS Umbraco, liste SSR limitée à ~10 cartes (44 résultats annoncés, reste en JS) → passage par le SITEMAP officiel (83 URLs de fiches fr mesurées) ; fiches structurées (member-title-1-1 : titre, plage de dates FR, municipalité) + og:image Tourinsoft + og:description ; robots.txt lu : Disallow /*? (aucune URL à paramètres requêtée) + Crawl-delay 2 s (respecté : GET séquentiels 2,1 s) ; effort ●●.",
264 "extraction": "sitemap.xml (1 GET) → fiches /fr/evenements-lanaudiere/<slug>/ incrémentales (cache data/lanaudiere_cache.json, LAN_FICHE_MAX/sync, re-visite 7 j) : titre, plage de dates FR, municipalité, og:image/og:description ; médias Tourinsoft → recoupement SITQ géré par dedup_key",
265 "acces_legal": "robots.txt : Disallow /*? + Crawl-delay: 2 — respectés à la lettre (aucun paramètre d'URL, 2,1 s entre GET), identification honnête",
266 "couverture": "Festivals et événements, Lanaudière",
267 "cadence": "horaire (watcher PM2, incrémental)",
268 "statut": "actif"
269 },
270 {
271 "id": "saguenaysorties",
272 "name": "Sorties Saguenay — portail événementiel de la Ville",
273 "url": "https://sorties.saguenay.ca/",
274 "type": "municipal",
275 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = portail SSR de la Ville de Saguenay motorisé par les données La Vitrine (médias lvc-medias.lavitrine.com) ; pagination POST /ajax/plus (36 séances/lot, ~300-400 séances mesurées entre offset 0 et 500) ; détails POST /ajax/details (description + adresse) ; chaque carte = une séance horodatée (date + heure + ville + salle) ; robots.txt : non publié (page de maintenance) → volumes modestes et throttle 0,8 s ; effort ●●. Remplace le CSV Données Québec mort (2022-06).",
276 "extraction": "POST /ajax/plus paginé (SAG_BATCH_MAX lots) : discipline, date FR, heure, titre, « Ville, Salle », image, detail-id → une fiche PAR séance ; détails incrémentaux POST /ajax/details (cache data/saguenaysorties_cache.json, SAG_DETAIL_MAX/sync, re-visite 7 j) : description, adresse civique",
277 "acces_legal": "portail public municipal — POST throttlés 0,8 s, identification honnête, lien fiche officielle /festivals?id=<id> ; recoupement lavitrine/lepointdevente géré par dedup_key",
278 "couverture": "Spectacles, expositions, festivals — Saguenay et environs (Saguenay–Lac-Saint-Jean)",
279 "cadence": "horaire (watcher PM2, incrémental)",
280 "statut": "actif"
281 },
282 {
283 "id": "bassaintlaurent",
284 "name": "Tourisme Bas-Saint-Laurent — événements (ATR)",
285 "url": "https://www.bassaintlaurent.ca/fr/quoi-faire/evenements/",
286 "type": "tourisme-regional",
287 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = WordPress (Bedrock) rendu serveur, 27 cartes complètes sur la liste (titre, plage de dates FR, municipalité, image) ; fiches avec GPS exact embarqué + og:description ; robots.txt lu : rien d'interdit sur /fr/ ; volume mesuré 27 ; couverture = BSL (168 fiches seulement au baseline) ; effort ●.",
288 "extraction": "liste SSR (1 GET, cartes .card--type-event) + fiches incrémentales (cache data/bassaintlaurent_cache.json, BSL_FICHE_MAX/sync, re-visite 7 j) : og:description + GPS exact de la carte embarquée",
289 "acces_legal": "pages publiques de l'ATR — GET throttlés 0,8 s, identification honnête, lien fiche originale",
290 "couverture": "Festivals et événements, Bas-Saint-Laurent",
291 "cadence": "horaire (watcher PM2, incrémental)",
292 "statut": "actif"
293 },
294 {
295 "id": "quebecanimee",
296 "name": "Québec animée — Ville de Québec",
297 "url": "https://animee.ville.quebec.qc.ca/activites/",
298 "type": "municipal",
299 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = portail municipal SSR ; la liste /activites/ porte TOUT en attributs data- (date-debut/date-fin ISO, arrondissement, catégories, prix « Gratuit ») : 126 séances / 73 activités mesurées, 1 GET ; fiches pour description/lieu ; le calendrier général ville.quebec.qc.ca n'expose pas de flux structuré (vérifié) ; effort ●. Comble l'absence de source municipale/gratuite de la 2ᵉ ville (inventaire Capitale-Nationale ~100 % billetteries).",
300 "extraction": "liste /activites/ (1 GET, attributs data-* : dates ISO, arrondissement, catégorie, prix) + fiches incrémentales (cache data/quebecanimee_cache.json, QA_FICHE_MAX/sync, re-visite 7 j) : og:description, lieu ; une carte par occurrence datée → une fiche par séance",
301 "acces_legal": "site municipal public rendu serveur — GET throttlés 0,8 s, identification honnête, lien fiche officielle",
302 "couverture": "Activités gratuites/à faible coût, Ville de Québec (Capitale-Nationale)",
303 "cadence": "horaire (watcher PM2, incrémental)",
304 "statut": "actif"
305 },
306 {
307 "id": "placedesarts",
308 "name": "Place des Arts — calendrier multi-salles",
309 "url": "https://www.placedesarts.com/programmation",
310 "type": "salle-de-spectacle",
311 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = Craft CMS rendu serveur ; /programmation liste 97 spectacles (6 salles) ; chaque fiche embarque #eventbannerdata + #eventGTM (JSON : titre, salle, producteur, discipline, plage de dates) + barre latérale des représentations avec heure ET PRIX RÉELS (« 33 $ à 97,50 $ ») — 23 séances mesurées sur la fiche sonde ; robots.txt lu : Disallow /cpresources/ /vendor/ /cache/ seulement ; effort ●●. Meilleure candidate unitaire de la famille « salles » (audit §C).",
312 "extraction": "liste /programmation (1 GET) → fiches /evenement/<slug> incrémentales (cache data/placedesarts_cache.json, PDA_FICHE_MAX/sync, re-visite 7 j) : une fiche Sorti-Ka PAR représentation (date + heure + prix réels), salle précise, producteur → organisateur, discipline → catégories",
313 "acces_legal": "robots.txt : Disallow /cpresources/ /vendor/ /cache/ seulement — GET throttlés 0,8 s, identification honnête, lien billetterie officielle ; recoupement lavitrine/evenko géré par dedup_key",
314 "couverture": "Spectacles des 6 salles de la Place des Arts, Montréal (~97 spectacles, plusieurs centaines de séances)",
315 "cadence": "horaire (watcher PM2, incrémental)",
316 "statut": "actif"
317 },
318 {
319 "id": "lhjmq",
320 "name": "LHJMQ — matchs des équipes québécoises",
321 "url": "https://theqmjhl.ca/",
322 "type": "sport",
323 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = API HockeyTech « modulekit » (lscluster.hockeytech.com, JSON, clé publique de la ligue embarquée dans ses propres sites) — saison 2026-27 : 580 matchs mesurés dont ~384 à domicile dans 12 villes QC (Chicoutimi, Baie-Comeau, Rimouski, Rouyn-Noranda, Val-d'Or, Gatineau, Shawinigan, Drummondville, Victoriaville, Boisbriand, Sherbrooke, Québec) ; date + heure + aréna structurés ; 2 GET/sync ; effort ●. Fort levier d'équilibre régional (Côte-Nord, Abitibi, BSL…).",
324 "extraction": "API modulekit (view=seasons puis view=schedule) : matchs à domicile au Québec seulement → titre « LHJMQ : Visiteur c. Receveur », date + heure locales, aréna, ville, statut annulé si publié ; prix non publié → jamais inventé",
325 "acces_legal": "flux JSON public qui motorise le site officiel de la ligue — usage index + lien fiche de match officielle (theqmjhl.ca/gamecentre/<id>), GET throttlés 1 s, identification honnête",
326 "couverture": "~384 matchs/saison à domicile au Québec, 12 villes dans 10 régions",
327 "cadence": "horaire (watcher PM2)",
328 "statut": "actif"
329 },
330 {
331 "id": "sallesjsonld",
332 "name": "Salles de spectacle — moteur générique JSON-LD Event",
333 "url": "https://grandtheatre.qc.ca/programmation",
334 "type": "salle-de-spectacle",
335 "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : moteur configurable (data/salles_jsonld.json, patron stores.json de fabri-ka) pour la longue traîne des salles qui publient schema.org/Event sur leurs fiches. Sondage 2026-08-19 : AVEC Event JSON-LD = Grand Théâtre de Québec (108 fiches listées), Théâtre du Trident, Théâtre Capitole (via gestev.com) ; SANS (écartées de la config, re-sonder plus tard) = Palais Montcalm, Le Diamant, Impérial Bell, MTelus/Corona/Club Soda (evenko, déjà couverts), Petit Champlain / Albert-Rousseau (plateforme JS « pages/collections ») ; robots.txt lu salle par salle avant ajout ; effort ●● (moteur) puis ● par salle.",
336 "extraction": "par salle configurée : page(s) de programmation → liens de fiche (motif regex) → fiches incrémentales (cache data/sallesjsonld_cache.json, SALLES_FICHE_MAX/sync partagé, re-visite 7 j) → blocs JSON-LD @type Event (nom, dates horodatées, salle, adresse, offres → prix, statut, image) ; heure « T00:00:00 » traitée comme placeholder",
337 "acces_legal": "robots.txt vérifié PAR SALLE avant ajout à la config (consigné dans data/salles_jsonld.json) — GET throttlés 0,8 s, identification honnête, lien fiche originale",
338 "couverture": "Longue traîne des salles de spectacle QC (amorce : Grand Théâtre de Québec, Trident, Capitole — Capitale-Nationale)",
339 "cadence": "horaire (watcher PM2, incrémental)",
340 "statut": "actif"
341 },
342 {
343 "id": "mauricie",
344 "name": "Tourisme Mauricie — événements (ATR)",
345 "url": "https://tourismemauricie.com/evenements",
346 "type": "tourisme-regional",
347 "extraction": "n/d",
348 "acces_legal": "n/d",
349 "couverture": "Mauricie",
350 "cadence": "n/d",
351 "statut": "abandonnee",
352 "note": "ÉCARTÉE 2026-08-19 (Phase 3) : liste /evenements rendue JS — 1 seule carte SSR par page (vérifié pages 1 et 2), cartes chargées par admin-ajax non documenté, API REST /wp-json verrouillée (401 rest_not_logged_in). À re-sonder (percer la facette admin-ajax) dans une vague ultérieure."
353 },
354 {
355 "id": "laurentides",
356 "name": "Tourisme Laurentides — événements (ATR)",
357 "url": "https://www.laurentides.com/fr/quoifaire/evenements",
358 "type": "tourisme-regional",
359 "extraction": "n/d",
360 "acces_legal": "n/d",
361 "couverture": "Laurentides",
362 "cadence": "n/d",
363 "statut": "abandonnee",
364 "note": "ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Scrapfly possible mais non justifié (la région est déjà la 4ᵉ mieux couverte : 620 fiches au baseline). À re-sonder."
365 },
366 {
367 "id": "gaspesie",
368 "name": "Tourisme Gaspésie — événements (ATR)",
369 "url": "https://www.tourisme-gaspesie.com/evenements-gaspesie.html",
370 "type": "tourisme-regional",
371 "extraction": "n/d",
372 "acces_legal": "n/d",
373 "couverture": "Gaspésie–Îles-de-la-Madeleine",
374 "cadence": "n/d",
375 "statut": "abandonnee",
376 "note": "ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Région faible (60 fiches au baseline) : candidate prioritaire pour une vague Scrapfly consignée (crédits) ou un partenariat. À re-sonder."
377 },
378 {
379 "id": "tuxedobillet",
380 "name": "Tuxedo Billet — billetterie Capitale-Nationale",
381 "url": "https://www.tuxedobillet.com/",
382 "type": "billetterie",
383 "extraction": "n/d",
384 "acces_legal": "n/d",
385 "couverture": "Salles de Québec/Lévis (Grand Théâtre, Palais Montcalm…)",
386 "cadence": "n/d",
387 "statut": "abandonnee",
388 "note": "ÉCARTÉE 2026-08-19 (Phase 3) : site en PANNE aux sondes — www = page « failover site », apex = erreur TLS (curl 60). Le Grand Théâtre et le Capitole sont couverts via sallesjsonld en attendant. À re-sonder."
389 },
390 {
391 "id": "clubs-pros",
392 "name": "Clubs professionnels (CF Montréal, Alouettes, Rocket, Victoire…)",
393 "url": "https://www.cfmontreal.com/",
394 "type": "sport",
395 "extraction": "n/d",
396 "acces_legal": "n/d",
397 "couverture": "Montréal/Laval (MLS, LCF, AHL, LPHF)",
398 "cadence": "n/d",
399 "statut": "abandonnee",
400 "note": "ÉCARTÉE 2026-08-19 (Phase 3) : les matchs à domicile passent déjà par les billetteries connectées (evenko/ticketmaster : Canadiens 74 fiches, Remparts 31…) — des connecteurs de ligue dédiés (MLS/LCF/AHL) apporteraient surtout des doublons pour ~150 fiches nettes/an. La LHJMQ, elle, est connectée (vrai levier régional). À revisiter si un besoin statut/heure officiel apparaît."
401 }
402 ]
403 }
2 + "_comment": "Sorti-Ka — Registre des sources d'événements (province de Québec). Auteur : Simon-Pierre Boucher — contact@spboucher.ai",
3 + "sources": [
4 + {
5 + "id": "sitq",
6 + "name": "SIT Québec — Ministère du Tourisme",
7 + "url": "https://www.donneesquebec.ca/recherche/dataset/sit-quebec-evenements",
8 + "type": "donnees-ouvertes",
9 + "extraction": "API JSON (Tourinsoft, syndication publique) — dates, géoloc, thèmes, contacts (TelephonePrincipals/Courriels → description « Renseignements : … ») ; le flux ne publie ni Descriptions* ni tarifs (vérifié 2026-08)",
10 + "acces_legal": "Licence Creative Commons 4.0 (Données Québec) — API officielle publiée par le Ministère du Tourisme",
11 + "couverture": "Événements touristiques, toutes les régions du Québec",
12 + "cadence": "horaire (watcher PM2)",
13 + "statut": "actif"
14 + },
15 + {
16 + "id": "montreal",
17 + "name": "Ville de Montréal — Événements publics",
18 + "url": "https://donnees.montreal.ca/dataset/evenements-publics",
19 + "type": "donnees-ouvertes",
20 + "extraction": "API CKAN datastore_search (JSON, paginée) — venue = emplacement (si vrai nom de lieu) ou titre_adresse ; address = adresse_principale + adresse_secondaire ; le jeu de données ne publie pas l'heure → heure locale captée dans le JSON-LD des fiches montreal.ca (« startDate »:\"…T19:30:00-04:00\") lors de l'enrichissement og:image incrémental (cache, plafond MTL_IMG_MAX/sync)",
21 + "acces_legal": "Licence CC BY 4.0 (donnees.montreal.ca) — attribution : Ville de Montréal",
22 + "couverture": "Événements municipaux, île de Montréal",
23 + "cadence": "horaire (watcher PM2)",
24 + "statut": "actif"
25 + },
26 + {
27 + "id": "lepointdevente",
28 + "name": "Le point de vente — billetterie québécoise",
29 + "url": "https://lepointdevente.com/billets",
30 + "type": "billetterie",
31 + "extraction": "pages de liste HTML rendues serveur (paginées), parse des cartes (titre, date FR + heure « 17 août 2026, 20h00 » → start_time, salle, ville, image) + fiches détail /billets/<id> incrémentales (cache data/lepointdevente_cache.json, 150 fiches/sync, re-visite 7 j) : description og:description, heure précise, tarifs JSON réels via /plugins/rates/<id> → price_min/price_label/is_free ; artistes extraits des titres par patrons sûrs seulement (« Artiste - Salle », « X en concert »)",
32 + "acces_legal": "robots.txt : « User-Agent: * / Allow: * » — GET throttlés, identification honnête, lien direct vers chaque billetterie",
33 + "couverture": "Concerts, humour, théâtre, festivals — partout au Québec (~2 500 événements à venir)",
34 + "cadence": "horaire (watcher PM2)",
35 + "statut": "actif"
36 + },
37 + {
38 + "id": "laval",
39 + "name": "Ville de Laval — Calendrier des activités",
40 + "url": "https://www.donneesquebec.ca/recherche/dataset/calendrier-des-activites",
41 + "type": "donnees-ouvertes",
42 + "extraction": "fichier JSON officiel (Données Québec) — EventDate/EndDate publiés en UTC (vérifié 2026-08) → convertis en date + heure locales Québec (minuit local = « toute la journée », pas d'heure) ; détection du préfixe « ANNULÉ - » du titre → statut cancelled (Phase 2)",
43 + "acces_legal": "Licence CC 4.0 (Données Québec) — Ville de Laval",
44 + "couverture": "Activités municipales, Laval",
45 + "cadence": "horaire (watcher PM2)",
46 + "statut": "morte-a-la-source",
47 + "decision": "2026-08-19 (audit Phase 1 + Phase 2) : le jeu ouvert est MORT À LA SOURCE — 293 enregistrements, EventDate max 2025-12-09, plus aucun événement futur publié depuis fin 2025 alors que le connecteur tourne sans erreur (41 runs verts). Le connecteur reste branché (archivage naturel du stock + réveil automatique si la Ville republie), mais la source ne compte plus comme active. REMPLACEMENT prévu en Phase 3 : calendrier vivant laval.ca / Tourisme Laval (voir 04-SOURCES-CANDIDATES de l'audit).",
48 + "note": " REMPLACÉE 2026-08-19 (Phase 3) par la source « lavalweb » (calendrier vivant laval.ca)."
49 + },
50 + {
51 + "id": "sherbrooke",
52 + "name": "Ville de Sherbrooke — Calendrier des événements",
53 + "url": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites",
54 + "type": "municipal",
55 + "extraction": "la page rend TOUT le calendrier dans window['TRANSFER_CACHE'] (JSON embarqué, CMS Maruche) → 1 seule requête, ~270 événements (nom, périodes en UTC → date + heure locales Québec, gratuité `payant`, catégories, clientèles, images)",
56 + "acces_legal": "page municipale publique (référencée sur Données Québec) — GET direct, identification honnête",
57 + "couverture": "Événements municipaux et culturels, Sherbrooke (Estrie)",
58 + "cadence": "horaire (watcher PM2)",
59 + "statut": "actif"
60 + },
61 + {
62 + "id": "longueuil",
63 + "name": "Ville de Longueuil — Événements (nouveau portail)",
64 + "url": "https://longueuil.quebec/fr/evenements",
65 + "type": "municipal",
66 + "extraction": "page liste rendue serveur (portail Nuxt) — cartes <article class=\"event__item\"> : <time datetime> (date + heure locales, début/fin), titre, lien, extrait ; enrichissement par fiche incrémental (cache data/longueuil_cache.json, 30 fiches/sync, re-visite 30 j) : adresse civique, code postal, municipalité depuis l'état Nuxt (address_line1/postal_code/locality) ; GPS via le géocodeur central",
67 + "acces_legal": "portail municipal public, rendu serveur — GET throttlés 0,8 s, identification honnête",
68 + "couverture": "Événements municipaux, Longueuil (Montérégie)",
69 + "cadence": "horaire (watcher PM2, incrémental)",
70 + "statut": "actif",
71 + "note": "Remplace le jeu Données Québec « evenements » périmé (dernière date 2017-03, vérifié 2026-08-17) par le portail vivant longueuil.quebec."
72 + },
73 + {
74 + "id": "eventbrite",
75 + "name": "Eventbrite — billetterie libre-service (Québec)",
76 + "url": "https://www.eventbrite.ca/d/canada--montreal/all-events/",
77 + "type": "billetterie",
78 + "extraction": "pages de découverte publiques /d/<lieu>/all-events/?page=N (7 lieux QC pondérés : Montréal, Québec, Laval, Gatineau, Sherbrooke, Longueuil, Trois-Rivières) — window.__SERVER_DATA__ livre 20 résultats/page : nom, résumé, date + heure locales, salle géocodée (lat/lng, adresse, code postal), image, catégories (tags Eventbrite*), URL ; prix par fiche détail /e/<slug> incrémentale (JSON-LD AggregateOffer lowPrice/highPrice CAD ; cache data/eventbrite_cache.json, 150 fiches/sync, re-visite 7 j) ; en ligne exclus, filtre region=QC strict",
79 + "acces_legal": "robots.txt « User-agent: * » n'interdit ni /d/ ni /e/ (seuls /directory/, checkout et endpoints privés) — GET throttlés 0,8 s, identification honnête, lien direct vers chaque fiche",
80 + "couverture": "Événements communautaires, ateliers, festivals, affaires — grandes villes du Québec",
81 + "cadence": "horaire (watcher PM2, incrémental)",
82 + "statut": "actif"
83 + },
84 + {
85 + "id": "cantonsdelest",
86 + "name": "Tourisme Cantons-de-l'Est — calendrier officiel (ATR Estrie)",
87 + "url": "https://www.cantonsdelest.com/evenements",
88 + "type": "tourisme-regional",
89 + "extraction": "page liste rendue serveur (~25 fiches à venir) puis fiches détail incrémentales (cache data/cantonsdelest_cache.json, 40 fiches/sync, re-visite 7 j) : JSON-LD Event complet (nom, dates, description riche, adresse civique, images Tourinsoft mto.media.tourinsoft.eu) + GPS exact (data-lat/data-lng de la carte) ; même plateforme Tourinsoft que le SIT Québec → recoupement géré par dedup_key",
90 + "acces_legal": "pages publiques de l'ATR, rendu serveur — GET throttlés 0,8 s, identification honnête, lien vers la fiche originale",
91 + "couverture": "Festivals, spectacles, événements — Cantons-de-l'Est (Estrie)",
92 + "cadence": "horaire (watcher PM2, incrémental)",
93 + "statut": "actif"
94 + },
95 + {
96 + "id": "saguenay",
97 + "name": "Ville de Saguenay — Événements culturels",
98 + "url": "https://www.donneesquebec.ca/recherche/dataset/sag_evenements-culturels",
99 + "type": "donnees-ouvertes",
100 + "extraction": "CSV (Données Québec)",
101 + "acces_legal": "Licence CC 4.0 (Données Québec)",
102 + "couverture": "Saguenay",
103 + "cadence": "n/d",
104 + "statut": "abandonnee",
105 + "note": "Jeu de données périmé — dernière date d'événement : 2022-06 (vérifié 2026-08-17). REMPLACÉE 2026-08-19 (Phase 3) par la source « saguenaysorties » (portail sorties.saguenay.ca)."
106 + },
107 + {
108 + "id": "repentigny",
109 + "name": "Ville de Repentigny — Événements",
110 + "url": "https://www.donneesquebec.ca/recherche/dataset/evenement-sur-le-territoire-de-la-ville",
111 + "type": "donnees-ouvertes",
112 + "extraction": "CSV (Données Québec)",
113 + "acces_legal": "Licence CC 4.0 (Données Québec)",
114 + "couverture": "Repentigny (Lanaudière)",
115 + "cadence": "n/d",
116 + "statut": "abandonnee",
117 + "note": "Jeu de données périmé — dernière date d'événement : 2021-09 (vérifié 2026-08-17)"
118 + },
119 + {
120 + "id": "evenko",
121 + "name": "evenko — grands concerts et festivals",
122 + "url": "https://evenko.ca/fr/evenements",
123 + "type": "promoteur",
124 + "extraction": "API /api/search (proxy Algolia) — format percé : GET ?body=base64({params:[query,options],lang:'fr-CA'}), filters entity_type:evenko_show, ~5 requêtes/sync ; heure locale du spectacle via show_time (epoch → America/Montreal, respect de hide_show_time) ; artistes = champ headliners explicite ; gratuité : champ free (sondé null sur 100 % des hits, 2026-08) + étiquettes « gratuit » (event_tag/promotional_tag) ; aucun montant exposé par l'index",
125 + "acces_legal": "robots.txt : Allow /api/search* (explicite) — lien billets = URL Ticketmaster officielle",
126 + "couverture": "Grands concerts (Centre Bell, Place Bell, MTELUS…), festivals (Osheaga, ÎleSoniq…)",
127 + "cadence": "horaire (watcher PM2)",
128 + "statut": "actif"
129 + },
130 + {
131 + "id": "atuvu",
132 + "name": "Atuvu.ca — calendrier culturel",
133 + "url": "https://atuvu.ca/",
134 + "type": "agregateur",
135 + "extraction": "Scrapfly (Cloudflare) — sitemaps lastmod + fiches .event-play (date FR + heure « 20h00 » → start_time), cache incrémental data/atuvu_cache.json, plafond 120 fiches/sync",
136 + "acces_legal": "robots.txt : Allow / + sitemap public",
137 + "couverture": "Sorties culturelles, Grand Montréal",
138 + "cadence": "horaire (watcher PM2, incrémental)",
139 + "statut": "actif"
140 + },
141 + {
142 + "id": "lavitrine",
143 + "name": "La Vitrine culturelle",
144 + "url": "https://www.lavitrine.com/",
145 + "type": "billetterie",
146 + "extraction": "GET directs — sitemap.xml (2 000 fiches evenement + 185 exposition) + fiches HTML (bloc event_calendar), cache incrémental, 150 fiches/sync, re-visite 7 j",
147 + "acces_legal": "robots.txt : Allow / + Content-Signal search=yes, use=reference — usage index + lien vers la fiche originale",
148 + "couverture": "Spectacles et sorties culturelles, Grand Montréal",
149 + "cadence": "horaire (watcher PM2, incrémental)",
150 + "statut": "actif"
151 + },
152 + {
153 + "id": "brossard",
154 + "name": "Ville de Brossard — Calendrier des événements",
155 + "url": "https://brossard.ca/calendrier-des-evenements/",
156 + "type": "municipal",
157 + "extraction": "API REST WordPress publique /wp-json/wp/v2/city-event (JSON paginé, ~400 fiches) + dates/heures par fiche : bannière « Cet événement aura lieu le … » (fiches ville) ou JSON-LD Event (fiches biblio.brossard.ca) — cache incrémental data/brossard_cache.json, 80 fiches/sync, re-visite 7 j ; repli : date entre parenthèses dans le titre",
158 + "acces_legal": "API REST WordPress publique (aucune authentification) — GET throttlés 0,8 s, identification honnête",
159 + "couverture": "Événements municipaux et de bibliothèque, Brossard (Montérégie)",
160 + "cadence": "horaire (watcher PM2, incrémental)",
161 + "statut": "actif"
162 + },
163 + {
164 + "id": "ticketmaster",
165 + "name": "Ticketmaster — Discovery API (Québec)",
166 + "url": "https://developer.ticketmaster.com/products-and-docs/apis/discovery-api/v2/",
167 + "type": "billetterie",
168 + "extraction": "SANS clé (mode par défaut) : scraping léger des pages découverte publiques www.ticketmaster.ca/discover/<ville>?page=N (montreal, quebec-city, gatineau) — l'état Next.js (__NEXT_DATA__, requête serveur cityEvents) livre 20 événements/page : titre, date UTC + fuseau → date/heure locales, salle géolocalisée (lat/lng), adresse, code postal, ville, URL billetterie, catégorie majeure, plan de salle (image) ; plafonds TM_MAX_EVENTS=360 / TM_PAGES_PER_CITY=8 ; Scrapfly ASP sans render_js en secours anti-bot ; aucun prix publié dans ce flux → jamais inventé. REPLI AUTOMATIQUE : si TICKETMASTER_API_KEY apparaît dans .env, bascule sur la Discovery API v2 officielle (priceRanges, attractions → artistes, images 16:9) — identifiants identiques, zéro doublon.",
169 + "acces_legal": "pages découverte publiques rendues serveur — robots.txt n'interdit pas /discover/ (seuls /api/ismds/*) ; GET throttlés 1 s, lien direct vers la billetterie officielle ; API officielle (clé gratuite) utilisée dès qu'une clé est fournie",
170 + "couverture": "Grands spectacles, sports et concerts — Montréal/Laval/Brossard, Québec, Gatineau",
171 + "cadence": "horaire (watcher PM2)",
172 + "statut": "actif",
173 + "note": "Fonctionne sans clé (scraping découverte). TICKETMASTER_API_KEY optionnelle : active le mode API riche (prix, artistes, images)."
174 + },
175 + {
176 + "id": "bandsintown",
177 + "name": "Bandsintown — dates de concerts par artiste",
178 + "url": "https://rest.bandsintown.com/",
179 + "type": "agregateur",
180 + "extraction": "API publique /artists/{artiste}/events (app_id format widget « js_www.sorti-ka.com », testé accepté 2026-08 ; un app_id arbitraire simple est refusé) — pas de recherche géographique : on interroge les artistes déjà extraits de la base (evenko/lepointdevente/ticketmaster, événements à venir), plafond 100 artistes/sync avec rotation et cache data/bandsintown_cache.json (re-visite 3 j) ; on ne garde que les dates au Québec (venue.region=QC) : datetime local (date+heure), salle géolocalisée, lineup → artistes, free → gratuité, lien billets",
181 + "acces_legal": "API publique Bandsintown (app_id d'identification) — usage index + lien vers la fiche originale",
182 + "couverture": "Concerts au Québec des artistes en tournée connus de la base",
183 + "cadence": "horaire (watcher PM2, rotation d'artistes)",
184 + "statut": "actif"
185 + },
186 + {
187 + "id": "gatineau",
188 + "name": "Ville de Gatineau — Événements",
189 + "url": "https://www.gatineau.ca/",
190 + "type": "municipal",
191 + "extraction": "n/d",
192 + "acces_legal": "n/d",
193 + "couverture": "Gatineau (Outaouais)",
194 + "cadence": "n/d",
195 + "statut": "abandonnee",
196 + "note": "Aucun jeu de données « événements » sur Données Québec (portail Gatineau : géomatique seulement) et site 100 % JavaScript sans API découverte (vérifié 2026-08-18)"
197 + },
198 + {
199 + "id": "trois-rivieres",
200 + "name": "Ville de Trois-Rivières — Événements",
201 + "url": "https://www.v3r.net/",
202 + "type": "municipal",
203 + "extraction": "n/d",
204 + "acces_legal": "n/d",
205 + "couverture": "Trois-Rivières (Mauricie)",
206 + "cadence": "n/d",
207 + "statut": "abandonnee",
208 + "note": "API REST WordPress verrouillée (401 rest_not_logged_in) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
209 + },
210 + {
211 + "id": "levis",
212 + "name": "Ville de Lévis — Calendrier",
213 + "url": "https://www.ville.levis.qc.ca/",
214 + "type": "municipal",
215 + "extraction": "n/d",
216 + "acces_legal": "n/d",
217 + "couverture": "Lévis (Chaudière-Appalaches)",
218 + "cadence": "n/d",
219 + "statut": "abandonnee",
220 + "note": "Site derrière un WAF (403 sur GET identifiés) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
221 + },
222 + {
223 + "id": "terrebonne",
224 + "name": "Ville de Terrebonne — Calendrier",
225 + "url": "https://terrebonne.ca/calendrier/",
226 + "type": "municipal",
227 + "extraction": "n/d",
228 + "acces_legal": "n/d",
229 + "couverture": "Terrebonne (Lanaudière)",
230 + "cadence": "n/d",
231 + "statut": "abandonnee",
232 + "note": "Cloudflare bloque tout GET non navigateur (403) et aucun jeu « événements » sur Données Québec (vérifié 2026-08-18)"
233 + },
234 + {
235 + "id": "lavalweb",
236 + "name": "Ville de Laval — calendrier vivant laval.ca",
237 + "url": "https://www.laval.ca/calendrier-activites/",
238 + "type": "municipal",
239 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = WordPress rendu serveur, pagination ?findstr[group]=events&findstr[page]=N (58 pages × ~8 cartes ≈ 460 activités à venir mesurées) ; l'API REST /wp-json/wp/v2/events (764 fiches) ne publie PAS la date de l'événement → listes SSR retenues ; volume mesuré ~460 ; couverture = activités municipales/culturelles/sportives Laval ; robots.txt lu : Disallow /wp-admin/ seulement ; effort ●●.",
240 + "extraction": "listes SSR paginées (cartes .event-item : date FR « 18 août 2026 | 18 h » → date + heure, titre, lieu, lien, image lazy) + fiches détail incrémentales (cache data/lavalweb_cache.json, LAVALWEB_FICHE_MAX/sync, re-visite 7 j) : og:description ; GPS via annuaire de salles + géocodeur",
241 + "acces_legal": "robots.txt laval.ca : Disallow /wp-admin/ seulement — pages calendrier publiques, GET throttlés 0,5 s, identification honnête",
242 + "couverture": "Activités municipales, Laval (remplace le jeu Données Québec mort à la source depuis 2025-12)",
243 + "cadence": "horaire (watcher PM2, incrémental)",
244 + "statut": "actif"
245 + },
246 + {
247 + "id": "outaouais",
248 + "name": "Tourisme Outaouais — calendrier des événements (ATR)",
249 + "url": "https://tourismeoutaouais.com/calendrier-des-evenements",
250 + "type": "tourisme-regional",
251 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = site Astro, la page calendrier embarque TOUT le calendrier dans les props sérialisées de l'îlot CalendrierDesEvenements (162 événements dont 25 futurs mesurés) — 1 GET/sync ; l'API GraphQL admin.tourismeoutaouais.com exige un jeton (403) ; robots.txt lu : Allow / ; effort ●.",
252 + "extraction": "props JSON de l'îlot Astro (déballage [type, valeur]) : id, titre, url (interne ou site officiel), municipalité, territoire, photo CDN, startDate/endDate ISO ; ni heure ni tarif publiés → jamais inventés",
253 + "acces_legal": "robots.txt : User-agent: * / Allow: / — GET direct throttlé 0,8 s, identification honnête, lien vers la fiche/site officiel",
254 + "couverture": "Festivals et événements, Outaouais (337 fiches seulement au baseline régional)",
255 + "cadence": "horaire (watcher PM2)",
256 + "statut": "actif"
257 + },
258 + {
259 + "id": "lanaudiere",
260 + "name": "Tourisme Lanaudière — événements (ATR)",
261 + "url": "https://lanaudiere.ca/fr/evenements-lanaudiere",
262 + "type": "tourisme-regional",
263 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = CMS Umbraco, liste SSR limitée à ~10 cartes (44 résultats annoncés, reste en JS) → passage par le SITEMAP officiel (83 URLs de fiches fr mesurées) ; fiches structurées (member-title-1-1 : titre, plage de dates FR, municipalité) + og:image Tourinsoft + og:description ; robots.txt lu : Disallow /*? (aucune URL à paramètres requêtée) + Crawl-delay 2 s (respecté : GET séquentiels 2,1 s) ; effort ●●.",
264 + "extraction": "sitemap.xml (1 GET) → fiches /fr/evenements-lanaudiere/<slug>/ incrémentales (cache data/lanaudiere_cache.json, LAN_FICHE_MAX/sync, re-visite 7 j) : titre, plage de dates FR, municipalité, og:image/og:description ; médias Tourinsoft → recoupement SITQ géré par dedup_key",
265 + "acces_legal": "robots.txt : Disallow /*? + Crawl-delay: 2 — respectés à la lettre (aucun paramètre d'URL, 2,1 s entre GET), identification honnête",
266 + "couverture": "Festivals et événements, Lanaudière",
267 + "cadence": "horaire (watcher PM2, incrémental)",
268 + "statut": "actif"
269 + },
270 + {
271 + "id": "saguenaysorties",
272 + "name": "Sorties Saguenay — portail événementiel de la Ville",
273 + "url": "https://sorties.saguenay.ca/",
274 + "type": "municipal",
275 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = portail SSR de la Ville de Saguenay motorisé par les données La Vitrine (médias lvc-medias.lavitrine.com) ; pagination POST /ajax/plus (36 séances/lot, ~300-400 séances mesurées entre offset 0 et 500) ; détails POST /ajax/details (description + adresse) ; chaque carte = une séance horodatée (date + heure + ville + salle) ; robots.txt : non publié (page de maintenance) → volumes modestes et throttle 0,8 s ; effort ●●. Remplace le CSV Données Québec mort (2022-06).",
276 + "extraction": "POST /ajax/plus paginé (SAG_BATCH_MAX lots) : discipline, date FR, heure, titre, « Ville, Salle », image, detail-id → une fiche PAR séance ; détails incrémentaux POST /ajax/details (cache data/saguenaysorties_cache.json, SAG_DETAIL_MAX/sync, re-visite 7 j) : description, adresse civique",
277 + "acces_legal": "portail public municipal — POST throttlés 0,8 s, identification honnête, lien fiche officielle /festivals?id=<id> ; recoupement lavitrine/lepointdevente géré par dedup_key",
278 + "couverture": "Spectacles, expositions, festivals — Saguenay et environs (Saguenay–Lac-Saint-Jean)",
279 + "cadence": "horaire (watcher PM2, incrémental)",
280 + "statut": "actif"
281 + },
282 + {
283 + "id": "bassaintlaurent",
284 + "name": "Tourisme Bas-Saint-Laurent — événements (ATR)",
285 + "url": "https://www.bassaintlaurent.ca/fr/quoi-faire/evenements/",
286 + "type": "tourisme-regional",
287 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = WordPress (Bedrock) rendu serveur, 27 cartes complètes sur la liste (titre, plage de dates FR, municipalité, image) ; fiches avec GPS exact embarqué + og:description ; robots.txt lu : rien d'interdit sur /fr/ ; volume mesuré 27 ; couverture = BSL (168 fiches seulement au baseline) ; effort ●.",
288 + "extraction": "liste SSR (1 GET, cartes .card--type-event) + fiches incrémentales (cache data/bassaintlaurent_cache.json, BSL_FICHE_MAX/sync, re-visite 7 j) : og:description + GPS exact de la carte embarquée",
289 + "acces_legal": "pages publiques de l'ATR — GET throttlés 0,8 s, identification honnête, lien fiche originale",
290 + "couverture": "Festivals et événements, Bas-Saint-Laurent",
291 + "cadence": "horaire (watcher PM2, incrémental)",
292 + "statut": "actif"
293 + },
294 + {
295 + "id": "quebecanimee",
296 + "name": "Québec animée — Ville de Québec",
297 + "url": "https://animee.ville.quebec.qc.ca/activites/",
298 + "type": "municipal",
299 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = portail municipal SSR ; la liste /activites/ porte TOUT en attributs data- (date-debut/date-fin ISO, arrondissement, catégories, prix « Gratuit ») : 126 séances / 73 activités mesurées, 1 GET ; fiches pour description/lieu ; le calendrier général ville.quebec.qc.ca n'expose pas de flux structuré (vérifié) ; effort ●. Comble l'absence de source municipale/gratuite de la 2ᵉ ville (inventaire Capitale-Nationale ~100 % billetteries).",
300 + "extraction": "liste /activites/ (1 GET, attributs data-* : dates ISO, arrondissement, catégorie, prix) + fiches incrémentales (cache data/quebecanimee_cache.json, QA_FICHE_MAX/sync, re-visite 7 j) : og:description, lieu ; une carte par occurrence datée → une fiche par séance",
301 + "acces_legal": "site municipal public rendu serveur — GET throttlés 0,8 s, identification honnête, lien fiche officielle",
302 + "couverture": "Activités gratuites/à faible coût, Ville de Québec (Capitale-Nationale)",
303 + "cadence": "horaire (watcher PM2, incrémental)",
304 + "statut": "actif"
305 + },
306 + {
307 + "id": "placedesarts",
308 + "name": "Place des Arts — calendrier multi-salles",
309 + "url": "https://www.placedesarts.com/programmation",
310 + "type": "salle-de-spectacle",
311 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = Craft CMS rendu serveur ; /programmation liste 97 spectacles (6 salles) ; chaque fiche embarque #eventbannerdata + #eventGTM (JSON : titre, salle, producteur, discipline, plage de dates) + barre latérale des représentations avec heure ET PRIX RÉELS (« 33 $ à 97,50 $ ») — 23 séances mesurées sur la fiche sonde ; robots.txt lu : Disallow /cpresources/ /vendor/ /cache/ seulement ; effort ●●. Meilleure candidate unitaire de la famille « salles » (audit §C).",
312 + "extraction": "liste /programmation (1 GET) → fiches /evenement/<slug> incrémentales (cache data/placedesarts_cache.json, PDA_FICHE_MAX/sync, re-visite 7 j) : une fiche Sorti-Ka PAR représentation (date + heure + prix réels), salle précise, producteur → organisateur, discipline → catégories",
313 + "acces_legal": "robots.txt : Disallow /cpresources/ /vendor/ /cache/ seulement — GET throttlés 0,8 s, identification honnête, lien billetterie officielle ; recoupement lavitrine/evenko géré par dedup_key",
314 + "couverture": "Spectacles des 6 salles de la Place des Arts, Montréal (~97 spectacles, plusieurs centaines de séances)",
315 + "cadence": "horaire (watcher PM2, incrémental)",
316 + "statut": "actif"
317 + },
318 + {
319 + "id": "lhjmq",
320 + "name": "LHJMQ — matchs des équipes québécoises",
321 + "url": "https://theqmjhl.ca/",
322 + "type": "sport",
323 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : accès constaté = API HockeyTech « modulekit » (lscluster.hockeytech.com, JSON, clé publique de la ligue embarquée dans ses propres sites) — saison 2026-27 : 580 matchs mesurés dont ~384 à domicile dans 12 villes QC (Chicoutimi, Baie-Comeau, Rimouski, Rouyn-Noranda, Val-d'Or, Gatineau, Shawinigan, Drummondville, Victoriaville, Boisbriand, Sherbrooke, Québec) ; date + heure + aréna structurés ; 2 GET/sync ; effort ●. Fort levier d'équilibre régional (Côte-Nord, Abitibi, BSL…).",
324 + "extraction": "API modulekit (view=seasons puis view=schedule) : matchs à domicile au Québec seulement → titre « LHJMQ : Visiteur c. Receveur », date + heure locales, aréna, ville, statut annulé si publié ; prix non publié → jamais inventé",
325 + "acces_legal": "flux JSON public qui motorise le site officiel de la ligue — usage index + lien fiche de match officielle (theqmjhl.ca/gamecentre/<id>), GET throttlés 1 s, identification honnête",
326 + "couverture": "~384 matchs/saison à domicile au Québec, 12 villes dans 10 régions",
327 + "cadence": "horaire (watcher PM2)",
328 + "statut": "actif"
329 + },
330 + {
331 + "id": "sallesjsonld",
332 + "name": "Salles de spectacle — moteur générique JSON-LD Event",
333 + "url": "https://grandtheatre.qc.ca/programmation",
334 + "type": "salle-de-spectacle",
335 + "evaluation": "ÉVALUATION 2026-08-19 (avant connexion) : moteur configurable (data/salles_jsonld.json, patron stores.json de fabri-ka) pour la longue traîne des salles qui publient schema.org/Event sur leurs fiches. Sondage 2026-08-19 : AVEC Event JSON-LD = Grand Théâtre de Québec (108 fiches listées), Théâtre du Trident, Théâtre Capitole (via gestev.com) ; SANS (écartées de la config, re-sonder plus tard) = Palais Montcalm, Le Diamant, Impérial Bell, MTelus/Corona/Club Soda (evenko, déjà couverts), Petit Champlain / Albert-Rousseau (plateforme JS « pages/collections ») ; robots.txt lu salle par salle avant ajout ; effort ●● (moteur) puis ● par salle.",
336 + "extraction": "par salle configurée : page(s) de programmation → liens de fiche (motif regex) → fiches incrémentales (cache data/sallesjsonld_cache.json, SALLES_FICHE_MAX/sync partagé, re-visite 7 j) → blocs JSON-LD @type Event (nom, dates horodatées, salle, adresse, offres → prix, statut, image) ; heure « T00:00:00 » traitée comme placeholder",
337 + "acces_legal": "robots.txt vérifié PAR SALLE avant ajout à la config (consigné dans data/salles_jsonld.json) — GET throttlés 0,8 s, identification honnête, lien fiche originale",
338 + "couverture": "Longue traîne des salles de spectacle QC (amorce : Grand Théâtre de Québec, Trident, Capitole — Capitale-Nationale)",
339 + "cadence": "horaire (watcher PM2, incrémental)",
340 + "statut": "actif"
341 + },
342 + {
343 + "id": "mauricie",
344 + "name": "Tourisme Mauricie — événements (ATR)",
345 + "url": "https://tourismemauricie.com/evenements",
346 + "type": "tourisme-regional",
347 + "extraction": "n/d",
348 + "acces_legal": "n/d",
349 + "couverture": "Mauricie",
350 + "cadence": "n/d",
351 + "statut": "abandonnee",
352 + "note": "ÉCARTÉE 2026-08-19 (Phase 3) : liste /evenements rendue JS — 1 seule carte SSR par page (vérifié pages 1 et 2), cartes chargées par admin-ajax non documenté, API REST /wp-json verrouillée (401 rest_not_logged_in). À re-sonder (percer la facette admin-ajax) dans une vague ultérieure."
353 + },
354 + {
355 + "id": "laurentides",
356 + "name": "Tourisme Laurentides — événements (ATR)",
357 + "url": "https://www.laurentides.com/fr/quoifaire/evenements",
358 + "type": "tourisme-regional",
359 + "extraction": "n/d",
360 + "acces_legal": "n/d",
361 + "couverture": "Laurentides",
362 + "cadence": "n/d",
363 + "statut": "abandonnee",
364 + "note": "ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Scrapfly possible mais non justifié (la région est déjà la 4ᵉ mieux couverte : 620 fiches au baseline). À re-sonder."
365 + },
366 + {
367 + "id": "gaspesie",
368 + "name": "Tourisme Gaspésie — événements (ATR)",
369 + "url": "https://www.tourisme-gaspesie.com/fr/festival-et-evenements/",
370 + "type": "tourisme-regional",
371 + "extraction": "Scrapfly (asp, country=ca, sans render_js — HTML rendu serveur) : liste (1 crédit) + fiches incrémentales (cache, GASP_FICHE_MAX=8/sync, re-visite 14 j) — titre h1, plage de dates FR, adresse/ville/CP (bloc coordonnées), og:description/og:image. Fiches sans date écartées (festivals annuels non datés).",
372 + "acces_legal": "Contenu public de l'ATR ; WAF contourné avec modération (budget ≤ 9 crédits Scrapfly/sync, consigné Phase 4), attribution + lien vers la fiche officielle",
373 + "couverture": "Gaspésie–Îles-de-la-Madeleine",
374 + "cadence": "horaire (watcher PM2) — cache 14 j, ~2 fiches re-visitées/j",
375 + "statut": "actif",
376 + "note": "RÉACTIVÉE 2026-08-21 (Phase 4) : la vague Scrapfly consignée annoncée en Phase 3. Ancienne URL /evenements-gaspesie.html morte (404) → /fr/festival-et-evenements/ (28 fiches, ~6 datées à l'amorce)."
377 + },
378 + {
379 + "id": "tuxedobillet",
380 + "name": "Tuxedo Billet — billetterie québécoise multi-tenants",
381 + "url": "https://www.tuxedobillet.com/",
382 + "type": "billetterie",
383 + "extraction": "Acteur Apify maison gorgeous_thistle/ka-tuxedo (Playwright, proxy résidentiel CA, code versionné dans apify/ka-tuxedo/) : les tenants <org>.tuxedobillet.com sont des SPA Angular sur Firebase (RTDB verrouillée, 401 même avec jeton anonyme) → rendu navigateur obligatoire. 1 run/TUX_TTL_HOURS (20 h), 29 tenants QC configurés dans data/tuxedo_tenants.json (ville vérifiée). Liste sans heure ni prix → jamais inventés.",
384 + "acces_legal": "Billetteries publiques ; rendu navigateur modéré (1 page/tenant/run, ~1 run par jour), lien d'achat vers la fiche officielle du tenant",
385 + "couverture": "29 salles/diffuseurs QC : Théâtre Outremont, Le Gesù, Centre Pierre-Péladeau, FTA, FIMAV, Espace Théâtre (Mont-Laurier), L'Anglicane (Lévis), Chandler, Lac-Mégantic, Îles-de-la-Madeleine…",
386 + "cadence": "quotidienne (TTL 20 h dans le watcher horaire)",
387 + "statut": "actif",
388 + "note": "RÉACTIVÉE 2026-08-21 (Phase 4) : l'apex reste vide mais les tenants vivent. Première source consommée via un acteur Apify (billetterie dure à crawler). Énumération des tenants non exhaustive — compléter via Serper/config."
389 + },
390 + {
391 + "id": "clubs-pros",
392 + "name": "Clubs professionnels (CF Montréal, Alouettes, Rocket, Victoire…)",
393 + "url": "https://www.cfmontreal.com/",
394 + "type": "sport",
395 + "extraction": "n/d",
396 + "acces_legal": "n/d",
397 + "couverture": "Montréal/Laval (MLS, LCF, AHL, LPHF)",
398 + "cadence": "n/d",
399 + "statut": "abandonnee",
400 + "note": "ÉCARTÉE 2026-08-19 (Phase 3) : les matchs à domicile passent déjà par les billetteries connectées (evenko/ticketmaster : Canadiens 74 fiches, Remparts 31…) — des connecteurs de ligue dédiés (MLS/LCF/AHL) apporteraient surtout des doublons pour ~150 fiches nettes/an. La LHJMQ, elle, est connectée (vrai levier régional). À revisiter si un besoin statut/heure officiel apparaît."
401 + },
402 + {
403 + "id": "monteregie",
404 + "name": "Tourisme Montérégie — événements (ATR)",
405 + "url": "https://www.tourisme-monteregie.qc.ca/membre/categorie/quoi-faire/festivals-et-evenements/",
406 + "type": "tourisme-regional",
407 + "extraction": "API REST WordPress publique /wp-json/wp/v2/cpt_event (paginée per_page=100) — titre, description, plage de dates FR, lat/long, site web, image (gisement Tourinsoft, acf.syndic_object_id EVENMTOV… ; recoupement SITQ géré par dedup_key). Fiches détail incrémentales (MTG_FICHE_MAX=15/sync, re-visite 7 j) : JSON-LD Event → adresse civique, ville, code postal.",
408 + "acces_legal": "API REST WordPress publique (aucune auth), robots.txt sans interdiction sur /wp-json/ — GET throttlés 0,8 s, UA honnête",
409 + "couverture": "Montérégie (~78 fiches à venir, vérifié 2026-08-21)",
410 + "cadence": "horaire (watcher PM2)",
411 + "statut": "actif"
412 + },
413 + {
414 + "id": "centreduquebec",
415 + "name": "Tourisme Centre-du-Québec — événements (ATR)",
416 + "url": "https://tourismecentreduquebec.com/evenements/",
417 + "type": "tourisme-regional",
418 + "extraction": "Liste rendue serveur (1 GET) : blocs events_listing (plage de dates FR + titre + lien) ; fiches incrémentales (CDQ_FICHE_MAX=10/sync, re-visite 7 j) : bloc « Adresse », og:description, og:image. Ni ville structurée, ni heure, ni tarif → jamais inventés (région dérivée de l'ATR).",
419 + "acces_legal": "Site public de l'ATR, robots.txt sans interdiction — GET throttlés 0,8 s, UA honnête",
420 + "couverture": "Centre-du-Québec (première source de la région — 0 fiche avant connexion ; ~10 fiches)",
421 + "cadence": "horaire (watcher PM2)",
422 + "statut": "actif"
423 + },
424 + {
425 + "id": "chaudiereappalaches",
426 + "name": "Tourisme Chaudière-Appalaches — festivals et événements (ATR)",
427 + "url": "https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/",
428 + "type": "tourisme-regional",
429 + "extraction": "Liste rendue serveur paginée (?listpage=1..3, ~12 fiches/page) → fiches incrémentales (CHA_FICHE_MAX=12/sync, re-visite 7 j) : JSON-LD Event COMPLET (dates ISO, description, adresse, ville, CP, GPS, images) via le parseur générique ld_events (sallesjsonld).",
430 + "acces_legal": "Site public de l'ATR, robots.txt sans interdiction sur /planifier-votre-sejour/ — GET throttlés 0,8 s, UA honnête",
431 + "couverture": "Chaudière-Appalaches (~36 fiches à venir, vérifié 2026-08-21)",
432 + "cadence": "horaire (watcher PM2)",
433 + "statut": "actif"
434 + },
435 + {
436 + "id": "ticketacces",
437 + "name": "TicketAcces.net — billetterie régionale québécoise",
438 + "url": "https://www.ticketacces.net/fr/portail/index.cfm",
439 + "type": "billetterie",
440 + "extraction": "HTML ColdFusion (cp1252) : annuaire complet des organisations dans le portail (cartes data-nom/ville/région, filtre Québec — 12 orgs NB + 1 ON exclues) + calendrier AGRÉGÉ multi-organisations en 4 GET (catégories Spectacles/Cinéma/Sport/Autre) : 1 Event par représentation datée (titre, date+heure FR, salle, ville, image). Fiches incrémentales (TA_DETAIL_MAX=25/sync) : og:description, adresse, prix plancher.",
441 + "acces_legal": "Billetterie publique, robots.txt sans interdiction sur le portail (vérifié 2026-08-21) ; GET throttlés 1 s, UA honnête, cookie de détection ColdFusion géré par session, lien d'achat vers la fiche officielle",
442 + "couverture": "47 organisations QC — dont 14 en Abitibi-Témiscamingue (Agora des Arts, Théâtre du cuivre, culture Val-d'Or/Amos/La Sarre/Malartic, Théâtre du Rift…), Bas-Saint-Laurent (Océanic, 3L), Côte-Nord (Drakkar), Charlevoix (Domaine Forget), Nord-du-Québec (Lebel-sur-Quévillon)… ~1 300 séances (vérifié 2026-08-21)",
443 + "cadence": "horaire (watcher PM2)",
444 + "statut": "actif"
445 + },
446 + {
447 + "id": "ovation",
448 + "name": "Réseau Ovation — billetterie québécoise multi-salles",
449 + "url": "https://reseau.ovation.ca/",
450 + "type": "billetterie",
451 + "extraction": "Sous-site mobile m.ovation.ca (HTML rendu serveur, AUCUN anti-bot) : par organisation (codes base36 validés par balayage, config data/ovation_orgs.json), /fr/Search/AllEvents paginé (?pageNumber=N) → 1 eventId = 1 représentation ; fiches fragment EventDescription (≈5 Ko, OVA_FICHE_MAX=40/sync) : date pleine FR + heure, catégories, ARTISTES (liens EventsByPerformer), description, salle + adresse + ville + code postal. Prix non exposé → jamais inventé.",
452 + "acces_legal": "Billetterie publique sans auth ; GET throttlés 0,7 s, UA honnête, lien d'achat vers la fiche officielle",
453 + "couverture": "26 organisations 100 % QC : Salle Albert-Rousseau, Petit Champlain, Diffusion Saguenay, Gatineau (Odyssée, Jean-Despréz), Vieux Terrebonne, Palace de Granby, Vieux Clocher Magog/Waterloo, La Tuque, Festival Western de St-Tite, clubs LHJMQ/LNAH…",
454 + "cadence": "horaire (watcher PM2)",
455 + "statut": "actif"
456 + }
457 + ]
458 +}
\ No newline at end of file
added data/tuxedo_tenants.json +33 −0
@@ -0,0 +1,33 @@
1 +{
2 + "_comment": "Sorti-Ka — Tenants Tuxedo Billet (*.tuxedobillet.com) moissonnés par l'acteur Apify gorgeous_thistle/ka-tuxedo. Auteur : Simon-Pierre Boucher — contact@spboucher.ai. QUÉBEC SEULEMENT (sagouine = Nouveau-Brunswick, exclu). Découverts via Serper le 2026-08-21 (énumération non exhaustive — ajouter un tenant = un bloc ici, 0 code). Ville vérifiée avant ajout (espacetheatre = Mont-Laurier, PAS Saint-Hyacinthe). Les tenants à ville incertaine (lacaserne, legrandespace, salledesilets, arcf, billetterieacces) sont EXCLUS en attendant vérification.",
3 + "tenants": [
4 + {"domaine": "espacetheatre", "nom": "Espace Théâtre", "ville": "Mont-Laurier"},
5 + {"domaine": "theatreoutremont", "nom": "Théâtre Outremont", "ville": "Montréal"},
6 + {"domaine": "centreculturelbeloeil", "nom": "Centre culturel de Belœil", "ville": "Belœil"},
7 + {"domaine": "villedechandler", "nom": "Ville de Chandler — Sapinart", "ville": "Chandler"},
8 + {"domaine": "lacmegantic", "nom": "Ville de Lac-Mégantic", "ville": "Lac-Mégantic"},
9 + {"domaine": "spectaclesjoliette", "nom": "Spectacles Joliette", "ville": "Joliette"},
10 + {"domaine": "spectaclesnewrichmond", "nom": "Spectacles New Richmond", "ville": "New Richmond"},
11 + {"domaine": "stbruno", "nom": "Ville de Saint-Bruno-de-Montarville", "ville": "Saint-Bruno-de-Montarville"},
12 + {"domaine": "magogculture", "nom": "Magog Culture", "ville": "Magog"},
13 + {"domaine": "frontenac", "nom": "Maison de la culture Frontenac", "ville": "Montréal"},
14 + {"domaine": "auvieuxtreuil", "nom": "Au Vieux Treuil", "ville": "Les Îles-de-la-Madeleine"},
15 + {"domaine": "verdun", "nom": "Quai 5160 — Maison de la culture de Verdun", "ville": "Montréal"},
16 + {"domaine": "theatredelaville", "nom": "Théâtre de la Ville", "ville": "Longueuil"},
17 + {"domaine": "centredesarts", "nom": "Centre des arts Juliette-Lassonde", "ville": "Saint-Hyacinthe"},
18 + {"domaine": "langlicane", "nom": "L'Anglicane", "ville": "Lévis"},
19 + {"domaine": "espacelibre", "nom": "Théâtre Espace Libre", "ville": "Montréal"},
20 + {"domaine": "montrealbaroque", "nom": "Festival Montréal Baroque", "ville": "Montréal"},
21 + {"domaine": "rdppat", "nom": "Maisons de la culture RDP–PAT", "ville": "Montréal"},
22 + {"domaine": "conservatoire-montreal", "nom": "Conservatoire de musique de Montréal", "ville": "Montréal"},
23 + {"domaine": "fta", "nom": "Festival TransAmériques", "ville": "Montréal"},
24 + {"domaine": "fimav", "nom": "Festival international de musique actuelle de Victoriaville", "ville": "Victoriaville"},
25 + {"domaine": "lachine", "nom": "Maison de la culture de Lachine", "ville": "Montréal"},
26 + {"domaine": "st-laurent", "nom": "Arrondissement de Saint-Laurent — culture", "ville": "Montréal"},
27 + {"domaine": "legesu", "nom": "Le Gesù — Centre de créativité", "ville": "Montréal"},
28 + {"domaine": "centrepierrepeladeau", "nom": "Centre Pierre-Péladeau", "ville": "Montréal"},
29 + {"domaine": "latribu-lenational", "nom": "Le National (La Tribu)", "ville": "Montréal"},
30 + {"domaine": "cdn-ndg", "nom": "Maisons de la culture CDN–NDG", "ville": "Montréal"},
31 + {"domaine": "mcgillmusic", "nom": "École de musique Schulich — McGill", "ville": "Montréal"}
32 + ]
33 +}
modified docs/CONFORMITE.md +10 −3
@@ -36,6 +36,13 @@
36 36 | **Place des Arts (`placedesarts`)** | robots.txt : Disallow /cpresources/ /vendor/ /cache/ seulement ; CGU d'achat non applicables à l'index | 2026-08-19 | Courtoisie : « Place des Arts » + lien billetterie officielle | horaire, 1 GET + 25 fiches/sync | **conforme (Phase 3)** |
37 37 | **LHJMQ (`lhjmq`)** | flux JSON public HockeyTech qui motorise le site officiel de la ligue (clé publique de la ligue) | n/a (API) | Courtoisie : « LHJMQ » + lien fiche de match officielle | horaire, 2 GET | **conforme (Phase 3)** — à retirer sur simple demande de la ligue |
38 38 | **Salles JSON-LD (`sallesjsonld`)** | schema.org/Event publié PAR les salles pour l'indexation ; robots.txt vérifié PAR SALLE avant ajout (consigné dans data/salles_jsonld.json) | 2026-08-19 (Grand Théâtre : Allow / ; Trident : Disallow vide ; Capitole/Gestev : /cpresources/ /vendor/) | Courtoisie : nom de la salle + lien fiche | horaire, 3 GET listes + 40 fiches/sync partagées | **conforme (Phase 3)** |
39 +| **Tourisme Montérégie (`monteregie`)** | API REST WordPress publique sans auth ; robots.txt : Disallow /wp-admin/ /wp-includes/ seulement (wp-json non interdit) | 2026-08-21 | Courtoisie : « Tourisme Montérégie » | horaire, ≤ 2 GET API + 15 fiches/sync | **conforme (Phase 4)** |
40 +| **Tourisme Centre-du-Québec (`centreduquebec`)** | robots.txt : User-agent * → `Content-Signal: search=yes, ai-train=no, use=reference` + Allow / (UA produit SortiKa ≠ bots IA listés) | 2026-08-21 | Courtoisie : « Tourisme Centre-du-Québec » — usage index/référence + lien fiche, conforme au Content-Signal | horaire, 1 GET + 10 fiches/sync | **conforme (Phase 4)** |
41 +| **Tourisme Chaudière-Appalaches (`chaudiereappalaches`)** | robots.txt : Disallow /wp/ /r/ *.php$… (les pages publiques /planifier-votre-sejour/ ne sont pas visées) ; JSON-LD Event publié par l'ATR pour l'indexation | 2026-08-21 | Courtoisie : « Tourisme Chaudière-Appalaches » | horaire, ≤ 4 GET listes + 12 fiches/sync | **conforme (Phase 4)** |
42 +| **Tourisme Gaspésie (`gaspesie`)** | WAF (403 sur GET identifié) contourné via **Scrapfly consigné** (asp, country=ca, sans render_js) — contenu public de l'ATR ; robots.txt : Disallow /umb* seulement (contenu public permis, ClaudeBot explicitement Allow) | 2026-08-21 (via Scrapfly) | Courtoisie : « Tourisme Gaspésie » | horaire, ≤ 9 crédits Scrapfly/sync, re-visite 14 j | **conforme (Phase 4)** — région faible, usage Scrapfly budgété comme annoncé Phase 3 |
43 +| **TicketAcces (`ticketacces`)** | robots.txt (tous hôtes) : **Crawl-delay: 120** + Disallow /achat/ et representations/prix.cfm (jamais requêtés) | 2026-08-21 | Courtoisie : nom de l'organisation + lien d'achat officiel | passage complet toutes les 6 h (TTL) : 5 GET www espacés de 120 s + ≤ 25 fiches (≤ 1 par hôte/passage) ; réémission du cache entre passages | **conforme (Phase 4)** — Crawl-delay honoré PAR HÔTE |
44 +| **Réseau Ovation (`ovation`)** | robots.txt m.ovation.ca : User-agent * → **Crawl-delay: 30** + Disallow /account/ /checkout/ /event/tickets (jamais requêtés) | 2026-08-21 | Courtoisie : nom de l'organisation + lien fiche officielle | passage complet 1×/20 h (TTL) : GET espacés de 30,5 s (hôte unique), ≤ 50 fiches/passage ; réémission du cache entre passages | **conforme (Phase 4)** — Crawl-delay honoré |
45 +| **Tuxedo Billet (`tuxedobillet`)** | tenants : robots.txt `Disallow:` (vide = tout permis) ; SPA Angular/Firebase (RTDB verrouillée) → rendu navigateur via acteur Apify maison (proxy résidentiel CA) | 2026-08-21 (espacetheatre) | Courtoisie : nom de la salle + lien d'achat officiel | 1 run d'acteur / 20 h (TTL), 1 page par tenant par run | **conforme (Phase 4)** — rendu JS modéré, 29 tenants QC vérifiés |
39 46
40 47 ## Données transversales
41 48
@@ -51,12 +58,12 @@
51 58 |---|---|---|
52 59 | Tourisme Mauricie | liste rendue JS (1 carte SSR/page), wp-json verrouillé (401) | oui (percer la facette admin-ajax) |
53 60 | Tourisme Laurentides | WAF : 403 sur GET identifié | oui (région déjà bien couverte : 620 fiches — Scrapfly non justifié) |
54 | Tourisme Gaspésie | WAF : 403 sur GET identifié | oui — région faible (60 fiches), candidate Scrapfly consignée ou partenariat |
55 | Tuxedo Billet | site en panne aux sondes (www = « failover site », apex = erreur TLS) | oui |
61 +| Tourisme Gaspésie | ~~WAF : 403 sur GET identifié~~ **RÉACTIVÉE 2026-08-21 (Phase 4)** via Scrapfly consigné — voir Sources actives | n/a |
62 +| Tuxedo Billet | ~~site en panne aux sondes~~ **RÉACTIVÉE 2026-08-21 (Phase 4)** : les tenants vivent — acteur Apify ka-tuxedo, voir Sources actives | n/a |
56 63 | Clubs pros (CF MTL, Alouettes, Rocket…) | déjà couverts par evenko/ticketmaster ; ligues dédiées = surtout des doublons | si besoin de statuts officiels |
57 64 | Palais Montcalm, Le Diamant, Impérial Bell | fiches SANS JSON-LD Event (sondées) | oui, après refonte de leurs sites |
58 65 | MTelus, Corona, Club Soda | salles evenko — déjà couvertes par le connecteur evenko | non (doublons garantis) |
59 | Petit Champlain, Salle Albert-Rousseau | plateforme JS (« pages/collections »), rien de SSR | oui |
66 +| Petit Champlain, Salle Albert-Rousseau | ~~plateforme JS, rien de SSR~~ **COUVERTES 2026-08-21 (Phase 4)** par le connecteur Réseau Ovation (m.ovation.ca, HTML serveur) | n/a |
60 67 | Médias (La Presse, Le Devoir, JdQ/JdM) | éditorial non structuré + droits d'auteur | non |
61 68
62 69 ## Règles d'affichage du crédit (vérifiées Phase 3)
modified docs/connecteurs/INDEX.md +32 −27
@@ -1,8 +1,8 @@
1 1 # Sorti-Ka — Connecteurs de sources d'événements
2 2
3 > Index généré le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` (rejouable : `python3 scripts/gen_connector_docs.py`). Croise `data/sources.json`, l'introspection de `sortika/connectors/*.py` et la base live `data/sortika.db`.
3 +> Index généré le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` (rejouable : `python3 scripts/gen_connector_docs.py`). Croise `data/sources.json`, l'introspection de `sortika/connectors/*.py` et la base live `data/sortika.db`.
4 4
5 **34 sources au registre** — 23 actives/prêtes, 11 abandonnées · **22105 événements en base** (dont 15449 actifs).
5 +**39 sources au registre** — 30 actives/prêtes, 9 abandonnées · **24986 événements en base** (dont 17695 actifs).
6 6
7 7 ⚠️ **Attributions requises** : Ville de Montréal (CC BY 4.0) ; SIT Québec / Ministère du Tourisme et Ville de Laval (CC 4.0 — Données Québec). Détails dans chaque fiche, section « Licence / attribution ».
8 8
@@ -10,29 +10,36 @@
10 10
11 11 | Source | Type | Statut | Actifs | GPS | Prix | Heure | Artistes | Image | Salle | Dernier sync |
12 12 |---|---|---|---|---|---|---|---|---|---|---|
13 | [SIT Québec — Ministère du Tourisme](sitq.md) | donnees-ouvertes | actif | 1013 | 97 % | 0 % | 0 % | 0 % | 0 % | 0 % | 63 % | 73 % | 2026-08-19 02:27 |
14 | [Ville de Montréal — Événements publics](montreal.md) | donnees-ouvertes | actif | 3291 | 99 % | 100 % | 100 % | 18 % | 0 % | 0 % | 100 % | 5 % | 2026-08-19 01:33 |
15 | [Le point de vente — billetterie québécoise](lepointdevente.md) | billetterie | actif | 1786 | 31 % | 92 % | 94 % | 0 % | 0 % | 1 % | 100 % | 99 % | 2026-08-19 01:32 |
16 | [Ville de Laval — Calendrier des activités](laval.md) | donnees-ouvertes | morte-a-la-source | 1 | 0 % | 0 % | 100 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-19 01:31 |
17 | [Ville de Sherbrooke — Calendrier des événements](sherbrooke.md) | municipal | actif | 116 | 0 % | 100 % | 87 % | 92 % | 0 % | 0 % | 99 % | 0 % | 2026-08-19 01:33 |
18 | [Ville de Longueuil — Événements (nouveau portail)](longueuil.md) | municipal | actif | 4 | 75 % | 0 % | 100 % | 50 % | 0 % | 0 % | 100 % | 0 % | 2026-08-19 01:32 |
19 | [Eventbrite — billetterie libre-service (Québec)](eventbrite.md) | billetterie | actif | 430 | 100 % | 87 % | 100 % | 100 % | 0 % | 0 % | 98 % | 100 % | 2026-08-19 01:31 |
20 | [Tourisme Cantons-de-l'Est — calendrier officiel (ATR Estrie)](cantonsdelest.md) | tourisme-regional | actif | 11 | 91 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-19 01:30 |
21 | [evenko — grands concerts et festivals](evenko.md) | promoteur | actif | 421 | 97 % | 0 % | 99 % | 0 % | 0 % | 99 % | 100 % | 100 % | 2026-08-19 01:30 |
22 | [Atuvu.ca — calendrier culturel](atuvu.md) | agregateur | actif | 152 | 70 % | 38 % | 9 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-19 02:33 |
23 | [La Vitrine culturelle](lavitrine.md) | billetterie | actif | 5086 | 69 % | 0 % | 71 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-19 01:31 |
24 | [Ville de Brossard — Calendrier des événements](brossard.md) | municipal | actif | 150 | 94 % | 0 % | 96 % | 0 % | 0 % | 0 % | 100 % | 94 % | 2026-08-19 01:30 |
25 | [Ticketmaster — Discovery API (Québec)](ticketmaster.md) | billetterie | actif | 357 | 100 % | 0 % | 96 % | 0 % | 4 % | 0 % | 91 % | 100 % | 2026-08-19 01:33 |
26 | [Bandsintown — dates de concerts par artiste](bandsintown.md) | agregateur | actif | 452 | 100 % | 1 % | 100 % | 0 % | 0 % | 100 % | 9 % | 100 % | 2026-08-19 01:29 |
27 | [Ville de Laval — calendrier vivant laval.ca](lavalweb.md) | municipal | actif | 470 | 74 % | 0 % | 98 % | 0 % | 0 % | 0 % | 0 % | 100 % | 2026-08-19 02:14 |
28 | [Tourisme Outaouais — calendrier des événements (ATR)](outaouais.md) | tourisme-regional | actif | 27 | 0 % | 0 % | 0 % | 0 % | 0 % | 0 % | 96 % | 0 % | 2026-08-19 02:19 |
29 | [Tourisme Lanaudière — événements (ATR)](lanaudiere.md) | tourisme-regional | actif | 44 | 0 % | 0 % | 0 % | 0 % | 0 % | 0 % | 95 % | 0 % | 2026-08-19 02:19 |
30 | [Sorties Saguenay — portail événementiel de la Ville](saguenaysorties.md) | municipal | actif | 445 | 33 % | 0 % | 100 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-19 02:19 |
31 | [Tourisme Bas-Saint-Laurent — événements (ATR)](bassaintlaurent.md) | tourisme-regional | actif | 20 | 100 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-19 02:10 |
32 | [Québec animée — Ville de Québec](quebecanimee.md) | municipal | actif | 126 | 54 % | 100 % | 0 % | 0 % | 0 % | 0 % | 100 % | 54 % | 2026-08-19 02:10 |
33 | [Place des Arts — calendrier multi-salles](placedesarts.md) | salle-de-spectacle | actif | 350 | 94 % | 72 % | 99 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-19 02:15 |
34 | [LHJMQ — matchs des équipes québécoises](lhjmq.md) | sport | actif | 421 | 99 % | 0 % | 100 % | 0 % | 0 % | 0 % | 0 % | 100 % | 2026-08-19 02:10 |
35 | [Salles de spectacle — moteur générique JSON-LD Event](sallesjsonld.md) | salle-de-spectacle | actif | 276 | 100 % | 97 % | 100 % | 47 % | 1 % | 0 % | 3 % | 100 % | 2026-08-19 02:19 |
13 +| [SIT Québec — Ministère du Tourisme](sitq.md) | donnees-ouvertes | actif | 1020 | 97 % | 0 % | 0 % | 0 % | 0 % | 0 % | 63 % | 74 % | 2026-08-21 20:55 |
14 +| [Ville de Montréal — Événements publics](montreal.md) | donnees-ouvertes | actif | 3567 | 99 % | 100 % | 99 % | 99 % | 0 % | 0 % | 100 % | 4 % | 2026-08-21 20:55 |
15 +| [Le point de vente — billetterie québécoise](lepointdevente.md) | billetterie | actif | 1789 | 30 % | 93 % | 94 % | 0 % | 0 % | 1 % | 100 % | 99 % | 2026-08-21 20:54 |
16 +| [Ville de Laval — Calendrier des activités](laval.md) | donnees-ouvertes | morte-a-la-source | 1 | 0 % | 0 % | 100 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 20:50 |
17 +| [Ville de Sherbrooke — Calendrier des événements](sherbrooke.md) | municipal | actif | 114 | 0 % | 100 % | 86 % | 92 % | 0 % | 0 % | 99 % | 0 % | 2026-08-21 20:55 |
18 +| [Ville de Longueuil — Événements (nouveau portail)](longueuil.md) | municipal | actif | 4 | 75 % | 0 % | 100 % | 50 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 20:54 |
19 +| [Eventbrite — billetterie libre-service (Québec)](eventbrite.md) | billetterie | actif | 438 | 100 % | 89 % | 100 % | 100 % | 0 % | 0 % | 98 % | 100 % | 2026-08-21 20:50 |
20 +| [Tourisme Cantons-de-l'Est — calendrier officiel (ATR Estrie)](cantonsdelest.md) | tourisme-regional | actif | 11 | 91 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 20:49 |
21 +| [evenko — grands concerts et festivals](evenko.md) | promoteur | actif | 417 | 97 % | 0 % | 99 % | 0 % | 1 % | 99 % | 100 % | 100 % | 2026-08-21 20:49 |
22 +| [Atuvu.ca — calendrier culturel](atuvu.md) | agregateur | actif | 224 | 82 % | 25 % | 46 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 20:49 |
23 +| [La Vitrine culturelle](lavitrine.md) | billetterie | actif | 5069 | 69 % | 0 % | 100 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 20:53 |
24 +| [Ville de Brossard — Calendrier des événements](brossard.md) | municipal | actif | 147 | 95 % | 0 % | 96 % | 0 % | 0 % | 0 % | 100 % | 95 % | 2026-08-21 20:49 |
25 +| [Ticketmaster — Discovery API (Québec)](ticketmaster.md) | billetterie | actif | 360 | 100 % | 0 % | 96 % | 0 % | 4 % | 0 % | 90 % | 100 % | 2026-08-21 20:56 |
26 +| [Bandsintown — dates de concerts par artiste](bandsintown.md) | agregateur | actif | 521 | 100 % | 1 % | 100 % | 0 % | 0 % | 100 % | 9 % | 100 % | 2026-08-21 20:49 |
27 +| [Ville de Laval — calendrier vivant laval.ca](lavalweb.md) | municipal | actif | 462 | 75 % | 0 % | 98 % | 0 % | 0 % | 0 % | 0 % | 100 % | 2026-08-21 20:53 |
28 +| [Tourisme Outaouais — calendrier des événements (ATR)](outaouais.md) | tourisme-regional | actif | 24 | 0 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 20:55 |
29 +| [Tourisme Lanaudière — événements (ATR)](lanaudiere.md) | tourisme-regional | actif | 44 | 0 % | 0 % | 0 % | 0 % | 0 % | 0 % | 93 % | 0 % | 2026-08-21 20:50 |
30 +| [Sorties Saguenay — portail événementiel de la Ville](saguenaysorties.md) | municipal | actif | 437 | 80 % | 0 % | 100 % | 0 % | 0 % | 0 % | 100 % | 86 % | 2026-08-21 20:55 |
31 +| [Tourisme Bas-Saint-Laurent — événements (ATR)](bassaintlaurent.md) | tourisme-regional | actif | 20 | 100 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 20:49 |
32 +| [Québec animée — Ville de Québec](quebecanimee.md) | municipal | actif | 121 | 100 % | 100 % | 0 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 20:55 |
33 +| [Place des Arts — calendrier multi-salles](placedesarts.md) | salle-de-spectacle | actif | 347 | 94 % | 73 % | 100 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 20:55 |
34 +| [LHJMQ — matchs des équipes québécoises](lhjmq.md) | sport | actif | 421 | 99 % | 0 % | 100 % | 0 % | 0 % | 0 % | 0 % | 100 % | 2026-08-21 20:54 |
35 +| [Salles de spectacle — moteur générique JSON-LD Event](sallesjsonld.md) | salle-de-spectacle | actif | 278 | 100 % | 97 % | 100 % | 47 % | 1 % | 0 % | 3 % | 100 % | 2026-08-21 20:55 |
36 +| [Tourisme Gaspésie — événements (ATR)](gaspesie.md) | tourisme-regional | actif | 10 | 90 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 21:08 |
37 +| [Tuxedo Billet — billetterie québécoise multi-tenants](tuxedobillet.md) | billetterie | actif | 252 | 90 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 21:44 |
38 +| [Tourisme Montérégie — événements (ATR)](monteregie.md) | tourisme-regional | actif | 28 | 100 % | 0 % | 0 % | 0 % | 0 % | 0 % | 96 % | 0 % | 2026-08-21 21:11 |
39 +| [Tourisme Centre-du-Québec — événements (ATR)](centreduquebec.md) | tourisme-regional | actif | 9 | 0 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 21:09 |
40 +| [Tourisme Chaudière-Appalaches — festivals et événements (ATR)](chaudiereappalaches.md) | tourisme-regional | actif | 28 | 100 % | 0 % | 0 % | 0 % | 0 % | 0 % | 100 % | 0 % | 2026-08-21 21:09 |
41 +| [TicketAcces.net — billetterie régionale québécoise](ticketacces.md) | billetterie | actif | 1320 | 73 % | 6 % | 100 % | 0 % | 0 % | 0 % | 100 % | 100 % | 2026-08-21 21:40 |
42 +| [Réseau Ovation — billetterie québécoise multi-salles](ovation.md) | billetterie | actif | 212 | 100 % | 0 % | 100 % | 0 % | 0 % | 94 % | 100 % | 100 % | 2026-08-21 21:46 |
36 43
37 44 ## Sources abandonnées
38 45
@@ -46,8 +53,6 @@
46 53 | [Ville de Terrebonne — Calendrier](terrebonne.md) | municipal | Cloudflare bloque tout GET non navigateur (403) et aucun jeu « événements » sur Données Québec (… |
47 54 | [Tourisme Mauricie — événements (ATR)](mauricie.md) | tourisme-regional | ÉCARTÉE 2026-08-19 (Phase 3) : liste /evenements rendue JS — 1 seule carte SSR par page (vérifié… |
48 55 | [Tourisme Laurentides — événements (ATR)](laurentides.md) | tourisme-regional | ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Scrapfly possible mais … |
49 | [Tourisme Gaspésie — événements (ATR)](gaspesie.md) | tourisme-regional | ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Région faible (60 fiche… |
50 | [Tuxedo Billet — billetterie Capitale-Nationale](tuxedobillet.md) | billetterie | ÉCARTÉE 2026-08-19 (Phase 3) : site en PANNE aux sondes — www = page « failover site », apex = e… |
51 56 | [Clubs professionnels (CF Montréal, Alouettes, Rocket, Victoire…)](clubs-pros.md) | sport | ÉCARTÉE 2026-08-19 (Phase 3) : les matchs à domicile passent déjà par les billetteries connectée… |
52 57
53 58 ## Régénérer cette documentation
modified docs/connecteurs/atuvu.md +29 −29
@@ -1,6 +1,6 @@
1 1 # Atuvu.ca — calendrier culturel (`atuvu`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `agregateur` · Connecteur : `sortika/connectors/atuvu.py`
6 6
@@ -38,30 +38,30 @@
38 38
39 39 ## Champs récupérés → schéma
40 40
41 Complétude mesurée sur les **152 événements actifs** de la source ; exemples tirés d'un événement réel (`atuvu:belmar-cardinal-en-spectacle-2026-08-19`).
41 +Complétude mesurée sur les **224 événements actifs** de la source ; exemples tirés d'un événement réel (`atuvu:une-femme-denfer-2026-08-21`).
42 42
43 43 | Champ (`events.*`) | Description | Complétude | Exemple réel |
44 44 |---|---|---|---|
45 | `title` — Titre | titre de l'événement | 100 % | Belmar Cardinal |
45 +| `title` — Titre | titre de l'événement | 100 % | Une femme d'enfer |
46 46 | `description` — Description | texte descriptif (source ou fiche détail) | 0 % | ∅ |
47 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://atuvu.ca/billets-spectacle/belmar-cardinal-en-spectacle |
48 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
49 | `venue` — Salle (venue) | nom du lieu | 100 % | Le Rendez-vous du thé |
47 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://atuvu.ca/billets-spectacle/une-femme-denfer |
48 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique", "arts-scene"] |
49 +| `venue` — Salle (venue) | nom du lieu | 100 % | La Comédie de Montréal |
50 50 | `address` — Adresse | adresse civique | 0 % | ∅ |
51 | `city` — Ville | municipalité | 97 % | Montréal |
52 | `region` — Région | région administrative | 94 % | Montréal |
53 | `lat` — GPS (lat/lng) | coordonnées géographiques | 70 % | 45.5602793, -73.6576904 |
54 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
55 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
56 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 9 % | 18:00 |
51 +| `city` — Ville | municipalité | 98 % | Montréal |
52 +| `region` — Région | région administrative | 96 % | Montréal |
53 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 82 % | 45.518607, -73.558419 |
54 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-21 |
55 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-21 |
56 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 46 % | 20:00 |
57 57 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
58 58 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
59 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 28 % | ∅ |
60 | `price_min` — Prix min | tarif minimal réel ($) | 38 % | 0.0 |
61 | `price_label` — Libellé prix | étiquette tarifaire affichable | 28 % | ∅ |
59 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 19 % | 0 |
60 +| `price_min` — Prix min | tarif minimal réel ($) | 25 % | 35.0 |
61 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 19 % | 35.00 $ (prix régulier atuvu.ca) |
62 62 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
63 63 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
64 | `image` — Image | visuel (og:image ou média source) | 100 % | https://atuvu.ca/uploads/images/2024/07/belmar-cardinal-en-spectacle.jpg |
64 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://atuvu.ca/uploads/images/2026/06/femme-denfer.jpg |
65 65 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
66 66
67 67 ## Fréquence & budgets
@@ -76,19 +76,19 @@ Complétude mesurée sur les **152 événements actifs** de la source ; exemples
76 76
77 77 ## Volumétrie & complétude live
78 78
79 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
80
81 - **Événements en base** : 162 (dont **152 actifs**)
82 - **GPS** : 106/152 actifs (70 %)
83 - **Prix** : 57/152 actifs (38 %)
84 - **Heure (start_time)** : 14/152 actifs (9 %)
85 - **Heure de fin** : 0/152 actifs (0 %)
86 - **Statut (annulé/reporté/complet)** : 0/152 actifs (0 %)
87 - **Artistes** : 0/152 actifs (0 %)
88 - **Image** : 152/152 actifs (100 %)
89 - **Salle (venue)** : 152/152 actifs (100 %)
90 - **Dernier sync** : 2026-08-19 02:33 — fetched 170, +0 ajoutés, ~2 mis à jour, -0 retirés
91 - **Syncs enregistrés** (sync_log) : 39
79 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
80 +
81 +- **Événements en base** : 249 (dont **224 actifs**)
82 +- **GPS** : 183/224 actifs (82 %)
83 +- **Prix** : 55/224 actifs (25 %)
84 +- **Heure (start_time)** : 103/224 actifs (46 %)
85 +- **Heure de fin** : 0/224 actifs (0 %)
86 +- **Statut (annulé/reporté/complet)** : 0/224 actifs (0 %)
87 +- **Artistes** : 0/224 actifs (0 %)
88 +- **Image** : 224/224 actifs (100 %)
89 +- **Salle (venue)** : 224/224 actifs (100 %)
90 +- **Dernier sync** : 2026-08-21 20:49 — fetched 253, +0 ajoutés, ~2 mis à jour, -0 retirés
91 +- **Syncs enregistrés** (sync_log) : 98
92 92
93 93 ## Erreurs & dépannage
94 94
modified docs/connecteurs/bandsintown.md +28 −28
@@ -1,6 +1,6 @@
1 1 # Bandsintown — dates de concerts par artiste (`bandsintown`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `agregateur` · Connecteur : `sortika/connectors/bandsintown.py`
6 6
@@ -43,30 +43,30 @@
43 43
44 44 ## Champs récupérés → schéma
45 45
46 Complétude mesurée sur les **452 événements actifs** de la source ; exemples tirés d'un événement réel (`bandsintown:108747079`).
46 +Complétude mesurée sur les **521 événements actifs** de la source ; exemples tirés d'un événement réel (`bandsintown:108801594`).
47 47
48 48 | Champ (`events.*`) | Description | Complétude | Exemple réel |
49 49 |---|---|---|---|
50 | `title` — Titre | titre de l'événement | 100 % | Doug St-Louis & Dj Chuck / Ptit Gros Bison à Grenville en Fête |
51 | `description` — Description | texte descriptif (source ou fiche détail) | 19 % | Au Parc Normand-Woodbury à Grenville. Spectacle Gratuit Ils seront en duo pour vous faire danser… |
52 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.bandsintown.com/e/108747079?app_id=js_www.sorti-ka.com&came_from=267&utm_medium=api&… |
50 +| `title` — Titre | titre de l'événement | 100 % | St-Roch XP 2026 |
51 +| `description` — Description | texte descriptif (source ou fiche détail) | 19 % | Saturday August 29, 20262:00 PM La Place Boutique gourmande 699, rue St-Joseph Est Free Saturday… |
52 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.bandsintown.com/e/108801594?app_id=js_www.sorti-ka.com&came_from=267&utm_medium=api&… |
53 53 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
54 | `venue` — Salle (venue) | nom du lieu | 100 % | Doug St-Louis & Dj Chuck / Ptit Gros Bison à Grenville en Fête |
55 | `address` — Adresse | adresse civique | 98 % | 21 Rue Tri Jean |
56 | `city` — Ville | municipalité | 100 % | Grenville |
57 | `region` — Région | région administrative | 99 % | Laurentides |
58 | `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.630063, -74.600568 |
59 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
60 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
61 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 20:30 |
54 +| `venue` — Salle (venue) | nom du lieu | 100 % | St-Roch XP 2026 |
55 +| `address` — Adresse | adresse civique | 99 % | 699 Rue Saint-Joseph E |
56 +| `city` — Ville | municipalité | 100 % | Québec |
57 +| `region` — Région | région administrative | 99 % | Capitale-Nationale |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 46.815349, -71.220885 |
59 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-28 |
60 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-28 |
61 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 19:00 |
62 62 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
63 63 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
64 64 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 1 % | 1 |
65 65 | `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
66 66 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
67 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 100 % | ["Doug St-Louis", "P'tit Gros Bison"] |
67 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 100 % | ["Fine Food Market"] |
68 68 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
69 | `image` — Image | visuel (og:image ou média source) | 9 % | https://photos.bandsintown.com/large/25853181.jpeg |
69 +| `image` — Image | visuel (og:image ou média source) | 9 % | https://photos.bandsintown.com/large/25970249.jpeg |
70 70 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
71 71
72 72 ## Fréquence & budgets
@@ -82,19 +82,19 @@ Complétude mesurée sur les **452 événements actifs** de la source ; exemples
82 82
83 83 ## Volumétrie & complétude live
84 84
85 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
86
87 - **Événements en base** : 454 (dont **452 actifs**)
88 - **GPS** : 452/452 actifs (100 %)
89 - **Prix** : 4/452 actifs (1 %)
90 - **Heure (start_time)** : 451/452 actifs (100 %)
91 - **Heure de fin** : 0/452 actifs (0 %)
92 - **Statut (annulé/reporté/complet)** : 0/452 actifs (0 %)
93 - **Artistes** : 452/452 actifs (100 %)
94 - **Image** : 42/452 actifs (9 %)
95 - **Salle (venue)** : 452/452 actifs (100 %)
96 - **Dernier sync** : 2026-08-19 01:29 — fetched 453, +82 ajoutés, ~0 mis à jour, -0 retirés
97 - **Syncs enregistrés** (sync_log) : 24
85 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
86 +
87 +- **Événements en base** : 529 (dont **521 actifs**)
88 +- **GPS** : 521/521 actifs (100 %)
89 +- **Prix** : 5/521 actifs (1 %)
90 +- **Heure (start_time)** : 520/521 actifs (100 %)
91 +- **Heure de fin** : 0/521 actifs (0 %)
92 +- **Statut (annulé/reporté/complet)** : 0/521 actifs (0 %)
93 +- **Artistes** : 521/521 actifs (100 %)
94 +- **Image** : 48/521 actifs (9 %)
95 +- **Salle (venue)** : 521/521 actifs (100 %)
96 +- **Dernier sync** : 2026-08-21 20:49 — fetched 521, +0 ajoutés, ~0 mis à jour, -0 retirés
97 +- **Syncs enregistrés** (sync_log) : 83
98 98
99 99 ## Erreurs & dépannage
100 100
modified docs/connecteurs/bassaintlaurent.md +8 −4
@@ -1,6 +1,6 @@
1 1 # Tourisme Bas-Saint-Laurent — événements (ATR) (`bassaintlaurent`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/bassaintlaurent.py`
6 6
@@ -78,7 +78,7 @@ Complétude mesurée sur les **20 événements actifs** de la source ; exemples
78 78
79 79 ## Volumétrie & complétude live
80 80
81 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
81 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
82 82
83 83 - **Événements en base** : 27 (dont **20 actifs**)
84 84 - **GPS** : 20/20 actifs (100 %)
@@ -89,8 +89,8 @@ Complétude mesurée sur les **20 événements actifs** de la source ; exemples
89 89 - **Artistes** : 0/20 actifs (0 %)
90 90 - **Image** : 20/20 actifs (100 %)
91 91 - **Salle (venue)** : 0/20 actifs (0 %)
92 - **Dernier sync** : 2026-08-19 02:10 — fetched 27, +27 ajoutés, ~0 mis à jour, -0 retirés
93 - **Syncs enregistrés** (sync_log) : 1
92 +- **Dernier sync** : 2026-08-21 20:49 — fetched 27, +0 ajoutés, ~0 mis à jour, -0 retirés
93 +- **Syncs enregistrés** (sync_log) : 60
94 94
95 95 ## Erreurs & dépannage
96 96
@@ -107,3 +107,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
107 107 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
108 108 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
109 109 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
110 +
111 +Commits touchant `sortika/connectors/bassaintlaurent.py` :
112 +
113 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/brossard.md +18 −18
@@ -1,6 +1,6 @@
1 1 # Ville de Brossard — Calendrier des événements (`brossard`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/brossard.py`
6 6
@@ -43,7 +43,7 @@
43 43
44 44 ## Champs récupérés → schéma
45 45
46 Complétude mesurée sur les **150 événements actifs** de la source ; exemples tirés d'un événement réel (`brossard:37838`).
46 +Complétude mesurée sur les **147 événements actifs** de la source ; exemples tirés d'un événement réel (`brossard:37838`).
47 47
48 48 | Champ (`events.*`) | Description | Complétude | Exemple réel |
49 49 |---|---|---|---|
@@ -51,11 +51,11 @@ Complétude mesurée sur les **150 événements actifs** de la source ; exemples
51 51 | `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Entrée libre. Les enfants âgés de moins de 8 ans doivent être accompagnés d’un adulte. Les enfan… |
52 52 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://brossard.ca/calendrier-evenements/atelier-jeux-de-societe-16-septembre-2026/ |
53 53 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["famille"] |
54 | `venue` — Salle (venue) | nom du lieu | 94 % | Salle d'animation |
55 | `address` — Adresse | adresse civique | 94 % | 7855, av San Francisco |
54 +| `venue` — Salle (venue) | nom du lieu | 95 % | Salle d'animation |
55 +| `address` — Adresse | adresse civique | 95 % | 7855, av San Francisco |
56 56 | `city` — Ville | municipalité | 100 % | Brossard |
57 57 | `region` — Région | région administrative | 100 % | Montérégie |
58 | `lat` — GPS (lat/lng) | coordonnées géographiques | 94 % | 45.4551469, -73.4682277 |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 95 % | 45.4551469, -73.4682277 |
59 59 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-16 |
60 60 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-16 |
61 61 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 96 % | 10:00 |
@@ -81,19 +81,19 @@ Complétude mesurée sur les **150 événements actifs** de la source ; exemples
81 81
82 82 ## Volumétrie & complétude live
83 83
84 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
85
86 - **Événements en base** : 264 (dont **150 actifs**)
87 - **GPS** : 141/150 actifs (94 %)
88 - **Prix** : 0/150 actifs (0 %)
89 - **Heure (start_time)** : 144/150 actifs (96 %)
90 - **Heure de fin** : 0/150 actifs (0 %)
91 - **Statut (annulé/reporté/complet)** : 0/150 actifs (0 %)
92 - **Artistes** : 0/150 actifs (0 %)
93 - **Image** : 150/150 actifs (100 %)
94 - **Salle (venue)** : 141/150 actifs (94 %)
95 - **Dernier sync** : 2026-08-19 01:30 — fetched 264, +0 ajoutés, ~0 mis à jour, -0 retirés
96 - **Syncs enregistrés** (sync_log) : 24
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 +
86 +- **Événements en base** : 264 (dont **147 actifs**)
87 +- **GPS** : 139/147 actifs (95 %)
88 +- **Prix** : 0/147 actifs (0 %)
89 +- **Heure (start_time)** : 141/147 actifs (96 %)
90 +- **Heure de fin** : 0/147 actifs (0 %)
91 +- **Statut (annulé/reporté/complet)** : 0/147 actifs (0 %)
92 +- **Artistes** : 0/147 actifs (0 %)
93 +- **Image** : 147/147 actifs (100 %)
94 +- **Salle (venue)** : 139/147 actifs (95 %)
95 +- **Dernier sync** : 2026-08-21 20:49 — fetched 264, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 83
97 97
98 98 ## Erreurs & dépannage
99 99
modified docs/connecteurs/cantonsdelest.md +15 −15
@@ -1,6 +1,6 @@
1 1 # Tourisme Cantons-de-l'Est — calendrier officiel (ATR Estrie) (`cantonsdelest`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/cantonsdelest.py`
6 6
@@ -41,21 +41,21 @@
41 41
42 42 ## Champs récupérés → schéma
43 43
44 Complétude mesurée sur les **11 événements actifs** de la source ; exemples tirés d'un événement réel (`cantonsdelest:10200-4714`).
44 +Complétude mesurée sur les **11 événements actifs** de la source ; exemples tirés d'un événement réel (`cantonsdelest:7646-2584`).
45 45
46 46 | Champ (`events.*`) | Description | Complétude | Exemple réel |
47 47 |---|---|---|---|
48 | `title` — Titre | titre de l'événement | 100 % | Vieillir en santé |
49 | `description` — Description | texte descriptif (source ou fiche détail) | 100 % | La troisième journée portera sur les différentes façons de maintenir une bonne santé lorsqu’on p… |
50 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.cantonsdelest.com/evenements/10200/vieillir-en-sante/4714 |
48 +| `title` — Titre | titre de l'événement | 100 % | NanoFest |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Le NanoFest présente de la musique, de la poésie, du conte, de la danse, du cirque et de l’art v… |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.cantonsdelest.com/evenements/7646/nanofest/2584 |
51 51 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
52 52 | `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
53 | `address` — Adresse | adresse civique | 91 % | 225 Rue Frontenac |
54 | `city` — Ville | municipalité | 100 % | Sherbrooke |
53 +| `address` — Adresse | adresse civique | 82 % | ∅ |
54 +| `city` — Ville | municipalité | 100 % | Bury |
55 55 | `region` — Région | région administrative | 100 % | Estrie |
56 | `lat` — GPS (lat/lng) | coordonnées géographiques | 91 % | 45.403367, -71.895639 |
57 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
58 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 91 % | 45.510797, -71.442105 |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-21 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-23 |
59 59 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
60 60 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
@@ -64,7 +64,7 @@ Complétude mesurée sur les **11 événements actifs** de la source ; exemples
64 64 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
65 65 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
66 66 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
67 | `image` — Image | visuel (og:image ou média source) | 100 % | https://lvc-medias.lavitrine.com/552bc497-70fc-5c58-840d-b83a1235a126.png |
67 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://mto.media.tourinsoft.eu/upload/IMG-0065.png?width=1191&height=670 |
68 68 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
69 69
70 70 ## Fréquence & budgets
@@ -79,9 +79,9 @@ Complétude mesurée sur les **11 événements actifs** de la source ; exemples
79 79
80 80 ## Volumétrie & complétude live
81 81
82 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
82 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
83 83
84 - **Événements en base** : 23 (dont **11 actifs**)
84 +- **Événements en base** : 47 (dont **11 actifs**)
85 85 - **GPS** : 10/11 actifs (91 %)
86 86 - **Prix** : 0/11 actifs (0 %)
87 87 - **Heure (start_time)** : 0/11 actifs (0 %)
@@ -90,8 +90,8 @@ Complétude mesurée sur les **11 événements actifs** de la source ; exemples
90 90 - **Artistes** : 0/11 actifs (0 %)
91 91 - **Image** : 11/11 actifs (100 %)
92 92 - **Salle (venue)** : 0/11 actifs (0 %)
93 - **Dernier sync** : 2026-08-19 01:30 — fetched 13, +0 ajoutés, ~0 mis à jour, -0 retirés
94 - **Syncs enregistrés** (sync_log) : 21
93 +- **Dernier sync** : 2026-08-21 20:49 — fetched 13, +0 ajoutés, ~0 mis à jour, -0 retirés
94 +- **Syncs enregistrés** (sync_log) : 80
95 95
96 96 ## Erreurs & dépannage
97 97
added docs/connecteurs/centreduquebec.md +110 −0
@@ -0,0 +1,110 @@
1 +# Tourisme Centre-du-Québec — événements (ATR) (`centreduquebec`)
2 +
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 +
5 +**Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/centreduquebec.py`
6 +
7 +## Description
8 +
9 +- **Source** : [Tourisme Centre-du-Québec — événements (ATR)](https://tourismecentreduquebec.com/evenements/)
10 +- **Couverture** : Centre-du-Québec (première source de la région — 0 fiche avant connexion ; ~10 fiches)
11 +- **Extraction** : Liste rendue serveur (1 GET) : blocs events_listing (plage de dates FR + titre + lien) ; fiches incrémentales (CDQ_FICHE_MAX=10/sync, re-visite 7 j) : bloc « Adresse », og:description, og:image. Ni ville structurée, ni heure, ni tarif → jamais inventés (région dérivée de l'ATR).
12 +
13 +## Accès
14 +
15 +- **Type** : tourisme-regional
16 +- **Endpoint** (`BASE`) : `https://tourismecentreduquebec.com`
17 +- **Auth** : Aucune (accès public, identification honnête par User-Agent)
18 +- **Pagination** : aucune (flux unique)
19 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
20 +- **Throttle** : 0.8 s entre requêtes
21 +- **Accès légal** : Site public de l'ATR, robots.txt sans interdiction — GET throttlés 0,8 s, UA honnête
22 +
23 +<details><summary>En-tête documentaire du connecteur</summary>
24 +
25 +> connectors/centreduquebec.py : Tourisme Centre-du-Québec — événements
26 +> (ATR, Phase 4). Première source de la région Centre-du-Québec (0 fiche
27 +> avant connexion).
28 +> Source : https://tourismecentreduquebec.com/evenements/ — WordPress,
29 +> liste rendue serveur (~10 fiches à venir, vérifié 2026-08-21) :
30 +> chaque bloc events_listing_results_event = plage de dates FR
31 +> (« 1 septembre 2026 au 6 septembre 2026 ») + titre + lien.
32 +> Extraction: GET liste (1 requête) → fiches détail incrémentales (cache
33 +> data/centreduquebec_cache.json, CDQ_FICHE_MAX/sync, re-visite
34 +> 7 j) : bloc « Adresse » (adresse civique), og:image,
35 +> og:description. La fiche ne publie ni ville structurée, ni
36 +> heure, ni tarif → jamais inventés (région dérivée de l'ATR).
37 +> Accès : site public, robots.txt sans interdiction sur /evenements/ —
38 +> GET throttlés, UA honnête.
39 +
40 +</details>
41 +
42 +## Champs récupérés → schéma
43 +
44 +Complétude mesurée sur les **9 événements actifs** de la source ; exemples tirés d'un événement réel (`centreduquebec:festival-du-boeuf-dinverness`).
45 +
46 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
47 +|---|---|---|---|
48 +| `title` — Titre | titre de l'événement | 100 % | Festival du Boeuf d'Inverness |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 0 % | ∅ |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://tourismecentreduquebec.com/evenements/festival-du-boeuf-dinverness/ |
51 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
52 +| `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
53 +| `address` — Adresse | adresse civique | 100 % | 1910, chemin Dublin |
54 +| `city` — Ville | municipalité | 0 % | ∅ |
55 +| `region` — Région | région administrative | 100 % | Centre-du-Québec |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 0 % | ∅ |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-01 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-06 |
59 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
60 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
62 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
63 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
64 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
65 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
66 +| `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
67 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://tourismecentreduquebec.com/wp-content/uploads/2026/05/placeholder_video2.jpg |
68 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
69 +
70 +## Fréquence & budgets
71 +
72 +- **Cadence** : horaire (watcher PM2)
73 +- **Budgets / plafonds (variables d'environnement)** :
74 +
75 +| Variable | Défaut | Rôle |
76 +|---|---|---|
77 +| `CDQ_FICHE_MAX` | `10` | — |
78 +- **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
79 +
80 +## Volumétrie & complétude live
81 +
82 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
83 +
84 +- **Événements en base** : 9 (dont **9 actifs**)
85 +- **GPS** : 0/9 actifs (0 %)
86 +- **Prix** : 0/9 actifs (0 %)
87 +- **Heure (start_time)** : 0/9 actifs (0 %)
88 +- **Heure de fin** : 0/9 actifs (0 %)
89 +- **Statut (annulé/reporté/complet)** : 0/9 actifs (0 %)
90 +- **Artistes** : 0/9 actifs (0 %)
91 +- **Image** : 9/9 actifs (100 %)
92 +- **Salle (venue)** : 0/9 actifs (0 %)
93 +- **Dernier sync** : 2026-08-21 21:09 — fetched 9, +0 ajoutés, ~0 mis à jour, -0 retirés
94 +- **Syncs enregistrés** (sync_log) : 3
95 +
96 +## Erreurs & dépannage
97 +
98 +Aucune erreur dans `sync_log` pour cette source. ✅
99 +
100 +- Rejouer un sync isolé : `python3 run.py sync --source centreduquebec` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='centreduquebec' ORDER BY ts DESC LIMIT 5;"`
101 +
102 +## Licence / attribution
103 +
104 +- Site public de l'ATR, robots.txt sans interdiction — GET throttlés 0,8 s, UA honnête
105 +
106 +## Historique
107 +
108 +- **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
109 +- **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
110 +- **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
added docs/connecteurs/chaudiereappalaches.md +112 −0
@@ -0,0 +1,112 @@
1 +# Tourisme Chaudière-Appalaches — festivals et événements (ATR) (`chaudiereappalaches`)
2 +
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 +
5 +**Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/chaudiereappalaches.py`
6 +
7 +## Description
8 +
9 +- **Source** : [Tourisme Chaudière-Appalaches — festivals et événements (ATR)](https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/)
10 +- **Couverture** : Chaudière-Appalaches (~36 fiches à venir, vérifié 2026-08-21)
11 +- **Extraction** : Liste rendue serveur paginée (?listpage=1..3, ~12 fiches/page) → fiches incrémentales (CHA_FICHE_MAX=12/sync, re-visite 7 j) : JSON-LD Event COMPLET (dates ISO, description, adresse, ville, CP, GPS, images) via le parseur générique ld_events (sallesjsonld).
12 +
13 +## Accès
14 +
15 +- **Type** : tourisme-regional
16 +- **Endpoint** (`BASE`) : `https://chaudiereappalaches.com`
17 +- **Auth** : Aucune (accès public, identification honnête par User-Agent)
18 +- **Pagination** : par numéro de page
19 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
20 +- **Throttle** : 0.8 s entre requêtes
21 +- **Accès légal** : Site public de l'ATR, robots.txt sans interdiction sur /planifier-votre-sejour/ — GET throttlés 0,8 s, UA honnête
22 +
23 +<details><summary>En-tête documentaire du connecteur</summary>
24 +
25 +> connectors/chaudiereappalaches.py : Tourisme Chaudière-Appalaches —
26 +> festivals et événements (ATR, Phase 4). Région faiblement couverte
27 +> avant connexion (essentiellement SITQ).
28 +> Source : https://chaudiereappalaches.com/planifier-votre-sejour/
29 +> ete-automne/festivals-et-evenements/ — CMS Woody, liste rendue
30 +> serveur paginée (?listpage=1..3, ~12 fiches/page, vérifié
31 +> 2026-08-21). Chaque fiche embarque un bloc JSON-LD Event
32 +> COMPLET : dates ISO, description, adresse civique, ville,
33 +> code postal, GPS, images.
34 +> Extraction: pages liste (1 GET/page) → fiches détail incrémentales (cache
35 +> data/chaudiereappalaches_cache.json, CHA_FICHE_MAX/sync,
36 +> re-visite 7 j) → JSON-LD Event (parseur générique ld_events
37 +> de sallesjsonld). Pas d'heure ni de tarif structurés fiables
38 +> → jamais inventés.
39 +> Accès : site public de l'ATR, robots.txt sans interdiction sur
40 +> /planifier-votre-sejour/ — GET throttlés, UA honnête.
41 +
42 +</details>
43 +
44 +## Champs récupérés → schéma
45 +
46 +Complétude mesurée sur les **28 événements actifs** de la source ; exemples tirés d'un événement réel (`chaudiereappalaches:5779956`).
47 +
48 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
49 +|---|---|---|---|
50 +| `title` — Titre | titre de l'événement | 100 % | Festival Relief |
51 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Amateurs de vélo de montagne et de plein air, rendez-vous du 6 au 8 août prochain pour festoyer!… |
52 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festi… |
53 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["festival"] |
54 +| `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
55 +| `address` — Adresse | adresse civique | 100 % | 120, route du Mont Adstock |
56 +| `city` — Ville | municipalité | 100 % | Adstock |
57 +| `region` — Région | région administrative | 100 % | Chaudière-Appalaches |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 46.029143, -71.187773 |
59 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2027-08-06 |
60 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2027-08-08 |
61 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
62 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
63 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
64 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
65 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
66 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
67 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
68 +| `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
69 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://hubo-cmaj-prod.s3.eu-west-1.amazonaws.com/medias/6722011b1ce7a9089bd3.jpg |
70 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
71 +
72 +## Fréquence & budgets
73 +
74 +- **Cadence** : horaire (watcher PM2)
75 +- **Budgets / plafonds (variables d'environnement)** :
76 +
77 +| Variable | Défaut | Rôle |
78 +|---|---|---|
79 +| `CHA_FICHE_MAX` | `12` | — |
80 +- **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
81 +
82 +## Volumétrie & complétude live
83 +
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 +
86 +- **Événements en base** : 28 (dont **28 actifs**)
87 +- **GPS** : 28/28 actifs (100 %)
88 +- **Prix** : 0/28 actifs (0 %)
89 +- **Heure (start_time)** : 0/28 actifs (0 %)
90 +- **Heure de fin** : 0/28 actifs (0 %)
91 +- **Statut (annulé/reporté/complet)** : 0/28 actifs (0 %)
92 +- **Artistes** : 0/28 actifs (0 %)
93 +- **Image** : 28/28 actifs (100 %)
94 +- **Salle (venue)** : 0/28 actifs (0 %)
95 +- **Dernier sync** : 2026-08-21 21:09 — fetched 28, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 3
97 +
98 +## Erreurs & dépannage
99 +
100 +Aucune erreur dans `sync_log` pour cette source. ✅
101 +
102 +- Rejouer un sync isolé : `python3 run.py sync --source chaudiereappalaches` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='chaudiereappalaches' ORDER BY ts DESC LIMIT 5;"`
103 +
104 +## Licence / attribution
105 +
106 +- Site public de l'ATR, robots.txt sans interdiction sur /planifier-votre-sejour/ — GET throttlés 0,8 s, UA honnête
107 +
108 +## Historique
109 +
110 +- **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
111 +- **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
112 +- **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
modified docs/connecteurs/clubs-pros.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Clubs professionnels (CF Montréal, Alouettes, Rocket, Victoire…) (`clubs-pros`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `sport` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/evenko.md +24 −24
@@ -1,6 +1,6 @@
1 1 # evenko — grands concerts et festivals (`evenko`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `promoteur` · Connecteur : `sortika/connectors/evenko.py`
6 6
@@ -40,30 +40,30 @@
40 40
41 41 ## Champs récupérés → schéma
42 42
43 Complétude mesurée sur les **421 événements actifs** de la source ; exemples tirés d'un événement réel (`evenko:e008238`).
43 +Complétude mesurée sur les **417 événements actifs** de la source ; exemples tirés d'un événement réel (`evenko:e008639`).
44 44
45 45 | Champ (`events.*`) | Description | Complétude | Exemple réel |
46 46 |---|---|---|---|
47 | `title` — Titre | titre de l'événement | 100 % | Bunt. |
47 +| `title` — Titre | titre de l'événement | 100 % | Hazlett |
48 48 | `description` — Description | texte descriptif (source ou fiche détail) | 99 % | Portes : 19 h 00 |
49 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.ticketmaster.ca/event/310064A1D614A5D4?lang=fr-ca&brand=mtelus |
49 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.ticketmaster.ca/event/310064CBAD997B95?lang=fr-ca&brand=evenko |
50 50 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
51 | `venue` — Salle (venue) | nom du lieu | 100 % | MTELUS |
51 +| `venue` — Salle (venue) | nom du lieu | 100 % | Théâtre Beanfield |
52 52 | `address` — Adresse | adresse civique | 0 % | ∅ |
53 53 | `city` — Ville | municipalité | 100 % | Montréal |
54 54 | `region` — Région | région administrative | 100 % | Montréal |
55 | `lat` — GPS (lat/lng) | coordonnées géographiques | 97 % | 45.5106101, -73.5633963 |
56 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-17 |
57 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-17 |
55 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 97 % | 45.4828925, -73.5750666 |
56 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-09 |
57 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-09 |
58 58 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 99 % | 20:00 |
59 59 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
60 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | soldout |
60 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 1 % | postponed |
61 61 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
62 62 | `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
63 63 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
64 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 99 % | ["BUNT."] |
64 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 99 % | ["Hazlett"] |
65 65 | `organizer` — Organisateur | promoteur / organisme | 100 % | evenko |
66 | `image` — Image | visuel (og:image ou média source) | 100 % | https://images.ctfassets.net/3yxl57nu0yl4/5vCTRo8NB8mgBnGkalFYWH/6b93506e56fb2e9f538b3d810817215… |
66 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://images.ctfassets.net/3yxl57nu0yl4/mN9LeqmCUHLpse7u5H6qZ/886d0a285be58605b448a73f9080464f… |
67 67 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
68 68
69 69 ## Fréquence & budgets
@@ -73,19 +73,19 @@ Complétude mesurée sur les **421 événements actifs** de la source ; exemples
73 73
74 74 ## Volumétrie & complétude live
75 75
76 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
77
78 - **Événements en base** : 424 (dont **421 actifs**)
79 - **GPS** : 408/421 actifs (97 %)
80 - **Prix** : 0/421 actifs (0 %)
81 - **Heure (start_time)** : 418/421 actifs (99 %)
82 - **Heure de fin** : 0/421 actifs (0 %)
83 - **Statut (annulé/reporté/complet)** : 2/421 actifs (0 %)
84 - **Artistes** : 418/421 actifs (99 %)
85 - **Image** : 421/421 actifs (100 %)
86 - **Salle (venue)** : 421/421 actifs (100 %)
87 - **Dernier sync** : 2026-08-19 01:30 — fetched 422, +0 ajoutés, ~0 mis à jour, -0 retirés
88 - **Syncs enregistrés** (sync_log) : 40
76 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
77 +
78 +- **Événements en base** : 426 (dont **417 actifs**)
79 +- **GPS** : 404/417 actifs (97 %)
80 +- **Prix** : 0/417 actifs (0 %)
81 +- **Heure (start_time)** : 414/417 actifs (99 %)
82 +- **Heure de fin** : 0/417 actifs (0 %)
83 +- **Statut (annulé/reporté/complet)** : 4/417 actifs (1 %)
84 +- **Artistes** : 414/417 actifs (99 %)
85 +- **Image** : 417/417 actifs (100 %)
86 +- **Salle (venue)** : 417/417 actifs (100 %)
87 +- **Dernier sync** : 2026-08-21 20:49 — fetched 417, +0 ajoutés, ~0 mis à jour, -0 retirés
88 +- **Syncs enregistrés** (sync_log) : 99
89 89
90 90 ## Erreurs & dépannage
91 91
modified docs/connecteurs/eventbrite.md +33 −33
@@ -1,6 +1,6 @@
1 1 # Eventbrite — billetterie libre-service (Québec) (`eventbrite`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/eventbrite.py`
6 6
@@ -46,30 +46,30 @@
46 46
47 47 ## Champs récupérés → schéma
48 48
49 Complétude mesurée sur les **430 événements actifs** de la source ; exemples tirés d'un événement réel (`eventbrite:1985751030788`).
49 +Complétude mesurée sur les **438 événements actifs** de la source ; exemples tirés d'un événement réel (`eventbrite:1997881258626`).
50 50
51 51 | Champ (`events.*`) | Description | Complétude | Exemple réel |
52 52 |---|---|---|---|
53 | `title` — Titre | titre de l'événement | 100 % | Châteauguay : Samedi 07 novembre 2026 - Spectacle hommage aux grandes voix |
54 | `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Châteauguay : Samedi 07 novembre 2026 à 19h30 - Spectacle hommage aux grandes voix avec René Laj… |
55 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.eventbrite.ca/e/billets-chateauguay-samedi-07-novembre-2026-spectacle-hommage-aux-gr… |
56 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["conference"] |
57 | `venue` — Salle (venue) | nom du lieu | 100 % | Hôtel Manoir D'Youville |
58 | `address` — Adresse | adresse civique | 100 % | 498 Boulevard D'Youville |
59 | `city` — Ville | municipalité | 100 % | Châteauguay |
60 | `region` — Région | région administrative | 99 % | Montérégie |
61 | `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.384507, -73.770495 |
62 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-11-07 |
63 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-11-07 |
64 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 19:30 |
65 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 100 % | 21:00 |
53 +| `title` — Titre | titre de l'événement | 100 % | SANTA RM - CELEBRA 20 AÑOS / MONTREAL, CANADA |
54 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | SANTA RM CELEBRA POR PRIMERA VEZ 20 AÑOS DE TRAYECTORIA EN MONTREAL DISFRUTA EN VIVO TODOS SUS G… |
55 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.eventbrite.ca/e/santa-rm-celebra-20-anos-montreal-canada-tickets-1997881258626 |
56 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
57 +| `venue` — Salle (venue) | nom du lieu | 100 % | 60 Rue Saint-Jacques |
58 +| `address` — Adresse | adresse civique | 100 % | 60 Rue Saint-Jacques |
59 +| `city` — Ville | municipalité | 100 % | Montréal |
60 +| `region` — Région | région administrative | 99 % | Montréal |
61 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.505461, -73.556948 |
62 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-10-03 |
63 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-10-03 |
64 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 19:00 |
65 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 100 % | 22:30 |
66 66 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
67 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 87 % | 0 |
68 | `price_min` — Prix min | tarif minimal réel ($) | 86 % | 60.79 |
69 | `price_label` — Libellé prix | étiquette tarifaire affichable | 87 % | 60.79 $ (Eventbrite) |
67 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 89 % | 0 |
68 +| `price_min` — Prix min | tarif minimal réel ($) | 87 % | 60.03 |
69 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 89 % | 60.03 $ à 106.88 $ (Eventbrite) |
70 70 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
71 | `organizer` — Organisateur | promoteur / organisme | 96 % | Productions Marc Gervais Conférence / Spectacle |
72 | `image` — Image | visuel (og:image ou média source) | 98 % | https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F1180396379%2F242151267944%2F1%2Fori… |
71 +| `organizer` — Organisateur | promoteur / organisme | 96 % | DIEGO MX |
72 +| `image` — Image | visuel (og:image ou média source) | 98 % | https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F1191383521%2F2637625526451%2F1%2For… |
73 73 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
74 74
75 75 ## Fréquence & budgets
@@ -84,19 +84,19 @@ Complétude mesurée sur les **430 événements actifs** de la source ; exemples
84 84
85 85 ## Volumétrie & complétude live
86 86
87 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
88
89 - **Événements en base** : 478 (dont **430 actifs**)
90 - **GPS** : 430/430 actifs (100 %)
91 - **Prix** : 376/430 actifs (87 %)
92 - **Heure (start_time)** : 430/430 actifs (100 %)
93 - **Heure de fin** : 430/430 actifs (100 %)
94 - **Statut (annulé/reporté/complet)** : 0/430 actifs (0 %)
95 - **Artistes** : 0/430 actifs (0 %)
96 - **Image** : 422/430 actifs (98 %)
97 - **Salle (venue)** : 430/430 actifs (100 %)
98 - **Dernier sync** : 2026-08-19 01:31 — fetched 426, +0 ajoutés, ~124 mis à jour, -2 retirés
99 - **Syncs enregistrés** (sync_log) : 22
87 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
88 +
89 +- **Événements en base** : 527 (dont **438 actifs**)
90 +- **GPS** : 438/438 actifs (100 %)
91 +- **Prix** : 388/438 actifs (89 %)
92 +- **Heure (start_time)** : 438/438 actifs (100 %)
93 +- **Heure de fin** : 438/438 actifs (100 %)
94 +- **Statut (annulé/reporté/complet)** : 0/438 actifs (0 %)
95 +- **Artistes** : 0/438 actifs (0 %)
96 +- **Image** : 428/438 actifs (98 %)
97 +- **Salle (venue)** : 438/438 actifs (100 %)
98 +- **Dernier sync** : 2026-08-21 20:50 — fetched 427, +5 ajoutés, ~1 mis à jour, -2 retirés
99 +- **Syncs enregistrés** (sync_log) : 81
100 100
101 101 ## Erreurs & dépannage
102 102
modified docs/connecteurs/gaspesie.md +81 −13
@@ -1,46 +1,114 @@
1 1 # Tourisme Gaspésie — événements (ATR) (`gaspesie`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 **Statut : abandonnee** · Type : `tourisme-regional` · Connecteur : *aucun (source sans connecteur runtime)*
5 +**Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/gaspesie.py`
6 6
7 7 ## Description
8 8
9 - **Source** : [Tourisme Gaspésie — événements (ATR)](https://www.tourisme-gaspesie.com/evenements-gaspesie.html)
9 +- **Source** : [Tourisme Gaspésie — événements (ATR)](https://www.tourisme-gaspesie.com/fr/festival-et-evenements/)
10 10 - **Couverture** : Gaspésie–Îles-de-la-Madeleine
11 - **Extraction** : n/d
12 - **Note** : ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Région faible (60 fiches au baseline) : candidate prioritaire pour une vague Scrapfly consignée (crédits) ou un partenariat. À re-sonder.
11 +- **Extraction** : Scrapfly (asp, country=ca, sans render_js — HTML rendu serveur) : liste (1 crédit) + fiches incrémentales (cache, GASP_FICHE_MAX=8/sync, re-visite 14 j) — titre h1, plage de dates FR, adresse/ville/CP (bloc coordonnées), og:description/og:image. Fiches sans date écartées (festivals annuels non datés).
12 +- **Note** : RÉACTIVÉE 2026-08-21 (Phase 4) : la vague Scrapfly consignée annoncée en Phase 3. Ancienne URL /evenements-gaspesie.html morte (404) → /fr/festival-et-evenements/ (28 fiches, ~6 datées à l'amorce).
13 13
14 14 ## Accès
15 15
16 16 - **Type** : tourisme-regional
17 - **Endpoint** : voir URL source (https://www.tourisme-gaspesie.com/evenements-gaspesie.html)
18 - **Auth** : Aucune (accès public, identification honnête par User-Agent)
19 - **Accès légal** : n/d
17 +- **Endpoint** (`BASE`) : `https://www.tourisme-gaspesie.com`
18 +- **Auth** : Aucune côté source ; `SCRAPFLY_API_KEY` requis pour le backend Scrapfly
19 +- **Pagination** : aucune (flux unique)
20 +- **Backend** : Scrapfly (anti-bot / rendu JS) via `base.get_scrapfly()`
21 +- **Throttle** : 1.5 s entre requêtes
22 +- **Accès légal** : Contenu public de l'ATR ; WAF contourné avec modération (budget ≤ 9 crédits Scrapfly/sync, consigné Phase 4), attribution + lien vers la fiche officielle
23 +
24 +<details><summary>En-tête documentaire du connecteur</summary>
25 +
26 +> connectors/gaspesie.py : Tourisme Gaspésie — festivals et événements (ATR,
27 +> Phase 4). Région parmi les plus faibles au baseline (Phase 3 : candidate
28 +> prioritaire consignée pour une vague Scrapfly) — la voici.
29 +> Source : https://www.tourisme-gaspesie.com/fr/festival-et-evenements/
30 +> (CMS Umbraco). Le site est derrière un WAF : 403 sur GET
31 +> identifié (revérifié 2026-08-21) → extraction via SCRAPFLY
32 +> (asp, country=ca, sans render_js — le HTML est rendu serveur),
33 +> usage consigné et budgeté (GASP_FICHE_MAX/sync).
34 +> Extraction: liste (1 crédit) → fiches détail incrémentales (cache
35 +> data/gaspesie_cache.json, GASP_FICHE_MAX/sync, re-visite 14 j
36 +> — festivals annuels stables) : titre <h1>, plage de dates FR
37 +> (« 18 au 21 juin 2026 »), ville (« Carleton-sur-Mer, Québec »),
38 +> og:description, og:image. Pas d'heure ni de tarif structurés
39 +> → jamais inventés.
40 +> Accès : contenu public de l'ATR ; anti-bot contourné avec modération
41 +> (≤ 1 + GASP_FICHE_MAX crédits Scrapfly/sync, re-visite 14 j),
42 +> attribution et lien vers la fiche officielle conservés.
43 +
44 +</details>
20 45
21 46 ## Champs récupérés → schéma
22 47
23 *Aucun événement actif en base pour cette source — pas de schéma live à montrer.*
48 +Complétude mesurée sur les **10 événements actifs** de la source ; exemples tirés d'un événement réel (`gaspesie:les-perceides-festival-international-de-cinema-et-dart-de-perce`).
49 +
50 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
51 +|---|---|---|---|
52 +| `title` — Titre | titre de l'événement | 100 % | Les Percéides - Festival international de cinéma et d'art de Percé |
53 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Les Percéides en tant qu’unique festival de cinéma d’auteur en Gaspésie se distingue par sa libe… |
54 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.tourisme-gaspesie.com/fr/festival-et-evenements/les-perceides-festival-international… |
55 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["festival"] |
56 +| `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
57 +| `address` — Adresse | adresse civique | 100 % | 162, route 132 Ouest |
58 +| `city` — Ville | municipalité | 100 % | Percé |
59 +| `region` — Région | région administrative | 100 % | Gaspésie–Îles-de-la-Madeleine |
60 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 90 % | 48.5217344, -64.2143739 |
61 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
62 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-23 |
63 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
64 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
65 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
66 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
67 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
68 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
69 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
70 +| `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
71 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://www.tourisme-gaspesie.com/media/joydhvzd/les-perceides-festival-international-de-cinema-… |
72 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
24 73
25 74 ## Fréquence & budgets
26 75
27 - **Cadence** : n/d
76 +- **Cadence** : horaire (watcher PM2) — cache 14 j, ~2 fiches re-visitées/j
77 +- **Budgets / plafonds (variables d'environnement)** :
78 +
79 +| Variable | Défaut | Rôle |
80 +|---|---|---|
81 +| `GASP_FICHE_MAX` | `8` | — |
28 82 - **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
29 83
30 84 ## Volumétrie & complétude live
31 85
32 *Source sans connecteur runtime : aucune donnée en base.*
86 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
87 +
88 +- **Événements en base** : 26 (dont **10 actifs**)
89 +- **GPS** : 9/10 actifs (90 %)
90 +- **Prix** : 0/10 actifs (0 %)
91 +- **Heure (start_time)** : 0/10 actifs (0 %)
92 +- **Heure de fin** : 0/10 actifs (0 %)
93 +- **Statut (annulé/reporté/complet)** : 0/10 actifs (0 %)
94 +- **Artistes** : 0/10 actifs (0 %)
95 +- **Image** : 10/10 actifs (100 %)
96 +- **Salle (venue)** : 0/10 actifs (0 %)
97 +- **Dernier sync** : 2026-08-21 21:08 — fetched 26, +0 ajoutés, ~0 mis à jour, -0 retirés
98 +- **Syncs enregistrés** (sync_log) : 4
33 99
34 100 ## Erreurs & dépannage
35 101
102 +Aucune erreur dans `sync_log` pour cette source. ✅
103 +
104 +- Rejouer un sync isolé : `python3 run.py sync --source gaspesie` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='gaspesie' ORDER BY ts DESC LIMIT 5;"`
36 105
37 106 ## Licence / attribution
38 107
39 - n/d
108 +- Contenu public de l'ATR ; WAF contourné avec modération (budget ≤ 9 crédits Scrapfly/sync, consigné Phase 4), attribution + lien vers la fiche officielle
40 109
41 110 ## Historique
42 111
43 - **Source abandonnée** — ÉCARTÉE 2026-08-19 (Phase 3) : WAF — 403 sur GET identifié (UA honnête). Région faible (60 fiches au baseline) : candidate prioritaire pour une vague Scrapfly consignée (crédits) ou un partenariat. À re-sonder.
44 112 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
45 113 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
46 114 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
modified docs/connecteurs/gatineau.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Gatineau — Événements (`gatineau`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `municipal` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/lanaudiere.md +11 −7
@@ -1,6 +1,6 @@
1 1 # Tourisme Lanaudière — événements (ATR) (`lanaudiere`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/lanaudiere.py`
6 6
@@ -65,7 +65,7 @@ Complétude mesurée sur les **44 événements actifs** de la source ; exemples
65 65 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
66 66 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
67 67 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
68 | `image` — Image | visuel (og:image ou média source) | 95 % | https://mto.media.tourinsoft.eu/upload/Corpo---SODECT-1804-2019-171.JPG |
68 +| `image` — Image | visuel (og:image ou média source) | 93 % | https://mto.media.tourinsoft.eu/upload/Corpo---SODECT-1804-2019-171.JPG |
69 69 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
70 70
71 71 ## Fréquence & budgets
@@ -80,19 +80,19 @@ Complétude mesurée sur les **44 événements actifs** de la source ; exemples
80 80
81 81 ## Volumétrie & complétude live
82 82
83 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
83 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
84 84
85 - **Événements en base** : 80 (dont **44 actifs**)
85 +- **Événements en base** : 81 (dont **44 actifs**)
86 86 - **GPS** : 0/44 actifs (0 %)
87 87 - **Prix** : 0/44 actifs (0 %)
88 88 - **Heure (start_time)** : 0/44 actifs (0 %)
89 89 - **Heure de fin** : 0/44 actifs (0 %)
90 90 - **Statut (annulé/reporté/complet)** : 0/44 actifs (0 %)
91 91 - **Artistes** : 0/44 actifs (0 %)
92 - **Image** : 42/44 actifs (95 %)
92 +- **Image** : 41/44 actifs (93 %)
93 93 - **Salle (venue)** : 0/44 actifs (0 %)
94 - **Dernier sync** : 2026-08-19 02:19 — fetched 80, +80 ajoutés, ~0 mis à jour, -0 retirés
95 - **Syncs enregistrés** (sync_log) : 1
94 +- **Dernier sync** : 2026-08-21 20:50 — fetched 81, +0 ajoutés, ~0 mis à jour, -0 retirés
95 +- **Syncs enregistrés** (sync_log) : 60
96 96
97 97 ## Erreurs & dépannage
98 98
@@ -109,3 +109,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
109 109 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
110 110 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
111 111 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
112 +
113 +Commits touchant `sortika/connectors/lanaudiere.py` :
114 +
115 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/laurentides.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Tourisme Laurentides — événements (ATR) (`laurentides`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `tourisme-regional` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/laval.md +4 −4
@@ -1,6 +1,6 @@
1 1 # Ville de Laval — Calendrier des activités (`laval`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : morte-a-la-source** · Type : `donnees-ouvertes` · Connecteur : `sortika/connectors/laval.py`
6 6
@@ -67,7 +67,7 @@ Complétude mesurée sur les **1 événements actifs** de la source ; exemples t
67 67
68 68 ## Volumétrie & complétude live
69 69
70 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
70 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
71 71
72 72 - **Événements en base** : 277 (dont **1 actifs**)
73 73 - **GPS** : 0/1 actifs (0 %)
@@ -78,8 +78,8 @@ Complétude mesurée sur les **1 événements actifs** de la source ; exemples t
78 78 - **Artistes** : 0/1 actifs (0 %)
79 79 - **Image** : 1/1 actifs (100 %)
80 80 - **Salle (venue)** : 0/1 actifs (0 %)
81 - **Dernier sync** : 2026-08-19 01:31 — fetched 293, +0 ajoutés, ~32 mis à jour, -0 retirés
82 - **Syncs enregistrés** (sync_log) : 43
81 +- **Dernier sync** : 2026-08-21 20:50 — fetched 293, +0 ajoutés, ~32 mis à jour, -0 retirés
82 +- **Syncs enregistrés** (sync_log) : 102
83 83
84 84 ## Erreurs & dépannage
85 85
modified docs/connecteurs/lavalweb.md +32 −24
@@ -1,6 +1,6 @@
1 1 # Ville de Laval — calendrier vivant laval.ca (`lavalweb`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/lavalweb.py`
6 6
@@ -41,22 +41,22 @@
41 41
42 42 ## Champs récupérés → schéma
43 43
44 Complétude mesurée sur les **470 événements actifs** de la source ; exemples tirés d'un événement réel (`lavalweb:cardio-fit-dehors-isabelle-2026-08-19`).
44 +Complétude mesurée sur les **462 événements actifs** de la source ; exemples tirés d'un événement réel (`lavalweb:halloween-exposition-de-plantes-carnivores-et-dautres-etrangetes`).
45 45
46 46 | Champ (`events.*`) | Description | Complétude | Exemple réel |
47 47 |---|---|---|---|
48 | `title` — Titre | titre de l'événement | 100 % | Cardio-fit |
49 | `description` — Description | texte descriptif (source ou fiche détail) | 5 % | ∅ |
50 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.laval.ca/calendrier-activites/cardio-fit-dehors-isabelle-2026-08-19/ |
51 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
52 | `venue` — Salle (venue) | nom du lieu | 100 % | Parc Isabelle |
48 +| `title` — Titre | titre de l'événement | 100 % | Halloween : exposition de plantes carnivores et d’autres étrangetés |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 48 % | ∅ |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.laval.ca/calendrier-activites/halloween-exposition-de-plantes-carnivores-et-dautres-… |
51 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["exposition-musee"] |
52 +| `venue` — Salle (venue) | nom du lieu | 100 % | Centre de la Nature |
53 53 | `address` — Adresse | adresse civique | 0 % | ∅ |
54 54 | `city` — Ville | municipalité | 100 % | Laval |
55 55 | `region` — Région | région administrative | 100 % | Laval |
56 | `lat` — GPS (lat/lng) | coordonnées géographiques | 74 % | 45.5676014, -73.8151736 |
57 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
58 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
59 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 98 % | 09:00 |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 75 % | 45.6059801, -73.6603569 |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-10-10 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-10-10 |
59 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 98 % | 09:30 |
60 60 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
62 62 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
@@ -80,23 +80,27 @@ Complétude mesurée sur les **470 événements actifs** de la source ; exemples
80 80
81 81 ## Volumétrie & complétude live
82 82
83 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
83 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
84 84
85 - **Événements en base** : 470 (dont **470 actifs**)
86 - **GPS** : 349/470 actifs (74 %)
87 - **Prix** : 0/470 actifs (0 %)
88 - **Heure (start_time)** : 460/470 actifs (98 %)
89 - **Heure de fin** : 0/470 actifs (0 %)
90 - **Statut (annulé/reporté/complet)** : 0/470 actifs (0 %)
91 - **Artistes** : 0/470 actifs (0 %)
92 - **Image** : 0/470 actifs (0 %)
93 - **Salle (venue)** : 470/470 actifs (100 %)
94 - **Dernier sync** : 2026-08-19 02:14 — fetched 470, +470 ajoutés, ~0 mis à jour, -0 retirés
95 - **Syncs enregistrés** (sync_log) : 1
85 +- **Événements en base** : 555 (dont **462 actifs**)
86 +- **GPS** : 348/462 actifs (75 %)
87 +- **Prix** : 0/462 actifs (0 %)
88 +- **Heure (start_time)** : 453/462 actifs (98 %)
89 +- **Heure de fin** : 0/462 actifs (0 %)
90 +- **Statut (annulé/reporté/complet)** : 0/462 actifs (0 %)
91 +- **Artistes** : 0/462 actifs (0 %)
92 +- **Image** : 0/462 actifs (0 %)
93 +- **Salle (venue)** : 462/462 actifs (100 %)
94 +- **Dernier sync** : 2026-08-21 20:53 — fetched 461, +0 ajoutés, ~0 mis à jour, -3 retirés
95 +- **Syncs enregistrés** (sync_log) : 60
96 96
97 97 ## Erreurs & dépannage
98 98
99 Aucune erreur dans `sync_log` pour cette source. ✅
99 +Erreurs relevées dans `sync_log` :
100 +
101 +| Erreur | Occurrences | Dernière fois |
102 +|---|---|---|
103 +| HTTPSConnectionPool(host='www.laval.ca', port=443): Read timed out. (read timeout=60) | 1 | 2026-08-19 14:16 |
100 104
101 105 - Rejouer un sync isolé : `python3 run.py sync --source lavalweb` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='lavalweb' ORDER BY ts DESC LIMIT 5;"`
102 106
@@ -109,3 +113,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
109 113 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
110 114 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
111 115 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
116 +
117 +Commits touchant `sortika/connectors/lavalweb.py` :
118 +
119 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/lavitrine.md +26 −26
@@ -1,6 +1,6 @@
1 1 # La Vitrine culturelle (`lavitrine`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/lavitrine.py`
6 6
@@ -43,22 +43,22 @@
43 43
44 44 ## Champs récupérés → schéma
45 45
46 Complétude mesurée sur les **5086 événements actifs** de la source ; exemples tirés d'un événement réel (`lavitrine:7561-2026-08-20`).
46 +Complétude mesurée sur les **5069 événements actifs** de la source ; exemples tirés d'un événement réel (`lavitrine:12490-2027-03-06`).
47 47
48 48 | Champ (`events.*`) | Description | Complétude | Exemple réel |
49 49 |---|---|---|---|
50 | `title` — Titre | titre de l'événement | 100 % | Pink Floyd - The Dark Side of the Moon |
51 | `description` — Description | texte descriptif (source ou fiche détail) | 71 % | Après avoir conquis des millions de spectateurs à travers le monde, Hubblo et le Théâtre Manuvie… |
52 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.lavitrine.com/fr/evenement/pink-floyd-dark-side-of-the-moon/7561 |
50 +| `title` — Titre | titre de l'événement | 100 % | Les Tireux d'Roches |
51 +| `description` — Description | texte descriptif (source ou fiche détail) | 99 % | Depuis 1998, Les Tireux d’Roches se sont affirmés comme des conteurs d’histoires et des artisans… |
52 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.lavitrine.com/fr/evenement/les-tireux-droches/12490 |
53 53 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
54 | `venue` — Salle (venue) | nom du lieu | 100 % | Le Dome |
54 +| `venue` — Salle (venue) | nom du lieu | 100 % | Espace Mandeville |
55 55 | `address` — Adresse | adresse civique | 0 % | ∅ |
56 | `city` — Ville | municipalité | 100 % | Brossard |
57 | `region` — Région | région administrative | 100 % | Montérégie |
58 | `lat` — GPS (lat/lng) | coordonnées géographiques | 69 % | 45.441097, -73.440315 |
59 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-20 |
60 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-20 |
61 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 71 % | 17:30 |
56 +| `city` — Ville | municipalité | 100 % | Drummondville |
57 +| `region` — Région | région administrative | 100 % | Centre-du-Québec |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 69 % | 45.883252, -72.489597 |
59 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2027-03-06 |
60 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2027-03-06 |
61 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 20:00 |
62 62 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
63 63 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
64 64 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
@@ -66,7 +66,7 @@ Complétude mesurée sur les **5086 événements actifs** de la source ; exemple
66 66 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
67 67 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
68 68 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
69 | `image` — Image | visuel (og:image ou média source) | 100 % | https://lvc-medias.lavitrine.com/3c48bccd-aa63-5048-9a3d-48e07d4b48e6.jpg |
69 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://lvc-medias.lavitrine.com/83ac1924-a8fe-5cb3-9778-a11e5cc3757c.jpg |
70 70 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
71 71
72 72 ## Fréquence & budgets
@@ -81,19 +81,19 @@ Complétude mesurée sur les **5086 événements actifs** de la source ; exemple
81 81
82 82 ## Volumétrie & complétude live
83 83
84 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
85
86 - **Événements en base** : 5145 (dont **5086 actifs**)
87 - **GPS** : 3509/5086 actifs (69 %)
88 - **Prix** : 0/5086 actifs (0 %)
89 - **Heure (start_time)** : 3627/5086 actifs (71 %)
90 - **Heure de fin** : 0/5086 actifs (0 %)
91 - **Statut (annulé/reporté/complet)** : 0/5086 actifs (0 %)
92 - **Artistes** : 0/5086 actifs (0 %)
93 - **Image** : 5086/5086 actifs (100 %)
94 - **Salle (venue)** : 5084/5086 actifs (100 %)
95 - **Dernier sync** : 2026-08-19 01:31 — fetched 5123, +0 ajoutés, ~1019 mis à jour, -1 retirés
96 - **Syncs enregistrés** (sync_log) : 34
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 +
86 +- **Événements en base** : 5172 (dont **5069 actifs**)
87 +- **GPS** : 3496/5069 actifs (69 %)
88 +- **Prix** : 0/5069 actifs (0 %)
89 +- **Heure (start_time)** : 5069/5069 actifs (100 %)
90 +- **Heure de fin** : 0/5069 actifs (0 %)
91 +- **Statut (annulé/reporté/complet)** : 0/5069 actifs (0 %)
92 +- **Artistes** : 0/5069 actifs (0 %)
93 +- **Image** : 5069/5069 actifs (100 %)
94 +- **Salle (venue)** : 5067/5069 actifs (100 %)
95 +- **Dernier sync** : 2026-08-21 20:53 — fetched 5120, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 93
97 97
98 98 ## Erreurs & dépannage
99 99
modified docs/connecteurs/lepointdevente.md +19 −19
@@ -1,6 +1,6 @@
1 1 # Le point de vente — billetterie québécoise (`lepointdevente`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/lepointdevente.py`
6 6
@@ -44,7 +44,7 @@
44 44
45 45 ## Champs récupérés → schéma
46 46
47 Complétude mesurée sur les **1786 événements actifs** de la source ; exemples tirés d'un événement réel (`lepointdevente:7gw260926001`).
47 +Complétude mesurée sur les **1789 événements actifs** de la source ; exemples tirés d'un événement réel (`lepointdevente:7gw260926001`).
48 48
49 49 | Champ (`events.*`) | Description | Complétude | Exemple réel |
50 50 |---|---|---|---|
@@ -56,15 +56,15 @@ Complétude mesurée sur les **1786 événements actifs** de la source ; exemple
56 56 | `address` — Adresse | adresse civique | 0 % | ∅ |
57 57 | `city` — Ville | municipalité | 100 % | Fossambault-sur-le-Lac |
58 58 | `region` — Région | région administrative | 98 % | Capitale-Nationale |
59 | `lat` — GPS (lat/lng) | coordonnées géographiques | 31 % | 46.8903229, -71.6121222 |
59 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 30 % | 46.8903229, -71.6121222 |
60 60 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-26 |
61 61 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-26 |
62 62 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 94 % | 20:00 |
63 63 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
64 64 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
65 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 92 % | 0 |
66 | `price_min` — Prix min | tarif minimal réel ($) | 89 % | 30.0 |
67 | `price_label` — Libellé prix | étiquette tarifaire affichable | 92 % | Table basse et chaise 30 $ · Table-bar et tabouret avec dossier 30 $ · Table-bistro et tabouret … |
65 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 93 % | 0 |
66 +| `price_min` — Prix min | tarif minimal réel ($) | 91 % | 30.0 |
67 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 93 % | Table basse et chaise 30 $ · Table-bar et tabouret avec dossier 30 $ · Table-bistro et tabouret … |
68 68 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 1 % | ["Sam Vigneault"] |
69 69 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
70 70 | `image` — Image | visuel (og:image ou média source) | 100 % | https://tpos.s3.amazonaws.com/events/7GW/26/09/26/001/7gw260926001-1152x648-fr-6984c862.png |
@@ -82,19 +82,19 @@ Complétude mesurée sur les **1786 événements actifs** de la source ; exemple
82 82
83 83 ## Volumétrie & complétude live
84 84
85 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
86
87 - **Événements en base** : 4237 (dont **1786 actifs**)
88 - **GPS** : 547/1786 actifs (31 %)
89 - **Prix** : 1647/1786 actifs (92 %)
90 - **Heure (start_time)** : 1673/1786 actifs (94 %)
91 - **Heure de fin** : 0/1786 actifs (0 %)
92 - **Statut (annulé/reporté/complet)** : 0/1786 actifs (0 %)
93 - **Artistes** : 16/1786 actifs (1 %)
94 - **Image** : 1786/1786 actifs (100 %)
95 - **Salle (venue)** : 1776/1786 actifs (99 %)
96 - **Dernier sync** : 2026-08-19 01:32 — fetched 1787, +0 ajoutés, ~4 mis à jour, -0 retirés
97 - **Syncs enregistrés** (sync_log) : 44
85 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
86 +
87 +- **Événements en base** : 4396 (dont **1789 actifs**)
88 +- **GPS** : 539/1789 actifs (30 %)
89 +- **Prix** : 1660/1789 actifs (93 %)
90 +- **Heure (start_time)** : 1675/1789 actifs (94 %)
91 +- **Heure de fin** : 0/1789 actifs (0 %)
92 +- **Statut (annulé/reporté/complet)** : 0/1789 actifs (0 %)
93 +- **Artistes** : 15/1789 actifs (1 %)
94 +- **Image** : 1789/1789 actifs (100 %)
95 +- **Salle (venue)** : 1780/1789 actifs (99 %)
96 +- **Dernier sync** : 2026-08-21 20:54 — fetched 1786, +1 ajoutés, ~4 mis à jour, -0 retirés
97 +- **Syncs enregistrés** (sync_log) : 103
98 98
99 99 ## Erreurs & dépannage
100 100
modified docs/connecteurs/levis.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Lévis — Calendrier (`levis`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `municipal` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/lhjmq.md +8 −4
@@ -1,6 +1,6 @@
1 1 # LHJMQ — matchs des équipes québécoises (`lhjmq`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `sport` · Connecteur : `sortika/connectors/lhjmq.py`
6 6
@@ -77,7 +77,7 @@ Complétude mesurée sur les **421 événements actifs** de la source ; exemples
77 77
78 78 ## Volumétrie & complétude live
79 79
80 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
80 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
81 81
82 82 - **Événements en base** : 423 (dont **421 actifs**)
83 83 - **GPS** : 415/421 actifs (99 %)
@@ -88,8 +88,8 @@ Complétude mesurée sur les **421 événements actifs** de la source ; exemples
88 88 - **Artistes** : 0/421 actifs (0 %)
89 89 - **Image** : 0/421 actifs (0 %)
90 90 - **Salle (venue)** : 421/421 actifs (100 %)
91 - **Dernier sync** : 2026-08-19 02:10 — fetched 423, +423 ajoutés, ~0 mis à jour, -0 retirés
92 - **Syncs enregistrés** (sync_log) : 1
91 +- **Dernier sync** : 2026-08-21 20:54 — fetched 423, +0 ajoutés, ~0 mis à jour, -0 retirés
92 +- **Syncs enregistrés** (sync_log) : 60
93 93
94 94 ## Erreurs & dépannage
95 95
@@ -106,3 +106,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
106 106 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
107 107 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
108 108 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
109 +
110 +Commits touchant `sortika/connectors/lhjmq.py` :
111 +
112 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/longueuil.md +4 −4
@@ -1,6 +1,6 @@
1 1 # Ville de Longueuil — Événements (nouveau portail) (`longueuil`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/longueuil.py`
6 6
@@ -81,7 +81,7 @@ Complétude mesurée sur les **4 événements actifs** de la source ; exemples t
81 81
82 82 ## Volumétrie & complétude live
83 83
84 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 85
86 86 - **Événements en base** : 9 (dont **4 actifs**)
87 87 - **GPS** : 3/4 actifs (75 %)
@@ -92,8 +92,8 @@ Complétude mesurée sur les **4 événements actifs** de la source ; exemples t
92 92 - **Artistes** : 0/4 actifs (0 %)
93 93 - **Image** : 4/4 actifs (100 %)
94 94 - **Salle (venue)** : 0/4 actifs (0 %)
95 - **Dernier sync** : 2026-08-19 01:32 — fetched 9, +0 ajoutés, ~0 mis à jour, -0 retirés
96 - **Syncs enregistrés** (sync_log) : 21
95 +- **Dernier sync** : 2026-08-21 20:54 — fetched 9, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 80
97 97
98 98 ## Erreurs & dépannage
99 99
modified docs/connecteurs/mauricie.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Tourisme Mauricie — événements (ATR) (`mauricie`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `tourisme-regional` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
added docs/connecteurs/monteregie.md +112 −0
@@ -0,0 +1,112 @@
1 +# Tourisme Montérégie — événements (ATR) (`monteregie`)
2 +
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 +
5 +**Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/monteregie.py`
6 +
7 +## Description
8 +
9 +- **Source** : [Tourisme Montérégie — événements (ATR)](https://www.tourisme-monteregie.qc.ca/membre/categorie/quoi-faire/festivals-et-evenements/)
10 +- **Couverture** : Montérégie (~78 fiches à venir, vérifié 2026-08-21)
11 +- **Extraction** : API REST WordPress publique /wp-json/wp/v2/cpt_event (paginée per_page=100) — titre, description, plage de dates FR, lat/long, site web, image (gisement Tourinsoft, acf.syndic_object_id EVENMTOV… ; recoupement SITQ géré par dedup_key). Fiches détail incrémentales (MTG_FICHE_MAX=15/sync, re-visite 7 j) : JSON-LD Event → adresse civique, ville, code postal.
12 +
13 +## Accès
14 +
15 +- **Type** : tourisme-regional
16 +- **Endpoint** (`BASE`) : `https://www.tourisme-monteregie.qc.ca`
17 +- **Auth** : Aucune (accès public, identification honnête par User-Agent)
18 +- **Pagination** : par numéro de page, per_page
19 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
20 +- **Throttle** : 0.8 s entre requêtes
21 +- **Accès légal** : API REST WordPress publique (aucune auth), robots.txt sans interdiction sur /wp-json/ — GET throttlés 0,8 s, UA honnête
22 +
23 +<details><summary>En-tête documentaire du connecteur</summary>
24 +
25 +> connectors/monteregie.py : Tourisme Montérégie — événements (ATR, Phase 4)
26 +> Source : https://www.tourisme-monteregie.qc.ca — WordPress dont l'API
27 +> REST publique expose le CPT `cpt_event` (~78 fiches à venir,
28 +> vérifié 2026-08-21). Gisement Tourinsoft (acf.syndic_object_id
29 +> EVENMTOV… — même famille que le SIT Québec ; recoupement géré
30 +> par dedup_key, mais l'ATR ajoute GPS, image, site web et
31 +> description que le flux SITQ ne publie pas).
32 +> Extraction: API JSON /wp-json/wp/v2/cpt_event (paginée per_page=100) →
33 +> titre, description, plage de dates FR (« Du 18 septembre au
34 +> 20 septembre 2026 »), lat/long, site web, image. Fiches détail
35 +> incrémentales (cache data/monteregie_cache.json,
36 +> MTG_FICHE_MAX/sync, re-visite 7 j) : bloc JSON-LD Event →
37 +> adresse civique, ville, code postal. Pas d'heure ni de tarif
38 +> structurés → jamais inventés.
39 +> Accès : API REST WordPress publique (aucune auth), robots.txt sans
40 +> interdiction sur /wp-json/ — GET throttlés, UA honnête.
41 +
42 +</details>
43 +
44 +## Champs récupérés → schéma
45 +
46 +Complétude mesurée sur les **28 événements actifs** de la source ; exemples tirés d'un événement réel (`monteregie:89015`).
47 +
48 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
49 +|---|---|---|---|
50 +| `title` — Titre | titre de l'événement | 100 % | MARCHÉ DE NOËL DE MARIEVILLE |
51 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Marché de Noël réunissant des producteurs et artisans de la région. En plus de découvrir de nouv… |
52 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.tourisme-monteregie.qc.ca/evenement/marche-de-noel-de-marieville/ |
53 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
54 +| `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
55 +| `address` — Adresse | adresse civique | 93 % | rue du Pont |
56 +| `city` — Ville | municipalité | 100 % | Marieville |
57 +| `region` — Région | région administrative | 100 % | Montérégie |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.432743, -73.159756 |
59 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-12-04 |
60 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-12-06 |
61 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
62 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
63 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
64 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
65 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
66 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
67 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
68 +| `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
69 +| `image` — Image | visuel (og:image ou média source) | 96 % | https://www.tourisme-monteregie.qc.ca/wp-content/uploads/2025/03/marche-de-noel-de-marieville-tr… |
70 +| `website` — Site web | site officiel de l'événement | 75 % | ∅ |
71 +
72 +## Fréquence & budgets
73 +
74 +- **Cadence** : horaire (watcher PM2)
75 +- **Budgets / plafonds (variables d'environnement)** :
76 +
77 +| Variable | Défaut | Rôle |
78 +|---|---|---|
79 +| `MTG_FICHE_MAX` | `15` | — |
80 +- **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
81 +
82 +## Volumétrie & complétude live
83 +
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 +
86 +- **Événements en base** : 78 (dont **28 actifs**)
87 +- **GPS** : 28/28 actifs (100 %)
88 +- **Prix** : 0/28 actifs (0 %)
89 +- **Heure (start_time)** : 0/28 actifs (0 %)
90 +- **Heure de fin** : 0/28 actifs (0 %)
91 +- **Statut (annulé/reporté/complet)** : 0/28 actifs (0 %)
92 +- **Artistes** : 0/28 actifs (0 %)
93 +- **Image** : 27/28 actifs (96 %)
94 +- **Salle (venue)** : 0/28 actifs (0 %)
95 +- **Dernier sync** : 2026-08-21 21:11 — fetched 78, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 7
97 +
98 +## Erreurs & dépannage
99 +
100 +Aucune erreur dans `sync_log` pour cette source. ✅
101 +
102 +- Rejouer un sync isolé : `python3 run.py sync --source monteregie` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='monteregie' ORDER BY ts DESC LIMIT 5;"`
103 +
104 +## Licence / attribution
105 +
106 +- API REST WordPress publique (aucune auth), robots.txt sans interdiction sur /wp-json/ — GET throttlés 0,8 s, UA honnête
107 +
108 +## Historique
109 +
110 +- **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
111 +- **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
112 +- **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
modified docs/connecteurs/montreal.md +27 −27
@@ -1,6 +1,6 @@
1 1 # Ville de Montréal — Événements publics (`montreal`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `donnees-ouvertes` · Connecteur : `sortika/connectors/montreal.py`
6 6
@@ -37,29 +37,29 @@
37 37
38 38 ## Champs récupérés → schéma
39 39
40 Complétude mesurée sur les **3291 événements actifs** de la source ; exemples tirés d'un événement réel (`montreal:les-legendaires-cine-ete-111611`).
40 +Complétude mesurée sur les **3567 événements actifs** de la source ; exemples tirés d'un événement réel (`montreal:schulichbon-pasteur-nelson-lee-denise-djokic-et-kyoko-hashimoto-118821`).
41 41
42 42 | Champ (`events.*`) | Description | Complétude | Exemple réel |
43 43 |---|---|---|---|
44 | `title` — Titre | titre de l'événement | 100 % | Les Légendaires – Ciné-été |
45 | `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Découvrez cette adaptation cinématographique des bandes dessinées de Patrick Sobral, réalisée pa… |
46 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://montreal.ca/evenements/les-legendaires-cine-ete-111611 |
47 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["cinema"] |
48 | `venue` — Salle (venue) | nom du lieu | 5 % | Cinéma Beaubien |
49 | `address` — Adresse | adresse civique | 5 % | 2396 Rue Beaubien E |
44 +| `title` — Titre | titre de l'événement | 100 % | Schulich@Bon-Pasteur - Nelson Lee, Denise Djokic et Kyoko Hashimoto |
45 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Ne manquez ce concert des membres du corps professoral de l’École de musique Schulich de l’Unive… |
46 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://montreal.ca/evenements/schulichbon-pasteur-nelson-lee-denise-djokic-et-kyoko-hashimoto-1… |
47 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
48 +| `venue` — Salle (venue) | nom du lieu | 4 % | Salle Paul-Desmarais |
49 +| `address` — Adresse | adresse civique | 4 % | 1920, rue Baile, Centre Canadien d'Architecture |
50 50 | `city` — Ville | municipalité | 100 % | Montréal |
51 51 | `region` — Région | région administrative | 100 % | Montréal |
52 | `lat` — GPS (lat/lng) | coordonnées géographiques | 99 % | 45.548115, -73.5921 |
53 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
54 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
55 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 09:30 |
56 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 18 % | 11:05 |
52 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 99 % | 45.491008, -73.578662 |
53 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-12-09 |
54 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-12-09 |
55 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 99 % | 19:30 |
56 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 99 % | 20:45 |
57 57 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
58 58 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 100 % | 1 |
59 59 | `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
60 60 | `price_label` — Libellé prix | étiquette tarifaire affichable | 100 % | Gratuit — Avec billet |
61 61 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
62 | `organizer` — Organisateur | promoteur / organisme | 100 % | Ville de Montréal — Rosemont–La Petite-Patrie |
62 +| `organizer` — Organisateur | promoteur / organisme | 100 % | Ville de Montréal — Ville de Montréal |
63 63 | `image` — Image | visuel (og:image ou média source) | 100 % | https://res.cloudinary.com/villemontreal/image/upload/w_1200,h_630,c_fill/f_auto,dpr_auto,q_auto… |
64 64 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
65 65
@@ -75,19 +75,19 @@ Complétude mesurée sur les **3291 événements actifs** de la source ; exemple
75 75
76 76 ## Volumétrie & complétude live
77 77
78 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
79
80 - **Événements en base** : 5809 (dont **3291 actifs**)
81 - **GPS** : 3253/3291 actifs (99 %)
82 - **Prix** : 3291/3291 actifs (100 %)
83 - **Heure (start_time)** : 3277/3291 actifs (100 %)
84 - **Heure de fin** : 588/3291 actifs (18 %)
85 - **Statut (annulé/reporté/complet)** : 0/3291 actifs (0 %)
86 - **Artistes** : 0/3291 actifs (0 %)
87 - **Image** : 3291/3291 actifs (100 %)
88 - **Salle (venue)** : 160/3291 actifs (5 %)
89 - **Dernier sync** : 2026-08-19 01:33 — fetched 5686, +0 ajoutés, ~300 mis à jour, -0 retirés
90 - **Syncs enregistrés** (sync_log) : 44
78 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
79 +
80 +- **Événements en base** : 6205 (dont **3567 actifs**)
81 +- **GPS** : 3531/3567 actifs (99 %)
82 +- **Prix** : 3567/3567 actifs (100 %)
83 +- **Heure (start_time)** : 3548/3567 actifs (99 %)
84 +- **Heure de fin** : 3548/3567 actifs (99 %)
85 +- **Statut (annulé/reporté/complet)** : 0/3567 actifs (0 %)
86 +- **Artistes** : 0/3567 actifs (0 %)
87 +- **Image** : 3567/3567 actifs (100 %)
88 +- **Salle (venue)** : 155/3567 actifs (4 %)
89 +- **Dernier sync** : 2026-08-21 20:55 — fetched 5855, +36 ajoutés, ~6 mis à jour, -0 retirés
90 +- **Syncs enregistrés** (sync_log) : 103
91 91
92 92 ## Erreurs & dépannage
93 93
modified docs/connecteurs/outaouais.md +25 −21
@@ -1,6 +1,6 @@
1 1 # Tourisme Outaouais — calendrier des événements (ATR) (`outaouais`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `tourisme-regional` · Connecteur : `sortika/connectors/outaouais.py`
6 6
@@ -42,21 +42,21 @@
42 42
43 43 ## Champs récupérés → schéma
44 44
45 Complétude mesurée sur les **27 événements actifs** de la source ; exemples tirés d'un événement réel (`outaouais:6a680428b2fca6a632e1e46b`).
45 +Complétude mesurée sur les **24 événements actifs** de la source ; exemples tirés d'un événement réel (`outaouais:6a756bca300d5843f38a1a25`).
46 46
47 47 | Champ (`events.*`) | Description | Complétude | Exemple réel |
48 48 |---|---|---|---|
49 | `title` — Titre | titre de l'événement | 100 % | La marche Maple Leaf |
49 +| `title` — Titre | titre de l'événement | 100 % | Place Laval |
50 50 | `description` — Description | texte descriptif (source ou fiche détail) | 0 % | ∅ |
51 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://tourismeoutaouais.com/attraits/la-marche-maple-leaf |
52 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["marche-foire"] |
51 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://tourismeoutaouais.com/attraits/place-laval |
52 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
53 53 | `venue` — Salle (venue) | nom du lieu | 0 % | ∅ |
54 54 | `address` — Adresse | adresse civique | 0 % | ∅ |
55 | `city` — Ville | municipalité | 59 % | ∅ |
55 +| `city` — Ville | municipalité | 58 % | Gatineau |
56 56 | `region` — Région | région administrative | 100 % | Outaouais |
57 57 | `lat` — GPS (lat/lng) | coordonnées géographiques | 0 % | ∅ |
58 | `start_date` — Date début | date locale AAAA-MM-JJ | 93 % | 2026-10-09 |
59 | `end_date` — Date fin | date locale AAAA-MM-JJ | 93 % | 2026-10-11 |
58 +| `start_date` — Date début | date locale AAAA-MM-JJ | 96 % | ∅ |
59 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 96 % | ∅ |
60 60 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
62 62 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
@@ -65,7 +65,7 @@ Complétude mesurée sur les **27 événements actifs** de la source ; exemples
65 65 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
66 66 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
67 67 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
68 | `image` — Image | visuel (og:image ou média source) | 96 % | https://admin.tourismeoutaouais.com/api/photos/file/ld-la-marche-maple-leaf-la-marche-maple-leaf… |
68 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://admin.tourismeoutaouais.com/api/photos/file/ld-camille-labonte-place-laval-2-620x620.web… |
69 69 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
70 70
71 71 ## Fréquence & budgets
@@ -75,19 +75,19 @@ Complétude mesurée sur les **27 événements actifs** de la source ; exemples
75 75
76 76 ## Volumétrie & complétude live
77 77
78 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
78 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
79 79
80 - **Événements en base** : 162 (dont **27 actifs**)
81 - **GPS** : 0/27 actifs (0 %)
82 - **Prix** : 0/27 actifs (0 %)
83 - **Heure (start_time)** : 0/27 actifs (0 %)
84 - **Heure de fin** : 0/27 actifs (0 %)
85 - **Statut (annulé/reporté/complet)** : 0/27 actifs (0 %)
86 - **Artistes** : 0/27 actifs (0 %)
87 - **Image** : 26/27 actifs (96 %)
88 - **Salle (venue)** : 0/27 actifs (0 %)
89 - **Dernier sync** : 2026-08-19 02:19 — fetched 162, +0 ajoutés, ~3 mis à jour, -0 retirés
90 - **Syncs enregistrés** (sync_log) : 2
80 +- **Événements en base** : 162 (dont **24 actifs**)
81 +- **GPS** : 0/24 actifs (0 %)
82 +- **Prix** : 0/24 actifs (0 %)
83 +- **Heure (start_time)** : 0/24 actifs (0 %)
84 +- **Heure de fin** : 0/24 actifs (0 %)
85 +- **Statut (annulé/reporté/complet)** : 0/24 actifs (0 %)
86 +- **Artistes** : 0/24 actifs (0 %)
87 +- **Image** : 24/24 actifs (100 %)
88 +- **Salle (venue)** : 0/24 actifs (0 %)
89 +- **Dernier sync** : 2026-08-21 20:55 — fetched 159, +0 ajoutés, ~0 mis à jour, -0 retirés
90 +- **Syncs enregistrés** (sync_log) : 61
91 91
92 92 ## Erreurs & dépannage
93 93
@@ -104,3 +104,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
104 104 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
105 105 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
106 106 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
107 +
108 +Commits touchant `sortika/connectors/outaouais.py` :
109 +
110 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
added docs/connecteurs/ovation.md +124 −0
@@ -0,0 +1,124 @@
1 +# Réseau Ovation — billetterie québécoise multi-salles (`ovation`)
2 +
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 +
5 +**Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/ovation.py`
6 +
7 +## Description
8 +
9 +- **Source** : [Réseau Ovation — billetterie québécoise multi-salles](https://reseau.ovation.ca/)
10 +- **Couverture** : 26 organisations 100 % QC : Salle Albert-Rousseau, Petit Champlain, Diffusion Saguenay, Gatineau (Odyssée, Jean-Despréz), Vieux Terrebonne, Palace de Granby, Vieux Clocher Magog/Waterloo, La Tuque, Festival Western de St-Tite, clubs LHJMQ/LNAH…
11 +- **Extraction** : Sous-site mobile m.ovation.ca (HTML rendu serveur, AUCUN anti-bot) : par organisation (codes base36 validés par balayage, config data/ovation_orgs.json), /fr/Search/AllEvents paginé (?pageNumber=N) → 1 eventId = 1 représentation ; fiches fragment EventDescription (≈5 Ko, OVA_FICHE_MAX=40/sync) : date pleine FR + heure, catégories, ARTISTES (liens EventsByPerformer), description, salle + adresse + ville + code postal. Prix non exposé → jamais inventé.
12 +
13 +## Accès
14 +
15 +- **Type** : billetterie
16 +- **Endpoint** (`BASE`) : `https://m.ovation.ca`
17 +- **Auth** : Aucune (accès public, identification honnête par User-Agent)
18 +- **Pagination** : par numéro de page
19 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
20 +- **Throttle** : 30.5 s entre requêtes
21 +- **Accès légal** : Billetterie publique sans auth ; GET throttlés 0,7 s, UA honnête, lien d'achat vers la fiche officielle
22 +
23 +<details><summary>En-tête documentaire du connecteur</summary>
24 +
25 +> connectors/ovation.py : Réseau Ovation — billetterie 100 % québécoise
26 +> (Phase 4). ~26 organisations (salles, diffuseurs municipaux, clubs
27 +> sportifs) : Salle Albert-Rousseau, Petit Champlain, Diffusion Saguenay,
28 +> Gatineau, Terrebonne, Granby, Magog, La Tuque… (sondé 2026-08-21).
29 +> Source : https://m.ovation.ca (sous-site mobile : HTML rendu serveur
30 +> propre, AUCUN anti-bot — le desktop reseau.ovation.ca exige
31 +> des cookies de session). Organisations configurées dans
32 +> data/ovation_orgs.json (ajouter une org = 1 bloc, 0 code ;
33 +> codes validés par balayage base36).
34 +> Extraction: par org, /fr/Search/AllEvents paginé (?pageNumber=N, 40
35 +> cartes/page) → eventIds ; chaque eventId = UNE représentation
36 +> datée. Fiches fragment /fr/Event/EventDescription?eventId=…
37 +> (≈ 5 Ko) incrémentales (cache data/ovation_cache.json,
38 +> OVA_FICHE_MAX/sync, re-visite 7 j) : date pleine FR
39 +> (« mercredi 26 août 2026, 20h00 »), catégories, artistes
40 +> (EventsByPerformer), description, salle + adresse + ville +
41 +> code postal. Prix non exposé par le sous-site mobile →
42 +> jamais inventé.
43 +> Accès : billetterie publique, HTML public sans auth. robots.txt
44 +> (lu 2026-08-21) : User-agent * → Crawl-delay: 30 +
45 +> Disallow /event/tickets (jamais requêté) → GET espacés de
46 +> 30,5 s sur m.ovation.ca (hôte unique). Pour rester poli, le
47 +> PASSAGE RÉSEAU complet (listes + fiches) n'a lieu qu'à
48 +> l'expiration d'un TTL (OVA_TTL_HOURS, 20 h — ≈ 45 min de
49 +> moisson/jour) ; entre deux passages, le connecteur réémet son
50 +> cache. Lien d'achat = la fiche officielle.
51 +
52 +</details>
53 +
54 +## Champs récupérés → schéma
55 +
56 +Complétude mesurée sur les **212 événements actifs** de la source ; exemples tirés d'un événement réel (`ovation:00000O-1D068U`).
57 +
58 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
59 +|---|---|---|---|
60 +| `title` — Titre | titre de l'événement | 100 % | Louis-Jean Cormier et l’Orchestre FILMharmonique |
61 +| `description` — Description | texte descriptif (source ou fiche détail) | 77 % | Depuis plus de vingt ans, Louis-Jean Cormier explore l’intime avec une sensibilité qui a marqué … |
62 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://m.ovation.ca/00000O/fr/Event/?eventId=1D068U |
63 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
64 +| `venue` — Salle (venue) | nom du lieu | 100 % | Centre culturel de l’Université de Sherbrooke - Salle Maurice-O’Bready |
65 +| `address` — Adresse | adresse civique | 100 % | 2500, boul. de l’Université |
66 +| `city` — Ville | municipalité | 100 % | Sherbrooke |
67 +| `region` — Région | région administrative | 100 % | Estrie |
68 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.3767733, -71.940274 |
69 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-10 |
70 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-10 |
71 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 20:00 |
72 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
73 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
74 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
75 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
76 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
77 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 94 % | ["Louis-Jean Cormier", "Orchestre FILMharmonique"] |
78 +| `organizer` — Organisateur | promoteur / organisme | 100 % | Centre culturel de l'Université de Sherbrooke |
79 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://m.ovation.ca/product-catalog-images/thumbnails/ca5a09cd-236b-427a-aa44-f79eafcb130c.jpg |
80 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
81 +
82 +## Fréquence & budgets
83 +
84 +- **Cadence** : horaire (watcher PM2)
85 +- **Budgets / plafonds (variables d'environnement)** :
86 +
87 +| Variable | Défaut | Rôle |
88 +|---|---|---|
89 +| `OVA_PAGE_MAX` | `6` | — |
90 +| `OVA_FICHE_MAX` | `50` | — |
91 +| `OVA_TTL_HOURS` | `20` | — |
92 +- **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
93 +
94 +## Volumétrie & complétude live
95 +
96 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
97 +
98 +- **Événements en base** : 212 (dont **212 actifs**)
99 +- **GPS** : 212/212 actifs (100 %)
100 +- **Prix** : 0/212 actifs (0 %)
101 +- **Heure (start_time)** : 212/212 actifs (100 %)
102 +- **Heure de fin** : 0/212 actifs (0 %)
103 +- **Statut (annulé/reporté/complet)** : 0/212 actifs (0 %)
104 +- **Artistes** : 199/212 actifs (94 %)
105 +- **Image** : 211/212 actifs (100 %)
106 +- **Salle (venue)** : 212/212 actifs (100 %)
107 +- **Dernier sync** : 2026-08-21 21:46 — fetched 212, +212 ajoutés, ~0 mis à jour, -0 retirés
108 +- **Syncs enregistrés** (sync_log) : 1
109 +
110 +## Erreurs & dépannage
111 +
112 +Aucune erreur dans `sync_log` pour cette source. ✅
113 +
114 +- Rejouer un sync isolé : `python3 run.py sync --source ovation` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='ovation' ORDER BY ts DESC LIMIT 5;"`
115 +
116 +## Licence / attribution
117 +
118 +- Billetterie publique sans auth ; GET throttlés 0,7 s, UA honnête, lien d'achat vers la fiche officielle
119 +
120 +## Historique
121 +
122 +- **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
123 +- **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
124 +- **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
modified docs/connecteurs/placedesarts.md +22 −18
@@ -1,6 +1,6 @@
1 1 # Place des Arts — calendrier multi-salles (`placedesarts`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `salle-de-spectacle` · Connecteur : `sortika/connectors/placedesarts.py`
6 6
@@ -44,7 +44,7 @@
44 44
45 45 ## Champs récupérés → schéma
46 46
47 Complétude mesurée sur les **350 événements actifs** de la source ; exemples tirés d'un événement réel (`placedesarts:24-poses-portraits-2026-09-02-19:30`).
47 +Complétude mesurée sur les **347 événements actifs** de la source ; exemples tirés d'un événement réel (`placedesarts:24-poses-portraits-2026-09-02-19:30`).
48 48
49 49 | Champ (`events.*`) | Description | Complétude | Exemple réel |
50 50 |---|---|---|---|
@@ -59,12 +59,12 @@ Complétude mesurée sur les **350 événements actifs** de la source ; exemples
59 59 | `lat` — GPS (lat/lng) | coordonnées géographiques | 94 % | 45.5084325, -73.5658186 |
60 60 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-02 |
61 61 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-02 |
62 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 99 % | 19:30 |
62 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 19:30 |
63 63 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
64 64 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
65 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 72 % | 0 |
66 | `price_min` — Prix min | tarif minimal réel ($) | 72 % | 33.0 |
67 | `price_label` — Libellé prix | étiquette tarifaire affichable | 72 % | 33 $ à 97,50 $ |
65 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 73 % | 0 |
66 +| `price_min` — Prix min | tarif minimal réel ($) | 73 % | 33.0 |
67 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 73 % | 33 $ à 97,50 $ |
68 68 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
69 69 | `organizer` — Organisateur | promoteur / organisme | 100 % | Duceppe |
70 70 | `image` — Image | visuel (og:image ou média source) | 100 % | https://cdn.placedesarts.com/tranforms/_300x140_crop_center-center_none_ns/1657294/24POSES_DU252… |
@@ -82,19 +82,19 @@ Complétude mesurée sur les **350 événements actifs** de la source ; exemples
82 82
83 83 ## Volumétrie & complétude live
84 84
85 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
85 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
86 86
87 - **Événements en base** : 352 (dont **350 actifs**)
88 - **GPS** : 328/350 actifs (94 %)
89 - **Prix** : 253/350 actifs (72 %)
90 - **Heure (start_time)** : 348/350 actifs (99 %)
91 - **Heure de fin** : 0/350 actifs (0 %)
92 - **Statut (annulé/reporté/complet)** : 0/350 actifs (0 %)
93 - **Artistes** : 0/350 actifs (0 %)
94 - **Image** : 350/350 actifs (100 %)
95 - **Salle (venue)** : 350/350 actifs (100 %)
96 - **Dernier sync** : 2026-08-19 02:15 — fetched 352, +352 ajoutés, ~0 mis à jour, -0 retirés
97 - **Syncs enregistrés** (sync_log) : 1
87 +- **Événements en base** : 356 (dont **347 actifs**)
88 +- **GPS** : 325/347 actifs (94 %)
89 +- **Prix** : 253/347 actifs (73 %)
90 +- **Heure (start_time)** : 346/347 actifs (100 %)
91 +- **Heure de fin** : 0/347 actifs (0 %)
92 +- **Statut (annulé/reporté/complet)** : 0/347 actifs (0 %)
93 +- **Artistes** : 0/347 actifs (0 %)
94 +- **Image** : 347/347 actifs (100 %)
95 +- **Salle (venue)** : 347/347 actifs (100 %)
96 +- **Dernier sync** : 2026-08-21 20:55 — fetched 354, +0 ajoutés, ~0 mis à jour, -0 retirés
97 +- **Syncs enregistrés** (sync_log) : 60
98 98
99 99 ## Erreurs & dépannage
100 100
@@ -111,3 +111,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
111 111 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
112 112 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
113 113 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
114 +
115 +Commits touchant `sortika/connectors/placedesarts.py` :
116 +
117 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/quebecanimee.md +27 −23
@@ -1,6 +1,6 @@
1 1 # Québec animée — Ville de Québec (`quebecanimee`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/quebecanimee.py`
6 6
@@ -41,21 +41,21 @@
41 41
42 42 ## Champs récupérés → schéma
43 43
44 Complétude mesurée sur les **126 événements actifs** de la source ; exemples tirés d'un événement réel (`quebecanimee:cinema-en-plein-air-de-radio-canada-2026-08-19`).
44 +Complétude mesurée sur les **121 événements actifs** de la source ; exemples tirés d'un événement réel (`quebecanimee:videoprojection-sur-la-bibliotheque-gabrielle-roy-avec-irina-gonzalez-2026-09-04`).
45 45
46 46 | Champ (`events.*`) | Description | Complétude | Exemple réel |
47 47 |---|---|---|---|
48 | `title` — Titre | titre de l'événement | 100 % | Cinéma en plein air de Radio-Canada |
49 | `description` — Description | texte descriptif (source ou fiche détail) | 54 % | 19 août 2026, Parc de la Pointe-aux-Lièvres |
50 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://animee.ville.quebec.qc.ca/activites/cinema-en-plein-air-de-radio-canada/ |
51 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["cinema", "famille"] |
52 | `venue` — Salle (venue) | nom du lieu | 54 % | Lieux |
48 +| `title` — Titre | titre de l'événement | 100 % | Vidéoprojection sur la bibliothèque Gabrielle-Roy avec Irina González |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | 4 septembre 2026, Place Jacques-Cartier |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://animee.ville.quebec.qc.ca/activites/videoprojection-sur-la-bibliotheque-gabrielle-roy-av… |
51 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["conference", "exposition-musee"] |
52 +| `venue` — Salle (venue) | nom du lieu | 100 % | Lieux |
53 53 | `address` — Adresse | adresse civique | 0 % | ∅ |
54 54 | `city` — Ville | municipalité | 100 % | Québec |
55 55 | `region` — Région | région administrative | 100 % | Capitale-Nationale |
56 | `lat` — GPS (lat/lng) | coordonnées géographiques | 54 % | 48.4527492, -71.0736674 |
57 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
58 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-19 |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 48.4527492, -71.0736674 |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-04 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-04 |
59 59 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
60 60 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
@@ -64,7 +64,7 @@ Complétude mesurée sur les **126 événements actifs** de la source ; exemples
64 64 | `price_label` — Libellé prix | étiquette tarifaire affichable | 100 % | Gratuit |
65 65 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
66 66 | `organizer` — Organisateur | promoteur / organisme | 100 % | Ville de Québec |
67 | `image` — Image | visuel (og:image ou média source) | 100 % | https://animee.ville.quebec.qc.ca/media/jvoijf3z/rc_photo_cine-dans-lparc.jpg?width=320&amp;heig… |
67 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://animee.ville.quebec.qc.ca/media/zpmo3k2f/lueursmemoire_irina_16x9-22.jpg?width=320&amp;h… |
68 68 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
69 69
70 70 ## Fréquence & budgets
@@ -79,19 +79,19 @@ Complétude mesurée sur les **126 événements actifs** de la source ; exemples
79 79
80 80 ## Volumétrie & complétude live
81 81
82 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
82 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
83 83
84 - **Événements en base** : 126 (dont **126 actifs**)
85 - **GPS** : 68/126 actifs (54 %)
86 - **Prix** : 126/126 actifs (100 %)
87 - **Heure (start_time)** : 0/126 actifs (0 %)
88 - **Heure de fin** : 0/126 actifs (0 %)
89 - **Statut (annulé/reporté/complet)** : 0/126 actifs (0 %)
90 - **Artistes** : 0/126 actifs (0 %)
91 - **Image** : 126/126 actifs (100 %)
92 - **Salle (venue)** : 68/126 actifs (54 %)
93 - **Dernier sync** : 2026-08-19 02:10 — fetched 126, +126 ajoutés, ~0 mis à jour, -0 retirés
94 - **Syncs enregistrés** (sync_log) : 1
84 +- **Événements en base** : 131 (dont **121 actifs**)
85 +- **GPS** : 121/121 actifs (100 %)
86 +- **Prix** : 121/121 actifs (100 %)
87 +- **Heure (start_time)** : 0/121 actifs (0 %)
88 +- **Heure de fin** : 0/121 actifs (0 %)
89 +- **Statut (annulé/reporté/complet)** : 0/121 actifs (0 %)
90 +- **Artistes** : 0/121 actifs (0 %)
91 +- **Image** : 121/121 actifs (100 %)
92 +- **Salle (venue)** : 121/121 actifs (100 %)
93 +- **Dernier sync** : 2026-08-21 20:55 — fetched 115, +0 ajoutés, ~0 mis à jour, -0 retirés
94 +- **Syncs enregistrés** (sync_log) : 60
95 95
96 96 ## Erreurs & dépannage
97 97
@@ -108,3 +108,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
108 108 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
109 109 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
110 110 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
111 +
112 +Commits touchant `sortika/connectors/quebecanimee.py` :
113 +
114 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/repentigny.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Repentigny — Événements (`repentigny`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `donnees-ouvertes` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/saguenay.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Saguenay — Événements culturels (`saguenay`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `donnees-ouvertes` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/saguenaysorties.md +30 −26
@@ -1,6 +1,6 @@
1 1 # Sorties Saguenay — portail événementiel de la Ville (`saguenaysorties`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/saguenaysorties.py`
6 6
@@ -41,22 +41,22 @@
41 41
42 42 ## Champs récupérés → schéma
43 43
44 Complétude mesurée sur les **445 événements actifs** de la source ; exemples tirés d'un événement réel (`saguenaysorties:497146-2026-08-20-08:30`).
44 +Complétude mesurée sur les **437 événements actifs** de la source ; exemples tirés d'un événement réel (`saguenaysorties:570260-2027-01-28-20:00`).
45 45
46 46 | Champ (`events.*`) | Description | Complétude | Exemple réel |
47 47 |---|---|---|---|
48 | `title` — Titre | titre de l'événement | 100 % | Rencontre avec les archéologues sur le terrain |
49 | `description` — Description | texte descriptif (source ou fiche détail) | 51 % | ∅ |
50 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://sorties.saguenay.ca/festivals?id=497146 |
51 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["patrimoine"] |
52 | `venue` — Salle (venue) | nom du lieu | 100 % | Poste-de-traite-de-Chicoutimi |
53 | `address` — Adresse | adresse civique | 51 % | ∅ |
54 | `city` — Ville | municipalité | 87 % | Chicoutimi |
48 +| `title` — Titre | titre de l'événement | 100 % | Confessions d'un enfant du siècle dernier |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 98 % | Cinq facettes d’un jeune homme en quête de lui-même. Entre l’école et la maison, un adolescent d… |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://sorties.saguenay.ca/festivals?id=570260 |
51 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["arts-scene"] |
52 +| `venue` — Salle (venue) | nom du lieu | 86 % | Théâtre C |
53 +| `address` — Adresse | adresse civique | 100 % | 534 Rue Jacques-Cartier E |
54 +| `city` — Ville | municipalité | 86 % | Chicoutimi |
55 55 | `region` — Région | région administrative | 100 % | Saguenay–Lac-Saint-Jean |
56 | `lat` — GPS (lat/lng) | coordonnées géographiques | 33 % | 48.4287017, -71.0756153 |
57 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-20 |
58 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-20 |
59 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 08:30 |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 80 % | 48.424452, -71.053231 |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2027-01-28 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2027-01-28 |
59 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 20:00 |
60 60 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
62 62 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
@@ -64,7 +64,7 @@ Complétude mesurée sur les **445 événements actifs** de la source ; exemples
64 64 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
65 65 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
66 66 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
67 | `image` — Image | visuel (og:image ou média source) | 100 % | https://lvc-medias.lavitrine.com/77b5b9cf-95f5-517a-be63-82ed1e8e4806.jpg |
67 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://lvc-medias.lavitrine.com/54f46863-28df-579a-8bcf-9471f0383d1f.jpg |
68 68 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
69 69
70 70 ## Fréquence & budgets
@@ -80,19 +80,19 @@ Complétude mesurée sur les **445 événements actifs** de la source ; exemples
80 80
81 81 ## Volumétrie & complétude live
82 82
83 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
83 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
84 84
85 - **Événements en base** : 445 (dont **445 actifs**)
86 - **GPS** : 149/445 actifs (33 %)
87 - **Prix** : 0/445 actifs (0 %)
88 - **Heure (start_time)** : 444/445 actifs (100 %)
89 - **Heure de fin** : 0/445 actifs (0 %)
90 - **Statut (annulé/reporté/complet)** : 0/445 actifs (0 %)
91 - **Artistes** : 0/445 actifs (0 %)
92 - **Image** : 445/445 actifs (100 %)
93 - **Salle (venue)** : 445/445 actifs (100 %)
94 - **Dernier sync** : 2026-08-19 02:19 — fetched 445, +445 ajoutés, ~0 mis à jour, -0 retirés
95 - **Syncs enregistrés** (sync_log) : 1
85 +- **Événements en base** : 460 (dont **437 actifs**)
86 +- **GPS** : 350/437 actifs (80 %)
87 +- **Prix** : 0/437 actifs (0 %)
88 +- **Heure (start_time)** : 436/437 actifs (100 %)
89 +- **Heure de fin** : 0/437 actifs (0 %)
90 +- **Statut (annulé/reporté/complet)** : 0/437 actifs (0 %)
91 +- **Artistes** : 0/437 actifs (0 %)
92 +- **Image** : 437/437 actifs (100 %)
93 +- **Salle (venue)** : 376/437 actifs (86 %)
94 +- **Dernier sync** : 2026-08-21 20:55 — fetched 437, +0 ajoutés, ~0 mis à jour, -14 retirés
95 +- **Syncs enregistrés** (sync_log) : 60
96 96
97 97 ## Erreurs & dépannage
98 98
@@ -109,3 +109,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
109 109 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
110 110 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
111 111 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
112 +
113 +Commits touchant `sortika/connectors/saguenaysorties.py` :
114 +
115 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/sallesjsonld.md +30 −26
@@ -1,6 +1,6 @@
1 1 # Salles de spectacle — moteur générique JSON-LD Event (`sallesjsonld`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `salle-de-spectacle` · Connecteur : `sortika/connectors/sallesjsonld.py`
6 6
@@ -43,31 +43,31 @@
43 43
44 44 ## Champs récupérés → schéma
45 45
46 Complétude mesurée sur les **276 événements actifs** de la source ; exemples tirés d'un événement réel (`sallesjsonld:grandtheatre-decembre-signe-quebec-issime-2026-12-04-0030`).
46 +Complétude mesurée sur les **278 événements actifs** de la source ; exemples tirés d'un événement réel (`sallesjsonld:grandtheatre-arpin-lepine-tournee-lune-brune-2026-10-04-0000`).
47 47
48 48 | Champ (`events.*`) | Description | Complétude | Exemple réel |
49 49 |---|---|---|---|
50 | `title` — Titre | titre de l'événement | 100 % | Décembre signé Québec Issime |
51 | `description` — Description | texte descriptif (source ou fiche détail) | 100 % | Célébrez la 4e décennie de magie, de musique et de souvenirs du spectacle Décembre lors de son g… |
52 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://grandtheatre.qc.ca/programmation/decembre-signe-quebec-issime/ |
50 +| `title` — Titre | titre de l'événement | 100 % | Tournée Lune Brune |
51 +| `description` — Description | texte descriptif (source ou fiche détail) | 100 % | PRÉVENTE EN COURS Pour être informé des prochaines préventes, inscrivez vous à l'infolettre!Arpi… |
52 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://grandtheatre.qc.ca/programmation/arpin-lepine-tournee-lune-brune/ |
53 53 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
54 54 | `venue` — Salle (venue) | nom du lieu | 100 % | Grand Théatre de Québec |
55 55 | `address` — Adresse | adresse civique | 100 % | 269 Boulevard René-Lévesque E |
56 56 | `city` — Ville | municipalité | 100 % | Québec |
57 57 | `region` — Région | région administrative | 100 % | Capitale-Nationale |
58 | `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 46.806236, -71.221762 |
59 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-12-03 |
60 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-12-03 |
61 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 19:30 |
62 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 47 % | 23:30 |
63 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 1 % | ∅ |
58 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 46.8062847, -71.2217335 |
59 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-10-03 |
60 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-10-04 |
61 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 20:00 |
62 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 47 % | ∅ |
63 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 1 % | cancelled |
64 64 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 97 % | 0 |
65 | `price_min` — Prix min | tarif minimal réel ($) | 97 % | 98.0 |
66 | `price_label` — Libellé prix | étiquette tarifaire affichable | 97 % | 98.00 $ |
65 +| `price_min` — Prix min | tarif minimal réel ($) | 97 % | 25.0 |
66 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 97 % | 25.00 $ |
67 67 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
68 68 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
69 69 | `image` — Image | visuel (og:image ou média source) | 3 % | ∅ |
70 | `website` — Site web | site officiel de l'événement | 100 % | https://am.ticketmaster.com/grandtheatre/fr-ca/buy/virtual-venue/TDAyNjIyMDE%3D |
70 +| `website` — Site web | site officiel de l'événement | 100 % | https://am.ticketmaster.com/grandtheatre/fr-ca/buy/ism/UzEyNjEyMDE%3D |
71 71
72 72 ## Fréquence & budgets
73 73
@@ -81,19 +81,19 @@ Complétude mesurée sur les **276 événements actifs** de la source ; exemples
81 81
82 82 ## Volumétrie & complétude live
83 83
84 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
84 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
85 85
86 - **Événements en base** : 276 (dont **276 actifs**)
87 - **GPS** : 276/276 actifs (100 %)
88 - **Prix** : 267/276 actifs (97 %)
89 - **Heure (start_time)** : 276/276 actifs (100 %)
90 - **Heure de fin** : 131/276 actifs (47 %)
91 - **Statut (annulé/reporté/complet)** : 2/276 actifs (1 %)
92 - **Artistes** : 0/276 actifs (0 %)
93 - **Image** : 9/276 actifs (3 %)
94 - **Salle (venue)** : 276/276 actifs (100 %)
95 - **Dernier sync** : 2026-08-19 02:19 — fetched 276, +0 ajoutés, ~267 mis à jour, -0 retirés
96 - **Syncs enregistrés** (sync_log) : 2
86 +- **Événements en base** : 282 (dont **278 actifs**)
87 +- **GPS** : 278/278 actifs (100 %)
88 +- **Prix** : 270/278 actifs (97 %)
89 +- **Heure (start_time)** : 278/278 actifs (100 %)
90 +- **Heure de fin** : 131/278 actifs (47 %)
91 +- **Statut (annulé/reporté/complet)** : 2/278 actifs (1 %)
92 +- **Artistes** : 0/278 actifs (0 %)
93 +- **Image** : 8/278 actifs (3 %)
94 +- **Salle (venue)** : 278/278 actifs (100 %)
95 +- **Dernier sync** : 2026-08-21 20:55 — fetched 281, +0 ajoutés, ~0 mis à jour, -0 retirés
96 +- **Syncs enregistrés** (sync_log) : 61
97 97
98 98 ## Erreurs & dépannage
99 99
@@ -110,3 +110,7 @@ Aucune erreur dans `sync_log` pour cette source. ✅
110 110 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
111 111 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
112 112 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
113 +
114 +Commits touchant `sortika/connectors/sallesjsonld.py` :
115 +
116 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
modified docs/connecteurs/sherbrooke.md +16 −16
@@ -1,6 +1,6 @@
1 1 # Ville de Sherbrooke — Calendrier des événements (`sherbrooke`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `municipal` · Connecteur : `sortika/connectors/sherbrooke.py`
6 6
@@ -36,7 +36,7 @@
36 36
37 37 ## Champs récupérés → schéma
38 38
39 Complétude mesurée sur les **116 événements actifs** de la source ; exemples tirés d'un événement réel (`sherbrooke:2818`).
39 +Complétude mesurée sur les **114 événements actifs** de la source ; exemples tirés d'un événement réel (`sherbrooke:2818`).
40 40
41 41 | Champ (`events.*`) | Description | Complétude | Exemple réel |
42 42 |---|---|---|---|
@@ -51,7 +51,7 @@ Complétude mesurée sur les **116 événements actifs** de la source ; exemples
51 51 | `lat` — GPS (lat/lng) | coordonnées géographiques | 0 % | ∅ |
52 52 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-17 |
53 53 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-21 |
54 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 87 % | 09:00 |
54 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 86 % | 09:00 |
55 55 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 92 % | 17:00 |
56 56 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
57 57 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 100 % | 0 |
@@ -69,19 +69,19 @@ Complétude mesurée sur les **116 événements actifs** de la source ; exemples
69 69
70 70 ## Volumétrie & complétude live
71 71
72 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
73
74 - **Événements en base** : 271 (dont **116 actifs**)
75 - **GPS** : 0/116 actifs (0 %)
76 - **Prix** : 116/116 actifs (100 %)
77 - **Heure (start_time)** : 101/116 actifs (87 %)
78 - **Heure de fin** : 107/116 actifs (92 %)
79 - **Statut (annulé/reporté/complet)** : 0/116 actifs (0 %)
80 - **Artistes** : 0/116 actifs (0 %)
81 - **Image** : 115/116 actifs (99 %)
82 - **Salle (venue)** : 0/116 actifs (0 %)
83 - **Dernier sync** : 2026-08-19 01:33 — fetched 271, +0 ajoutés, ~0 mis à jour, -0 retirés
84 - **Syncs enregistrés** (sync_log) : 36
72 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
73 +
74 +- **Événements en base** : 275 (dont **114 actifs**)
75 +- **GPS** : 0/114 actifs (0 %)
76 +- **Prix** : 114/114 actifs (100 %)
77 +- **Heure (start_time)** : 98/114 actifs (86 %)
78 +- **Heure de fin** : 105/114 actifs (92 %)
79 +- **Statut (annulé/reporté/complet)** : 0/114 actifs (0 %)
80 +- **Artistes** : 0/114 actifs (0 %)
81 +- **Image** : 113/114 actifs (99 %)
82 +- **Salle (venue)** : 0/114 actifs (0 %)
83 +- **Dernier sync** : 2026-08-21 20:55 — fetched 274, +0 ajoutés, ~0 mis à jour, -0 retirés
84 +- **Syncs enregistrés** (sync_log) : 95
85 85
86 86 ## Erreurs & dépannage
87 87
modified docs/connecteurs/sitq.md +29 −28
@@ -1,6 +1,6 @@
1 1 # SIT Québec — Ministère du Tourisme (`sitq`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `donnees-ouvertes` · Connecteur : `sortika/connectors/sitq.py`
6 6
@@ -41,21 +41,21 @@
41 41
42 42 ## Champs récupérés → schéma
43 43
44 Complétude mesurée sur les **1013 événements actifs** de la source ; exemples tirés d'un événement réel (`sitq:EVENMTOV503XJP`).
44 +Complétude mesurée sur les **1020 événements actifs** de la source ; exemples tirés d'un événement réel (`sitq:EVENMTOV503QAX`).
45 45
46 46 | Champ (`events.*`) | Description | Complétude | Exemple réel |
47 47 |---|---|---|---|
48 | `title` — Titre | titre de l'événement | 100 % | Le Phoenix de Sherbrooke |
49 | `description` — Description | texte descriptif (source ou fiche détail) | 89 % | Renseignements : 819 560-8842 · billets@hockeyphoenix.ca |
50 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://chl.ca/lhjmq-phoenix |
51 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["sport"] |
52 | `venue` — Salle (venue) | nom du lieu | 73 % | Palais des sports Léopold-Drolet |
53 | `address` — Adresse | adresse civique | 84 % | 360 Rue du Cégep |
54 | `city` — Ville | municipalité | 96 % | Sherbrooke |
55 | `region` — Région | région administrative | 99 % | Estrie |
56 | `lat` — GPS (lat/lng) | coordonnées géographiques | 97 % | 45.410473, -71.883448 |
57 | `start_date` — Date début | date locale AAAA-MM-JJ | 99 % | 2026-07-19 |
58 | `end_date` — Date fin | date locale AAAA-MM-JJ | 99 % | 2027-03-20 |
48 +| `title` — Titre | titre de l'événement | 100 % | Génération MusiquePlus – 86-96 |
49 +| `description` — Description | texte descriptif (source ou fiche détail) | 89 % | Renseignements : info@espacestdenis.com |
50 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://espacestdenis.com/evenement/generation-musique-plus/ |
51 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique", "cinema", "patrimoine"] |
52 +| `venue` — Salle (venue) | nom du lieu | 74 % | Studio-Cabaret - Théâtre St-Denis |
53 +| `address` — Adresse | adresse civique | 84 % | 1594 Rue Saint-Denis |
54 +| `city` — Ville | municipalité | 95 % | Montréal |
55 +| `region` — Région | région administrative | 99 % | Montréal |
56 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 97 % | 45.514629, -73.562936 |
57 +| `start_date` — Date début | date locale AAAA-MM-JJ | 99 % | 2026-08-21 |
58 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 99 % | 2026-08-22 |
59 59 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
60 60 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
61 61 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
@@ -63,9 +63,9 @@ Complétude mesurée sur les **1013 événements actifs** de la source ; exemple
63 63 | `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
64 64 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
65 65 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
66 | `organizer` — Organisateur | promoteur / organisme | 100 % | Tourisme Cantons-de-l’Est |
67 | `image` — Image | visuel (og:image ou média source) | 63 % | https://cdn.theqmjhl.ca/uploads/sherbrooke_phoenix/2020/09/08132344/LOGO.png |
68 | `website` — Site web | site officiel de l'événement | 96 % | https://chl.ca/lhjmq-phoenix |
66 +| `organizer` — Organisateur | promoteur / organisme | 100 % | Tourisme Montréal |
67 +| `image` — Image | visuel (og:image ou média source) | 63 % | https://cdn.espacestdenis.com/uploads/_1200x630_crop_center-center_82_none/GM_EnSpectacle_1000x1… |
68 +| `website` — Site web | site officiel de l'événement | 96 % | https://espacestdenis.com/evenement/generation-musique-plus/ |
69 69
70 70 ## Fréquence & budgets
71 71
@@ -79,19 +79,19 @@ Complétude mesurée sur les **1013 événements actifs** de la source ; exemple
79 79
80 80 ## Volumétrie & complétude live
81 81
82 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
82 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
83 83
84 - **Événements en base** : 1811 (dont **1013 actifs**)
85 - **GPS** : 981/1013 actifs (97 %)
86 - **Prix** : 0/1013 actifs (0 %)
87 - **Heure (start_time)** : 0/1013 actifs (0 %)
88 - **Heure de fin** : 0/1013 actifs (0 %)
89 - **Statut (annulé/reporté/complet)** : 0/1013 actifs (0 %)
90 - **Artistes** : 0/1013 actifs (0 %)
91 - **Image** : 643/1013 actifs (63 %)
92 - **Salle (venue)** : 742/1013 actifs (73 %)
93 - **Dernier sync** : 2026-08-19 02:27 — fetched 1803, +204 ajoutés, ~35 mis à jour, -0 retirés
94 - **Syncs enregistrés** (sync_log) : 45
84 +- **Événements en base** : 1814 (dont **1020 actifs**)
85 +- **GPS** : 987/1020 actifs (97 %)
86 +- **Prix** : 0/1020 actifs (0 %)
87 +- **Heure (start_time)** : 0/1020 actifs (0 %)
88 +- **Heure de fin** : 0/1020 actifs (0 %)
89 +- **Statut (annulé/reporté/complet)** : 0/1020 actifs (0 %)
90 +- **Artistes** : 0/1020 actifs (0 %)
91 +- **Image** : 644/1020 actifs (63 %)
92 +- **Salle (venue)** : 751/1020 actifs (74 %)
93 +- **Dernier sync** : 2026-08-21 20:55 — fetched 1798, +0 ajoutés, ~0 mis à jour, -3 retirés
94 +- **Syncs enregistrés** (sync_log) : 104
95 95
96 96 ## Erreurs & dépannage
97 97
@@ -118,5 +118,6 @@ Erreurs relevées dans `sync_log` :
118 118
119 119 Commits touchant `sortika/connectors/sitq.py` :
120 120
121 +- `0b8a6f8` 2026-08-19 Phase 3 — 9 nouveaux connecteurs + déroulage des périodes SITQ (fiches d évaluation avant connexion)
121 122 - `7d9d1b0` 2026-08-18 Enrichissement des connecteurs + géocodage des salles
122 123 - `9164456` 2026-08-17 Sorti·Ka v1 — agrégateur de sorties & événements du Québec
modified docs/connecteurs/terrebonne.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Terrebonne — Calendrier (`terrebonne`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `municipal` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
added docs/connecteurs/ticketacces.md +129 −0
@@ -0,0 +1,129 @@
1 +# TicketAcces.net — billetterie régionale québécoise (`ticketacces`)
2 +
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 +
5 +**Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/ticketacces.py`
6 +
7 +## Description
8 +
9 +- **Source** : [TicketAcces.net — billetterie régionale québécoise](https://www.ticketacces.net/fr/portail/index.cfm)
10 +- **Couverture** : 47 organisations QC — dont 14 en Abitibi-Témiscamingue (Agora des Arts, Théâtre du cuivre, culture Val-d'Or/Amos/La Sarre/Malartic, Théâtre du Rift…), Bas-Saint-Laurent (Océanic, 3L), Côte-Nord (Drakkar), Charlevoix (Domaine Forget), Nord-du-Québec (Lebel-sur-Quévillon)… ~1 300 séances (vérifié 2026-08-21)
11 +- **Extraction** : HTML ColdFusion (cp1252) : annuaire complet des organisations dans le portail (cartes data-nom/ville/région, filtre Québec — 12 orgs NB + 1 ON exclues) + calendrier AGRÉGÉ multi-organisations en 4 GET (catégories Spectacles/Cinéma/Sport/Autre) : 1 Event par représentation datée (titre, date+heure FR, salle, ville, image). Fiches incrémentales (TA_DETAIL_MAX=25/sync) : og:description, adresse, prix plancher.
12 +
13 +## Accès
14 +
15 +- **Type** : billetterie
16 +- **Endpoint** (`PORTAL`) : `https://www.ticketacces.net/fr/portail/index.cfm`
17 +- **Endpoint** (`CALENDAR`) : `https://www.ticketacces.net/fr/portail/evenements/calendrier.cfm?RepresentationCategoriePrincipaleID={cat}`
18 +- **Auth** : Aucune (accès public, identification honnête par User-Agent)
19 +- **Pagination** : aucune (flux unique)
20 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
21 +- **Throttle** : 1.0 s entre requêtes
22 +- **Accès légal** : Billetterie publique, robots.txt sans interdiction sur le portail (vérifié 2026-08-21) ; GET throttlés 1 s, UA honnête, cookie de détection ColdFusion géré par session, lien d'achat vers la fiche officielle
23 +
24 +<details><summary>En-tête documentaire du connecteur</summary>
25 +
26 +> connectors/ticketacces.py : TicketAcces.net — billetterie régionale
27 +> québécoise (Phase 4). Gros levier régional : 47 organisations au Québec
28 +> dont l'Abitibi-Témiscamingue (14 !), le Bas-Saint-Laurent, la Côte-Nord,
29 +> Charlevoix, la Gaspésie… (sondé 2026-08-21).
30 +> Source : https://www.ticketacces.net (ColdFusion, encodage cp1252).
31 +> L'annuaire COMPLET des organisations est dans le portail
32 +> (cartes data-nom/data-ville/data-region/data-domaine) ; le
33 +> calendrier AGRÉGÉ multi-organisations expose chaque
34 +> représentation datée en 4 requêtes (catégories 1=Spectacles,
35 +> 2=Cinéma, 3=Sport, 4=Autre).
36 +> Extraction: portail (1 GET, annuaire + filtre Québec — le réseau compte
37 +> 12 orgs au Nouveau-Brunswick et 1 en Ontario, EXCLUES) →
38 +> calendrier par catégorie (4 GET) : titre + sous-titre, date
39 +> et heure FR (« Samedi 22 août 2026 à 12 h 30 »), « Salle,
40 +> Ville », image, lien fiche. Fiches détail incrémentales
41 +> (cache data/ticketacces_cache.json, TA_DETAIL_MAX/sync,
42 +> re-visite 7 j) : og:description, adresse civique, prix
43 +> plancher si les ventes sont ouvertes. 1 Event par
44 +> représentation (une même fiche multi-dates = plusieurs
45 +> séances, patron lepointdevente).
46 +> Accès : billetterie publique. robots.txt (lu 2026-08-21, identique
47 +> sur tous les hôtes *.ticketacces.net) : Crawl-delay: 120 +
48 +> Disallow /achat/ et representations/prix.cfm (jamais
49 +> requêtés) → GET espacés de 120 s PAR HÔTE (www porte le
50 +> portail + les 4 calendriers = 5 GET ≈ 8 min ; les fiches
51 +> vivent sur ~47 sous-domaines distincts, ≤ 1 fiche par hôte
52 +> par passage). Le passage réseau complet n'a lieu qu'à
53 +> l'expiration d'un TTL (TA_TTL_HOURS, 6 h) ; entre deux
54 +> passages, le connecteur réémet son cache. Cookie de détection
55 +> ColdFusion géré par la session (302 suivi). Lien d'achat =
56 +> la fiche officielle chez la source.
57 +
58 +</details>
59 +
60 +## Champs récupérés → schéma
61 +
62 +Complétude mesurée sur les **1320 événements actifs** de la source ; exemples tirés d'un événement réel (`ticketacces:salledesjardins-304-2026-08-22-17:00`).
63 +
64 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
65 +|---|---|---|---|
66 +| `title` — Titre | titre de l'événement | 100 % | Unchained and The madDogs - hommage à Joe Cocker — sur le Parvis |
67 +| `description` — Description | texte descriptif (source ou fiche détail) | 6 % | Venez voir Unchained and The madDogs - hommage à Joe Cocker avec Marie-Ève Brosseau en première … |
68 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://salledesjardins.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=30… |
69 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["arts-scene"] |
70 +| `venue` — Salle (venue) | nom du lieu | 100 % | Carrefour culturel Estacade - Salle Desjardins |
71 +| `address` — Adresse | adresse civique | 0 % | ∅ |
72 +| `city` — Ville | municipalité | 100 % | Gatineau |
73 +| `region` — Région | région administrative | 100 % | Outaouais |
74 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 73 % | 45.6229867, -72.9455913 |
75 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
76 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
77 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 100 % | 17:00 |
78 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
79 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
80 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
81 +| `price_min` — Prix min | tarif minimal réel ($) | 6 % | 0.0 |
82 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
83 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
84 +| `organizer` — Organisateur | promoteur / organisme | 100 % | Carrefour culturel Estacade - Salle Desjardins |
85 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://www.ticketacces.net/organisations/salledesjardins/evenements/petites/affiche-joe-cocker.… |
86 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
87 +
88 +## Fréquence & budgets
89 +
90 +- **Cadence** : horaire (watcher PM2)
91 +- **Budgets / plafonds (variables d'environnement)** :
92 +
93 +| Variable | Défaut | Rôle |
94 +|---|---|---|
95 +| `TA_DETAIL_MAX` | `25` | — |
96 +| `TA_TTL_HOURS` | `6` | — |
97 +- **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
98 +
99 +## Volumétrie & complétude live
100 +
101 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
102 +
103 +- **Événements en base** : 1320 (dont **1320 actifs**)
104 +- **GPS** : 959/1320 actifs (73 %)
105 +- **Prix** : 76/1320 actifs (6 %)
106 +- **Heure (start_time)** : 1320/1320 actifs (100 %)
107 +- **Heure de fin** : 0/1320 actifs (0 %)
108 +- **Statut (annulé/reporté/complet)** : 0/1320 actifs (0 %)
109 +- **Artistes** : 0/1320 actifs (0 %)
110 +- **Image** : 1319/1320 actifs (100 %)
111 +- **Salle (venue)** : 1320/1320 actifs (100 %)
112 +- **Dernier sync** : 2026-08-21 21:40 — fetched 1320, +1320 ajoutés, ~0 mis à jour, -0 retirés
113 +- **Syncs enregistrés** (sync_log) : 1
114 +
115 +## Erreurs & dépannage
116 +
117 +Aucune erreur dans `sync_log` pour cette source. ✅
118 +
119 +- Rejouer un sync isolé : `python3 run.py sync --source ticketacces` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='ticketacces' ORDER BY ts DESC LIMIT 5;"`
120 +
121 +## Licence / attribution
122 +
123 +- Billetterie publique, robots.txt sans interdiction sur le portail (vérifié 2026-08-21) ; GET throttlés 1 s, UA honnête, cookie de détection ColdFusion géré par session, lien d'achat vers la fiche officielle
124 +
125 +## Historique
126 +
127 +- **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
128 +- **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
129 +- **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
modified docs/connecteurs/ticketmaster.md +26 −26
@@ -1,6 +1,6 @@
1 1 # Ticketmaster — Discovery API (Québec) (`ticketmaster`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/ticketmaster.py`
6 6
@@ -46,22 +46,22 @@
46 46
47 47 ## Champs récupérés → schéma
48 48
49 Complétude mesurée sur les **357 événements actifs** de la source ; exemples tirés d'un événement réel (`ticketmaster:31006476DB368606`).
49 +Complétude mesurée sur les **360 événements actifs** de la source ; exemples tirés d'un événement réel (`ticketmaster:310064CAFB66B462`).
50 50
51 51 | Champ (`events.*`) | Description | Complétude | Exemple réel |
52 52 |---|---|---|---|
53 | `title` — Titre | titre de l'événement | 100 % | ANNULÉ - Rock This Town |
53 +| `title` — Titre | titre de l'événement | 100 % | BINGO BRUNCH - Hip Hop & RnB Bingo avec Tom Swoope |
54 54 | `description` — Description | texte descriptif (source ou fiche détail) | 0 % | ∅ |
55 | `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.ticketmaster.ca/annule-rock-this-town-quebec-quebec-09-13-2026/event/31006476DB36860… |
55 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://www.ticketmaster.ca/bingo-brunch-hip-hop-rnb-bingo-montreal-quebec-08-22-2026/event/3100… |
56 56 | `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["musique"] |
57 | `venue` — Salle (venue) | nom du lieu | 100 % | Théâtre Capitole |
58 | `address` — Adresse | adresse civique | 100 % | 972 Rue Saint-Jean |
59 | `city` — Ville | municipalité | 100 % | Québec |
60 | `region` — Région | région administrative | 100 % | Capitale-Nationale |
61 | `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 46.813254, -71.214215 |
62 | `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-09-13 |
63 | `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-09-13 |
64 | `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 96 % | 20:00 |
57 +| `venue` — Salle (venue) | nom du lieu | 100 % | Le Balcon |
58 +| `address` — Adresse | adresse civique | 100 % | 463 Sainte-Catherine Ouest |
59 +| `city` — Ville | municipalité | 100 % | Montréal |
60 +| `region` — Région | région administrative | 100 % | Montréal |
61 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 100 % | 45.506011, -73.567007 |
62 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
63 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
64 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 96 % | 11:00 |
65 65 | `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
66 66 | `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 4 % | cancelled |
67 67 | `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
@@ -69,7 +69,7 @@ Complétude mesurée sur les **357 événements actifs** de la source ; exemples
69 69 | `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
70 70 | `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
71 71 | `organizer` — Organisateur | promoteur / organisme | 0 % | ∅ |
72 | `image` — Image | visuel (og:image ou média source) | 91 % | https://mapsapi.tmol.io/maps/geometry/3/event/31006476DB368606/staticImage?type=png&systemId=HOS… |
72 +| `image` — Image | visuel (og:image ou média source) | 90 % | https://mapsapi.tmol.io/maps/geometry/3/event/310064CAFB66B462/staticImage?type=png&systemId=HOS… |
73 73 | `website` — Site web | site officiel de l'événement | 0 % | ∅ |
74 74
75 75 ## Fréquence & budgets
@@ -87,19 +87,19 @@ Complétude mesurée sur les **357 événements actifs** de la source ; exemples
87 87
88 88 ## Volumétrie & complétude live
89 89
90 *(mesuré le 2026-08-19 02:34 EDT dans `data/sortika.db`)*
91
92 - **Événements en base** : 380 (dont **357 actifs**)
93 - **GPS** : 357/357 actifs (100 %)
94 - **Prix** : 0/357 actifs (0 %)
95 - **Heure (start_time)** : 343/357 actifs (96 %)
96 - **Heure de fin** : 0/357 actifs (0 %)
97 - **Statut (annulé/reporté/complet)** : 14/357 actifs (4 %)
98 - **Artistes** : 0/357 actifs (0 %)
99 - **Image** : 325/357 actifs (91 %)
100 - **Salle (venue)** : 357/357 actifs (100 %)
101 - **Dernier sync** : 2026-08-19 01:33 — fetched 360, +0 ajoutés, ~0 mis à jour, -11 retirés
102 - **Syncs enregistrés** (sync_log) : 25
90 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
91 +
92 +- **Événements en base** : 394 (dont **360 actifs**)
93 +- **GPS** : 360/360 actifs (100 %)
94 +- **Prix** : 0/360 actifs (0 %)
95 +- **Heure (start_time)** : 345/360 actifs (96 %)
96 +- **Heure de fin** : 0/360 actifs (0 %)
97 +- **Statut (annulé/reporté/complet)** : 15/360 actifs (4 %)
98 +- **Artistes** : 0/360 actifs (0 %)
99 +- **Image** : 323/360 actifs (90 %)
100 +- **Salle (venue)** : 360/360 actifs (100 %)
101 +- **Dernier sync** : 2026-08-21 20:56 — fetched 360, +0 ajoutés, ~0 mis à jour, -0 retirés
102 +- **Syncs enregistrés** (sync_log) : 84
103 103
104 104 ## Erreurs & dépannage
105 105
modified docs/connecteurs/trois-rivieres.md +1 −1
@@ -1,6 +1,6 @@
1 1 # Ville de Trois-Rivières — Événements (`trois-rivieres`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 5 **Statut : abandonnee** · Type : `municipal` · Connecteur : *aucun (source sans connecteur runtime)*
6 6
modified docs/connecteurs/tuxedobillet.md +88 −13
@@ -1,46 +1,121 @@
1 # Tuxedo Billet — billetterie Capitale-Nationale (`tuxedobillet`)
1 +# Tuxedo Billet — billetterie québécoise multi-tenants (`tuxedobillet`)
2 2
3 > Fiche générée le 2026-08-19 02:34 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
3 +> Fiche générée le 2026-08-21 21:47 EDT par `scripts/gen_connector_docs.py` — ne pas éditer à la main, relancer le script.
4 4
5 **Statut : abandonnee** · Type : `billetterie` · Connecteur : *aucun (source sans connecteur runtime)*
5 +**Statut : actif** · Type : `billetterie` · Connecteur : `sortika/connectors/tuxedobillet.py`
6 6
7 7 ## Description
8 8
9 - **Source** : [Tuxedo Billet — billetterie Capitale-Nationale](https://www.tuxedobillet.com/)
10 - **Couverture** : Salles de Québec/Lévis (Grand Théâtre, Palais Montcalm…)
11 - **Extraction** : n/d
12 - **Note** : ÉCARTÉE 2026-08-19 (Phase 3) : site en PANNE aux sondes — www = page « failover site », apex = erreur TLS (curl 60). Le Grand Théâtre et le Capitole sont couverts via sallesjsonld en attendant. À re-sonder.
9 +- **Source** : [Tuxedo Billet — billetterie québécoise multi-tenants](https://www.tuxedobillet.com/)
10 +- **Couverture** : 29 salles/diffuseurs QC : Théâtre Outremont, Le Gesù, Centre Pierre-Péladeau, FTA, FIMAV, Espace Théâtre (Mont-Laurier), L'Anglicane (Lévis), Chandler, Lac-Mégantic, Îles-de-la-Madeleine…
11 +- **Extraction** : Acteur Apify maison gorgeous_thistle/ka-tuxedo (Playwright, proxy résidentiel CA, code versionné dans apify/ka-tuxedo/) : les tenants <org>.tuxedobillet.com sont des SPA Angular sur Firebase (RTDB verrouillée, 401 même avec jeton anonyme) → rendu navigateur obligatoire. 1 run/TUX_TTL_HOURS (20 h), 29 tenants QC configurés dans data/tuxedo_tenants.json (ville vérifiée). Liste sans heure ni prix → jamais inventés.
12 +- **Note** : RÉACTIVÉE 2026-08-21 (Phase 4) : l'apex reste vide mais les tenants vivent. Première source consommée via un acteur Apify (billetterie dure à crawler). Énumération des tenants non exhaustive — compléter via Serper/config.
13 13
14 14 ## Accès
15 15
16 16 - **Type** : billetterie
17 17 - **Endpoint** : voir URL source (https://www.tuxedobillet.com/)
18 18 - **Auth** : Aucune (accès public, identification honnête par User-Agent)
19 - **Accès légal** : n/d
19 +- **Pagination** : aucune (flux unique)
20 +- **Backend** : GET direct `requests` (throttlé, UA `SortiKa/1.0` identifié)
21 +- **Throttle** : 1.0 s entre requêtes
22 +- **Accès légal** : Billetteries publiques ; rendu navigateur modéré (1 page/tenant/run, ~1 run par jour), lien d'achat vers la fiche officielle du tenant
23 +
24 +<details><summary>En-tête documentaire du connecteur</summary>
25 +
26 +> connectors/tuxedobillet.py : Tuxedo Billet — billetterie québécoise
27 +> multi-tenants (Phase 4). Reprise de la source écartée en Phase 3 (site en
28 +> panne) : l'apex reste vide mais les tenants <org>.tuxedobillet.com vivent.
29 +> Source : SPA Angular sur Firebase par tenant — RTDB verrouillée (401
30 +> même avec jeton anonyme, vérifié 2026-08-21) → rendu
31 +> navigateur OBLIGATOIRE. L'extraction est déléguée à l'acteur
32 +> Apify maison gorgeous_thistle/ka-tuxedo (Playwright, proxy
33 +> résidentiel CA, code dans apify/ka-tuxedo/) qui rend chaque
34 +> tenant et retourne les tuiles spectacles : titre, sous-titre,
35 +> date FR, image, lien /main/<slug>.
36 +> Extraction: 1 run d'acteur par ≈ TUX_TTL_HOURS (20 h par défaut — le
37 +> rendu JS coûte des crédits Apify ; les programmations de
38 +> salles bougent lentement). Résultat mis en cache disque
39 +> (data/tuxedobillet_cache.json) et réémis entre deux runs.
40 +> Tenants configurés dans data/tuxedo_tenants.json (Québec
41 +> seulement, ville vérifiée). La liste ne publie ni heure ni
42 +> prix → jamais inventés (heure « non confirmée » côté front).
43 +> Accès : billetteries publiques ; rendu navigateur modéré (1 page par
44 +> tenant par run, ~1 run/jour), UA/robots gérés par Apify,
45 +> lien d'achat = la fiche officielle du tenant.
46 +> Secrets : APIFY_TOKEN requis (dans .env) — sans lui, le connecteur
47 +> réémet le cache et n'échoue que s'il n'y a aucun cache.
48 +
49 +</details>
20 50
21 51 ## Champs récupérés → schéma
22 52
23 *Aucun événement actif en base pour cette source — pas de schéma live à montrer.*
53 +Complétude mesurée sur les **252 événements actifs** de la source ; exemples tirés d'un événement réel (`tuxedobillet:centredesarts-le-duplex-260822`).
54 +
55 +| Champ (`events.*`) | Description | Complétude | Exemple réel |
56 +|---|---|---|---|
57 +| `title` — Titre | titre de l'événement | 100 % | Le duplex — Comédie - Théâtre d'été |
58 +| `description` — Description | texte descriptif (source ou fiche détail) | 0 % | ∅ |
59 +| `url` — URL | lien vers la fiche originale / billetterie | 100 % | https://centredesarts.tuxedobillet.com/main/le-duplex-260822 |
60 +| `categories` — Catégories | taxonomie canonique Sorti-Ka (JSON) | 100 % | ["autre"] |
61 +| `venue` — Salle (venue) | nom du lieu | 100 % | Centre des arts Juliette-Lassonde |
62 +| `address` — Adresse | adresse civique | 0 % | ∅ |
63 +| `city` — Ville | municipalité | 100 % | Saint-Hyacinthe |
64 +| `region` — Région | région administrative | 100 % | Montérégie |
65 +| `lat` — GPS (lat/lng) | coordonnées géographiques | 90 % | 45.6229339, -72.9455416 |
66 +| `start_date` — Date début | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
67 +| `end_date` — Date fin | date locale AAAA-MM-JJ | 100 % | 2026-08-22 |
68 +| `start_time` — Heure (start_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
69 +| `end_time` — Heure de fin (end_time) | heure locale HH:MM (Québec) | 0 % | ∅ |
70 +| `status` — Statut | cancelled / postponed / soldout (NULL = prévu) | 0 % | ∅ |
71 +| `is_free` — Gratuité (is_free) | 1 gratuit / 0 payant / NULL inconnu | 0 % | ∅ |
72 +| `price_min` — Prix min | tarif minimal réel ($) | 0 % | ∅ |
73 +| `price_label` — Libellé prix | étiquette tarifaire affichable | 0 % | ∅ |
74 +| `artists` — Artistes | têtes d'affiche / lineup (JSON) | 0 % | [] |
75 +| `organizer` — Organisateur | promoteur / organisme | 100 % | Centre des arts Juliette-Lassonde |
76 +| `image` — Image | visuel (og:image ou média source) | 100 % | https://tuxedov1.blob.core.windows.net/tuxedo-centredesarts/show/Le%20duplex_-OpJkp-kUHJSuv8wru8… |
77 +| `website` — Site web | site officiel de l'événement | 0 % | ∅ |
24 78
25 79 ## Fréquence & budgets
26 80
27 - **Cadence** : n/d
81 +- **Cadence** : quotidienne (TTL 20 h dans le watcher horaire)
82 +- **Budgets / plafonds (variables d'environnement)** :
83 +
84 +| Variable | Défaut | Rôle |
85 +|---|---|---|
86 +| `TUX_TTL_HOURS` | `20` | — |
87 +| `TUX_POLL_TIMEOUT` | `1500` | s |
88 +| `APIFY_TOKEN` | `—` | — |
28 89 - **Budget global** : géocodage Nominatim plafonné à `SORTIKA_GEOCODE_MAX` (défaut 120) requêtes réseau/cycle, toutes sources confondues (`sortika/geocode.py`, cache disque).
29 90
30 91 ## Volumétrie & complétude live
31 92
32 *Source sans connecteur runtime : aucune donnée en base.*
93 +*(mesuré le 2026-08-21 21:47 EDT dans `data/sortika.db`)*
94 +
95 +- **Événements en base** : 252 (dont **252 actifs**)
96 +- **GPS** : 226/252 actifs (90 %)
97 +- **Prix** : 0/252 actifs (0 %)
98 +- **Heure (start_time)** : 0/252 actifs (0 %)
99 +- **Heure de fin** : 0/252 actifs (0 %)
100 +- **Statut (annulé/reporté/complet)** : 0/252 actifs (0 %)
101 +- **Artistes** : 0/252 actifs (0 %)
102 +- **Image** : 252/252 actifs (100 %)
103 +- **Salle (venue)** : 252/252 actifs (100 %)
104 +- **Dernier sync** : 2026-08-21 21:44 — fetched 252, +252 ajoutés, ~0 mis à jour, -0 retirés
105 +- **Syncs enregistrés** (sync_log) : 1
33 106
34 107 ## Erreurs & dépannage
35 108
109 +Aucune erreur dans `sync_log` pour cette source. ✅
110 +
111 +- Rejouer un sync isolé : `python3 run.py sync --source tuxedobillet` (ou via le watcher PM2) ; inspecter ensuite `sync_log` : `sqlite3 data/sortika.db "SELECT * FROM sync_log WHERE source='tuxedobillet' ORDER BY ts DESC LIMIT 5;"`
36 112
37 113 ## Licence / attribution
38 114
39 - n/d
115 +- Billetteries publiques ; rendu navigateur modéré (1 page/tenant/run, ~1 run par jour), lien d'achat vers la fiche officielle du tenant
40 116
41 117 ## Historique
42 118
43 - **Source abandonnée** — ÉCARTÉE 2026-08-19 (Phase 3) : site en PANNE aux sondes — www = page « failover site », apex = erreur TLS (curl 60). Le Grand Théâtre et le Capitole sont couverts via sallesjsonld en attendant. À re-sonder.
44 119 - **2026-08-18 — Vague 1 d'enrichissement** (commit `7d9d1b0`) : géocodage Nominatim des salles (`sortika/geocode.py`, cache disque, plafond `SORTIKA_GEOCODE_MAX`), fiches détail lepointdevente (description, heure, tarifs réels `/plugins/rates/<id>`), contacts Tourinsoft (SITQ), heure locale Montréal via JSON-LD.
45 120 - **2026-08-18 — Vague 2 d'enrichissement** (commit `65d2d33`) : colonnes additives `events.start_time` (HH:MM locale QC) et `events.artists` (JSON) + migration auto ; heure captée partout où la source la publie (lepointdevente, evenko, Laval, Sherbrooke, Montréal, atuvu) ; artistes (headliners evenko, lineup Bandsintown, attractions Ticketmaster, patrons sûrs sur les titres) ; 3 nouveaux connecteurs (brossard, ticketmaster, bandsintown).
46 121 - **2026-08-19 — Phase 2 (travail de moine)** : colonnes additives `end_time`, `status` (annulé/reporté/complet) et `quarantine` (règles anti-aberrations, non publié mais réintégrable) ; règle d'ARCHIVAGE des événements terminés à chaque cycle (jamais supprimés) ; dedup_key inclut désormais l'heure (les séances multiples d'un même jour ne sont plus écrasées à l'affichage) ; alerte « 0 événement futur / chute > 80 % » par source (data/future_counts.json + /api/sources) ; heure et description La Vitrine ; end_time/organisateur/statut Eventbrite ; og:description Atuvu ; premières parties + portes + tournée evenko ; offres billets Bandsintown ; taxonomie type-event + description intégrale Brossard ; image Longueuil ; géocodage par ADRESSE CIVIQUE prioritaire ; villes normalisées au Répertoire MAMH (data/villes_canon.json) ; laval consignée « morte-a-la-source ».
added sortika/connectors/centreduquebec.py +108 −0
@@ -0,0 +1,108 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/centreduquebec.py : Tourisme Centre-du-Québec — événements
5 +# (ATR, Phase 4). Première source de la région Centre-du-Québec (0 fiche
6 +# avant connexion).
7 +# Source : https://tourismecentreduquebec.com/evenements/ — WordPress,
8 +# liste rendue serveur (~10 fiches à venir, vérifié 2026-08-21) :
9 +# chaque bloc events_listing_results_event = plage de dates FR
10 +# (« 1 septembre 2026 au 6 septembre 2026 ») + titre + lien.
11 +# Extraction: GET liste (1 requête) → fiches détail incrémentales (cache
12 +# data/centreduquebec_cache.json, CDQ_FICHE_MAX/sync, re-visite
13 +# 7 j) : bloc « Adresse » (adresse civique), og:image,
14 +# og:description. La fiche ne publie ni ville structurée, ni
15 +# heure, ni tarif → jamais inventés (région dérivée de l'ATR).
16 +# Accès : site public, robots.txt sans interdiction sur /evenements/ —
17 +# GET throttlés, UA honnête.
18 +# -----------------------------------------------------------------------------
19 +from __future__ import annotations
20 +
21 +import os
22 +import re
23 +import time
24 +
25 +from ..normalize import clean_text, parse_date_range_fr
26 +from ..schema import Event
27 +from .base import BaseConnector
28 +
29 +BASE = "https://tourismecentreduquebec.com"
30 +LIST_URL = BASE + "/evenements/"
31 +FICHE_MAX = int(os.environ.get("CDQ_FICHE_MAX", "10"))
32 +REFRESH_AFTER = 7 * 86400
33 +
34 +_BLOCK_RE = re.compile(
35 + r'events_listing_results_event_date">\s*<h4>\s*(?P<date>[^<]+?)\s*</h4>.*?'
36 + r'events_listing_results_event_name">\s*<h4>(?P<title>[^<]+)</h4>.*?'
37 + r'href="(?P<url>https://tourismecentreduquebec\.com/evenements/[^"]+)"',
38 + re.S)
39 +_ADDR_RE = re.compile(r'Adresse\s*</[^>]+>\s*<[^>]+>\s*([^<]{4,120})', re.S)
40 +_OG_IMG_RE = re.compile(r'property="og:image"\s+content="([^"]*)"')
41 +_OG_DESC_RE = re.compile(r'property="og:description"\s+content="([^"]*)"')
42 +
43 +
44 +class CentreDuQuebecConnector(BaseConnector):
45 + source_id = "centreduquebec"
46 + request_delay = 0.8
47 +
48 + def _parse_fiche(self, html: str) -> dict:
49 + # la fiche présente « Adresse » comme libellé suivi de l'adresse civique
50 + plain = re.sub(r"<[^>]+>", "|", html)
51 + addr = ""
52 + m = re.search(r"\|Adresse\|+\s*([^|]{4,120})", plain)
53 + if m:
54 + addr = clean_text(m.group(1))
55 + img = _OG_IMG_RE.search(html)
56 + desc = _OG_DESC_RE.search(html)
57 + return {
58 + "address": addr,
59 + "image": clean_text(img.group(1)) if img else "",
60 + "description": clean_text(desc.group(1)) if desc else "",
61 + }
62 +
63 + def fetch(self) -> list[Event]:
64 + html = self.get(LIST_URL).text
65 + rows: list[dict] = []
66 + for m in _BLOCK_RE.finditer(html):
67 + url = m.group("url").rstrip("/") + "/"
68 + start, end = parse_date_range_fr(m.group("date"))
69 + rows.append({
70 + "external_id": url.rstrip("/").split("/")[-1],
71 + "url": url,
72 + "title": clean_text(m.group("title")),
73 + "start_date": start, "end_date": end,
74 + })
75 +
76 + cache = self.load_cache()
77 + now = time.time()
78 + current = {r["external_id"] for r in rows}
79 + cache = {k: v for k, v in cache.items() if k in current}
80 + to_fetch = sorted(
81 + (r for r in rows
82 + if r["external_id"] not in cache
83 + or now - cache[r["external_id"]].get("ts", 0) > REFRESH_AFTER),
84 + key=lambda r: cache.get(r["external_id"], {}).get("ts", 0))[:FICHE_MAX]
85 + for r in to_fetch:
86 + try:
87 + detail = self._parse_fiche(self.get(r["url"]).text)
88 + except Exception:
89 + detail = {}
90 + cache[r["external_id"]] = {"ts": now, "detail": detail}
91 + self.save_cache(cache)
92 +
93 + events: list[Event] = []
94 + for r in rows:
95 + detail = cache.get(r["external_id"], {}).get("detail") or {}
96 + events.append(Event(
97 + source=self.source_id,
98 + external_id=r["external_id"],
99 + url=r["url"],
100 + title=r["title"],
101 + description=detail.get("description", ""),
102 + address=detail.get("address", ""),
103 + tourist_region="Centre-du-Québec",
104 + start_date=r["start_date"],
105 + end_date=r["end_date"],
106 + image=detail.get("image", ""),
107 + ))
108 + return events
added sortika/connectors/chaudiereappalaches.py +143 −0
@@ -0,0 +1,143 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/chaudiereappalaches.py : Tourisme Chaudière-Appalaches —
5 +# festivals et événements (ATR, Phase 4). Région faiblement couverte
6 +# avant connexion (essentiellement SITQ).
7 +# Source : https://chaudiereappalaches.com/planifier-votre-sejour/
8 +# ete-automne/festivals-et-evenements/ — CMS Woody, liste rendue
9 +# serveur paginée (?listpage=1..3, ~12 fiches/page, vérifié
10 +# 2026-08-21). Chaque fiche embarque un bloc JSON-LD Event
11 +# COMPLET : dates ISO, description, adresse civique, ville,
12 +# code postal, GPS, images.
13 +# Extraction: pages liste (1 GET/page) → fiches détail incrémentales (cache
14 +# data/chaudiereappalaches_cache.json, CHA_FICHE_MAX/sync,
15 +# re-visite 7 j) → JSON-LD Event (parseur générique ld_events
16 +# de sallesjsonld). Pas d'heure ni de tarif structurés fiables
17 +# → jamais inventés.
18 +# Accès : site public de l'ATR, robots.txt sans interdiction sur
19 +# /planifier-votre-sejour/ — GET throttlés, UA honnête.
20 +# -----------------------------------------------------------------------------
21 +from __future__ import annotations
22 +
23 +import os
24 +import re
25 +import time
26 +
27 +from ..normalize import clean_text, parse_date_iso
28 +from ..schema import Event
29 +from .base import BaseConnector
30 +from .sallesjsonld import ld_events
31 +
32 +BASE = "https://chaudiereappalaches.com"
33 +LIST_URL = BASE + "/planifier-votre-sejour/ete-automne/festivals-et-evenements/"
34 +PAGE_MAX = 6 # 3 pages constatées en 2026-08
35 +FICHE_MAX = int(os.environ.get("CHA_FICHE_MAX", "12"))
36 +REFRESH_AFTER = 7 * 86400
37 +
38 +_FICHE_RE = re.compile(
39 + r'href="(https://chaudiereappalaches\.com/planifier-votre-sejour/[^"]*'
40 + r'/festivals-et-evenements/[a-z0-9-]+-fr-(\d+)/)"')
41 +_LISTPAGE_RE = re.compile(r"listpage=(\d+)")
42 +
43 +
44 +class ChaudiereAppalachesConnector(BaseConnector):
45 + source_id = "chaudiereappalaches"
46 + request_delay = 0.8
47 +
48 + def _list_fiches(self) -> dict[str, str]:
49 + """{url de fiche: id numérique} sur toutes les pages de la liste."""
50 + fiches: dict[str, str] = {}
51 + first = self.get(LIST_URL).text
52 + pages = {int(p) for p in _LISTPAGE_RE.findall(first) if p.isdigit()}
53 + last = min(max(pages or {1}), PAGE_MAX)
54 + for url, fid in _FICHE_RE.findall(first):
55 + fiches[url] = fid
56 + for page in range(2, last + 1):
57 + try:
58 + html = self.get(LIST_URL, params={"listpage": page}).text
59 + except Exception:
60 + break
61 + for url, fid in _FICHE_RE.findall(html):
62 + fiches[url] = fid
63 + return fiches
64 +
65 + def _parse_fiche(self, url: str, fid: str, html: str) -> dict | None:
66 + for ld in ld_events(html):
67 + title = clean_text(str(ld.get("name") or ""))
68 + start = parse_date_iso(str(ld.get("startDate") or ""))
69 + if not title or not start:
70 + continue
71 + loc = ld.get("location")
72 + loc = (loc[0] if isinstance(loc, list) and loc else loc) or {}
73 + loc = loc if isinstance(loc, dict) else {}
74 + addr = loc.get("address")
75 + addr = addr if isinstance(addr, dict) else {}
76 + geo = loc.get("geo")
77 + geo = geo if isinstance(geo, dict) else {}
78 + image = ld.get("image")
79 + if isinstance(image, list):
80 + image = image[0] if image else ""
81 + venue = clean_text(str(loc.get("name") or ""))
82 + if venue.lower() in ("adresse", title.lower()):
83 + venue = "" # libellé générique du CMS
84 + return {
85 + "external_id": fid,
86 + "url": url,
87 + "title": title,
88 + "description": clean_text(str(ld.get("description") or "")),
89 + "venue": venue,
90 + "address": clean_text(str(addr.get("streetAddress") or "")),
91 + "city": clean_text(str(addr.get("addressLocality") or "")),
92 + "postal_code": str(addr.get("postalCode") or "").strip(),
93 + "lat": geo.get("latitude"), "lng": geo.get("longitude"),
94 + "start_date": start,
95 + "end_date": parse_date_iso(str(ld.get("endDate") or "")) or start,
96 + "image": image if isinstance(image, str) else "",
97 + }
98 + return None
99 +
100 + def fetch(self) -> list[Event]:
101 + fiches = self._list_fiches()
102 + cache = self.load_cache()
103 + now = time.time()
104 + current = set(fiches.values())
105 + cache = {k: v for k, v in cache.items() if k in current}
106 + to_fetch = sorted(
107 + (u for u, fid in fiches.items()
108 + if fid not in cache or now - cache[fid].get("ts", 0) > REFRESH_AFTER),
109 + key=lambda u: cache.get(fiches[u], {}).get("ts", 0))[:FICHE_MAX]
110 + for url in to_fetch:
111 + fid = fiches[url]
112 + try:
113 + row = self._parse_fiche(url, fid, self.get(url).text)
114 + except Exception:
115 + row = None
116 + if row is None:
117 + print(f"[sorti-ka] chaudiereappalaches : fiche ignorée {url}")
118 + cache[fid] = {"ts": now, "row": row}
119 + self.save_cache(cache)
120 +
121 + events: list[Event] = []
122 + for entry in cache.values():
123 + r = entry.get("row")
124 + if not r:
125 + continue
126 + events.append(Event(
127 + source=self.source_id,
128 + external_id=r["external_id"],
129 + url=r["url"],
130 + title=r["title"],
131 + description=r["description"],
132 + raw_categories=[r["title"]],
133 + venue=r["venue"],
134 + address=r["address"],
135 + city=r["city"],
136 + postal_code=r["postal_code"],
137 + tourist_region="Chaudière-Appalaches",
138 + lat=r["lat"], lng=r["lng"],
139 + start_date=r["start_date"],
140 + end_date=r["end_date"],
141 + image=r["image"],
142 + ))
143 + return events
added sortika/connectors/gaspesie.py +130 −0
@@ -0,0 +1,130 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/gaspesie.py : Tourisme Gaspésie — festivals et événements (ATR,
5 +# Phase 4). Région parmi les plus faibles au baseline (Phase 3 : candidate
6 +# prioritaire consignée pour une vague Scrapfly) — la voici.
7 +# Source : https://www.tourisme-gaspesie.com/fr/festival-et-evenements/
8 +# (CMS Umbraco). Le site est derrière un WAF : 403 sur GET
9 +# identifié (revérifié 2026-08-21) → extraction via SCRAPFLY
10 +# (asp, country=ca, sans render_js — le HTML est rendu serveur),
11 +# usage consigné et budgeté (GASP_FICHE_MAX/sync).
12 +# Extraction: liste (1 crédit) → fiches détail incrémentales (cache
13 +# data/gaspesie_cache.json, GASP_FICHE_MAX/sync, re-visite 14 j
14 +# — festivals annuels stables) : titre <h1>, plage de dates FR
15 +# (« 18 au 21 juin 2026 »), ville (« Carleton-sur-Mer, Québec »),
16 +# og:description, og:image. Pas d'heure ni de tarif structurés
17 +# → jamais inventés.
18 +# Accès : contenu public de l'ATR ; anti-bot contourné avec modération
19 +# (≤ 1 + GASP_FICHE_MAX crédits Scrapfly/sync, re-visite 14 j),
20 +# attribution et lien vers la fiche officielle conservés.
21 +# -----------------------------------------------------------------------------
22 +from __future__ import annotations
23 +
24 +import os
25 +import re
26 +import time
27 +
28 +from ..normalize import clean_text, parse_date_range_fr
29 +from ..schema import Event
30 +from .base import BaseConnector
31 +
32 +BASE = "https://www.tourisme-gaspesie.com"
33 +LIST_URL = BASE + "/fr/festival-et-evenements/"
34 +FICHE_MAX = int(os.environ.get("GASP_FICHE_MAX", "8"))
35 +REFRESH_AFTER = 14 * 86400 # festivals annuels : 2 semaines
36 +
37 +_FICHE_RE = re.compile(r'href="(/fr/festival-et-evenements/[a-z0-9-]+/)"')
38 +_TITLE_RE = re.compile(r"<h1[^>]*>\s*([^<]+?)\s*</h1>")
39 +_DATE_RE = re.compile(
40 + r'font-extrabold[^>]*>\s*((?:[Dd]u\s+)?\d{1,2}(?:er)?\s+[^<]*?\d{4})\s*<')
41 +# bloc coordonnées : « <p>775, route Flavie-Drapeau<br>Sainte-Flavie,
42 +# Québec G0J 2L0</p> » ou « <p>Carleton-sur-Mer, Québec</p> » — le « é » est
43 +# publié en entité HTML (&#xE9;) par le CMS
44 +_ADDR_CITY_RE = re.compile(
45 + r"<p>\s*(?:(?P<addr>[^<>]{3,80}?)\s*<br[^>]*>\s*)?"
46 + r"(?P<city>[^<>]{2,60}?),\s*Qu(?:&#xE9;|&eacute;|é)bec"
47 + r"(?:\s*(?P<pc>[A-Z]\d[A-Z]\s?\d[A-Z]\d))?", re.S)
48 +_OG_IMG_RE = re.compile(r'property="og:image"\s+content="([^"]*)"')
49 +_OG_DESC_RE = re.compile(r'property="og:description"\s+content="([^"]*)"')
50 +
51 +
52 +class GaspesieConnector(BaseConnector):
53 + source_id = "gaspesie"
54 + request_delay = 1.5 # crédits Scrapfly : modération
55 +
56 + def _get_waf(self, url: str) -> str:
57 + """GET via Scrapfly (WAF) — HTML rendu serveur, pas de render_js."""
58 + return self.get_rendered(url, render_js=False)
59 +
60 + def _parse_fiche(self, url: str, html: str) -> dict | None:
61 + title = _TITLE_RE.search(html)
62 + if not title:
63 + return None
64 + date_raw = _DATE_RE.search(html)
65 + raw = date_raw.group(1) if date_raw else ""
66 + # « 18 au 21 juin 2026 » sans « du » initial → on le préfixe pour que
67 + # parse_date_range_fr retrouve le jour de début (jamais inventé)
68 + if re.match(r"^\d", raw or ""):
69 + raw = "du " + raw
70 + start, end = parse_date_range_fr(raw)
71 + coord = _ADDR_CITY_RE.search(html)
72 + img = _OG_IMG_RE.search(html)
73 + desc = _OG_DESC_RE.search(html)
74 + return {
75 + "external_id": url.rstrip("/").split("/")[-1],
76 + "url": BASE + url,
77 + "title": clean_text(title.group(1)),
78 + "description": clean_text(desc.group(1)) if desc else "",
79 + "address": clean_text(coord.group("addr") or "") if coord else "",
80 + "city": clean_text(coord.group("city")) if coord else "",
81 + "postal_code": clean_text(coord.group("pc") or "") if coord else "",
82 + "start_date": start, "end_date": end,
83 + "image": clean_text(img.group(1)) if img else "",
84 + }
85 +
86 + def fetch(self) -> list[Event]:
87 + html = self._get_waf(LIST_URL)
88 + urls = sorted({u for u in _FICHE_RE.findall(html)
89 + if u.rstrip("/") != "/fr/festival-et-evenements"})
90 + cache = self.load_cache()
91 + now = time.time()
92 + current = set(urls)
93 + cache = {u: c for u, c in cache.items() if u in current}
94 + to_fetch = sorted(
95 + (u for u in urls
96 + if u not in cache or now - cache[u].get("ts", 0) > REFRESH_AFTER),
97 + key=lambda u: cache.get(u, {}).get("ts", 0))[:FICHE_MAX]
98 + for url in to_fetch: # séquentiel : budget crédits
99 + try:
100 + row = self._parse_fiche(url, self._get_waf(BASE + url))
101 + except Exception:
102 + row = None
103 + if row is None:
104 + print(f"[sorti-ka] gaspesie : fiche ignorée {url}")
105 + cache[url] = {"ts": now, "row": row}
106 + self.save_cache(cache)
107 +
108 + events: list[Event] = []
109 + seen: set[str] = set()
110 + for entry in cache.values():
111 + r = entry.get("row")
112 + if not r or r["external_id"] in seen or not r["start_date"]:
113 + continue
114 + seen.add(r["external_id"])
115 + events.append(Event(
116 + source=self.source_id,
117 + external_id=r["external_id"],
118 + url=r["url"],
119 + title=r["title"],
120 + description=r.get("description", ""),
121 + raw_categories=[r["title"]],
122 + address=r.get("address", ""),
123 + city=r["city"],
124 + postal_code=r.get("postal_code", ""),
125 + tourist_region="Gaspésie",
126 + start_date=r["start_date"],
127 + end_date=r["end_date"],
128 + image=r["image"],
129 + ))
130 + return events
added sortika/connectors/monteregie.py +139 −0
@@ -0,0 +1,139 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/monteregie.py : Tourisme Montérégie — événements (ATR, Phase 4)
5 +# Source : https://www.tourisme-monteregie.qc.ca — WordPress dont l'API
6 +# REST publique expose le CPT `cpt_event` (~78 fiches à venir,
7 +# vérifié 2026-08-21). Gisement Tourinsoft (acf.syndic_object_id
8 +# EVENMTOV… — même famille que le SIT Québec ; recoupement géré
9 +# par dedup_key, mais l'ATR ajoute GPS, image, site web et
10 +# description que le flux SITQ ne publie pas).
11 +# Extraction: API JSON /wp-json/wp/v2/cpt_event (paginée per_page=100) →
12 +# titre, description, plage de dates FR (« Du 18 septembre au
13 +# 20 septembre 2026 »), lat/long, site web, image. Fiches détail
14 +# incrémentales (cache data/monteregie_cache.json,
15 +# MTG_FICHE_MAX/sync, re-visite 7 j) : bloc JSON-LD Event →
16 +# adresse civique, ville, code postal. Pas d'heure ni de tarif
17 +# structurés → jamais inventés.
18 +# Accès : API REST WordPress publique (aucune auth), robots.txt sans
19 +# interdiction sur /wp-json/ — GET throttlés, UA honnête.
20 +# -----------------------------------------------------------------------------
21 +from __future__ import annotations
22 +
23 +import json
24 +import os
25 +import re
26 +import time
27 +
28 +from ..normalize import clean_text, parse_date_range_fr
29 +from ..schema import Event
30 +from .base import BaseConnector
31 +
32 +BASE = "https://www.tourisme-monteregie.qc.ca"
33 +API = BASE + "/wp-json/wp/v2/cpt_event"
34 +PER_PAGE = 100
35 +PAGE_MAX = 5 # garde-fou (78 fiches en 2026-08)
36 +FICHE_MAX = int(os.environ.get("MTG_FICHE_MAX", "15"))
37 +REFRESH_AFTER = 7 * 86400
38 +
39 +_LDJSON_RE = re.compile(
40 + r'<script type="application/ld\+json"[^>]*>(.*?)</script>', re.S)
41 +
42 +
43 +class MonteregieConnector(BaseConnector):
44 + source_id = "monteregie"
45 + request_delay = 0.8
46 +
47 + def _list_events(self) -> list[dict]:
48 + rows: list[dict] = []
49 + for page in range(1, PAGE_MAX + 1):
50 + batch = self.get_json(API, params={
51 + "per_page": PER_PAGE, "page": page})
52 + if not isinstance(batch, list) or not batch:
53 + break
54 + rows.extend(batch)
55 + if len(batch) < PER_PAGE:
56 + break
57 + return rows
58 +
59 + def _parse_fiche(self, html: str) -> dict:
60 + """Adresse/ville/salle du bloc JSON-LD Event de la fiche (thème ATR)."""
61 + for m in _LDJSON_RE.findall(html):
62 + try:
63 + data = json.loads(m)
64 + except ValueError:
65 + continue
66 + items = data if isinstance(data, list) else [data]
67 + for item in items:
68 + if not isinstance(item, dict) or item.get("@type") != "Event":
69 + continue
70 + loc = item.get("location") or {}
71 + addr = loc.get("address") or {}
72 + venue = clean_text(loc.get("name", ""))
73 + # le thème recopie le nom de l'événement comme nom de lieu —
74 + # on ne garde le lieu que s'il diffère du titre (jamais inventé)
75 + if venue and venue.lower() == clean_text(
76 + item.get("name", "")).lower():
77 + venue = ""
78 + return {
79 + "venue": venue,
80 + "address": clean_text(addr.get("streetAddress", "")),
81 + "city": clean_text(addr.get("addressLocality", "")),
82 + "postal_code": clean_text(addr.get("postalCode", "")),
83 + }
84 + return {}
85 +
86 + def fetch(self) -> list[Event]:
87 + rows = self._list_events()
88 + cache = self.load_cache()
89 + now = time.time()
90 + current = {str(r.get("id")) for r in rows}
91 + cache = {k: v for k, v in cache.items() if k in current}
92 + to_fetch = sorted(
93 + (r for r in rows
94 + if str(r.get("id")) not in cache
95 + or now - cache[str(r.get("id"))].get("ts", 0) > REFRESH_AFTER),
96 + key=lambda r: cache.get(str(r.get("id")), {}).get("ts", 0))[:FICHE_MAX]
97 + for row in to_fetch:
98 + eid, link = str(row.get("id")), row.get("link", "")
99 + detail: dict = {}
100 + try:
101 + if link:
102 + detail = self._parse_fiche(self.get(link).text)
103 + except Exception:
104 + detail = {}
105 + cache[eid] = {"ts": now, "detail": detail}
106 + self.save_cache(cache)
107 +
108 + events: list[Event] = []
109 + for row in rows:
110 + eid = str(row.get("id") or "")
111 + acf = row.get("acf") or {}
112 + if not eid or not isinstance(acf, dict):
113 + continue
114 + start, end = parse_date_range_fr(acf.get("date", ""))
115 + images = acf.get("images") or []
116 + image = ""
117 + if isinstance(images, list) and images and isinstance(images[0], dict):
118 + image = images[0].get("url", "")
119 + detail = cache.get(eid, {}).get("detail") or {}
120 + events.append(Event(
121 + source=self.source_id,
122 + external_id=eid,
123 + url=row.get("link", ""),
124 + title=clean_text((row.get("title") or {}).get("rendered", "")),
125 + description=clean_text(
126 + (row.get("content") or {}).get("rendered", "")),
127 + venue=detail.get("venue", ""),
128 + address=detail.get("address", ""),
129 + city=detail.get("city", ""),
130 + postal_code=detail.get("postal_code", ""),
131 + tourist_region="Montérégie",
132 + lat=acf.get("lat") or None,
133 + lng=acf.get("long") or None,
134 + start_date=start,
135 + end_date=end,
136 + website=acf.get("website", "") or "",
137 + image=image,
138 + ))
139 + return events
added sortika/connectors/ovation.py +189 −0
@@ -0,0 +1,189 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/ovation.py : Réseau Ovation — billetterie 100 % québécoise
5 +# (Phase 4). ~26 organisations (salles, diffuseurs municipaux, clubs
6 +# sportifs) : Salle Albert-Rousseau, Petit Champlain, Diffusion Saguenay,
7 +# Gatineau, Terrebonne, Granby, Magog, La Tuque… (sondé 2026-08-21).
8 +# Source : https://m.ovation.ca (sous-site mobile : HTML rendu serveur
9 +# propre, AUCUN anti-bot — le desktop reseau.ovation.ca exige
10 +# des cookies de session). Organisations configurées dans
11 +# data/ovation_orgs.json (ajouter une org = 1 bloc, 0 code ;
12 +# codes validés par balayage base36).
13 +# Extraction: par org, /fr/Search/AllEvents paginé (?pageNumber=N, 40
14 +# cartes/page) → eventIds ; chaque eventId = UNE représentation
15 +# datée. Fiches fragment /fr/Event/EventDescription?eventId=…
16 +# (≈ 5 Ko) incrémentales (cache data/ovation_cache.json,
17 +# OVA_FICHE_MAX/sync, re-visite 7 j) : date pleine FR
18 +# (« mercredi 26 août 2026, 20h00 »), catégories, artistes
19 +# (EventsByPerformer), description, salle + adresse + ville +
20 +# code postal. Prix non exposé par le sous-site mobile →
21 +# jamais inventé.
22 +# Accès : billetterie publique, HTML public sans auth. robots.txt
23 +# (lu 2026-08-21) : User-agent * → Crawl-delay: 30 +
24 +# Disallow /event/tickets (jamais requêté) → GET espacés de
25 +# 30,5 s sur m.ovation.ca (hôte unique). Pour rester poli, le
26 +# PASSAGE RÉSEAU complet (listes + fiches) n'a lieu qu'à
27 +# l'expiration d'un TTL (OVA_TTL_HOURS, 20 h — ≈ 45 min de
28 +# moisson/jour) ; entre deux passages, le connecteur réémet son
29 +# cache. Lien d'achat = la fiche officielle.
30 +# -----------------------------------------------------------------------------
31 +from __future__ import annotations
32 +
33 +import json
34 +import os
35 +import re
36 +import time
37 +from pathlib import Path
38 +
39 +from ..normalize import clean_text, parse_date_fr, parse_time
40 +from ..schema import Event
41 +from .base import BaseConnector
42 +
43 +BASE = "https://m.ovation.ca"
44 +CONFIG_PATH = Path(__file__).resolve().parents[2] / "data" / "ovation_orgs.json"
45 +PAGE_MAX = int(os.environ.get("OVA_PAGE_MAX", "6"))
46 +FICHE_MAX = int(os.environ.get("OVA_FICHE_MAX", "50"))
47 +REFRESH_AFTER = 7 * 86400
48 +TTL = float(os.environ.get("OVA_TTL_HOURS", "20")) * 3600
49 +
50 +_CARD_RE = re.compile(r'data-event-id="([0-9A-Z]+)"')
51 +_TITLE_RE = re.compile(
52 + r'<div class="title">\s*([^<]+?)\s*'
53 + r'(?:<span class="event-additional-name">\s*\|?\s*([^<]+?)\s*</span>)?\s*</div>',
54 + re.S)
55 +_DATE_RE = re.compile(r'class="info-date">\s*([^<]+)')
56 +_CAT_RE = re.compile(r'EventsInCategory/[^"]*">\s*([^<]+?)\s*</a>')
57 +_PERF_RE = re.compile(r'EventsByPerformer/[^"]*">\s*([^<]+?)\s*</a>')
58 +_VENUE_RE = re.compile(r'EventsByVenue/[^"]*">\s*([^<]+?)\s*</a>')
59 +_ADDR_RE = re.compile(
60 + r"EventsByVenue/[^>]*>[^<]+</a>\s*</div>\s*"
61 + r"([^<]{3,90}?),\s*<a href=[^>]*EventsByCity[^>]*>([^<]+)</a>"
62 + r"[^<]*?([A-Z]\d[A-Z]\s?\d[A-Z]\d)?\s*,\s*Canada", re.S)
63 +_IMG_RE = re.compile(r'<img src="(/product-catalog-images/[^"]+)"')
64 +_DESC_RE = re.compile(r'event-presentation.*?<p>(.*?)</p>', re.S)
65 +
66 +
67 +class OvationConnector(BaseConnector):
68 + source_id = "ovation"
69 + request_delay = 30.5 # robots.txt : Crawl-delay: 30 (hôte unique)
70 +
71 + def _load_orgs(self) -> list[dict]:
72 + try:
73 + return json.loads(
74 + CONFIG_PATH.read_text(encoding="utf-8"))["organisations"]
75 + except Exception:
76 + return []
77 +
78 + def _get_utf8(self, url: str) -> str:
79 + """GET décodé UTF-8 (les fragments mêlent entités HTML et UTF-8 brut)."""
80 + return self.get(url).content.decode("utf-8", errors="replace")
81 +
82 + def _event_ids(self, code: str) -> list[str]:
83 + """Tous les eventIds publiés par une organisation (pages AllEvents)."""
84 + ids: list[str] = []
85 + for page in range(1, PAGE_MAX + 1):
86 + url = f"{BASE}/{code}/fr/Search/AllEvents"
87 + if page > 1:
88 + url += f"?pageNumber={page}" # ?page= est ignoré (vérifié)
89 + try:
90 + html = self._get_utf8(url)
91 + except Exception:
92 + break
93 + batch = [i for i in _CARD_RE.findall(html) if i not in ids]
94 + if not batch:
95 + break
96 + ids.extend(batch)
97 + return ids
98 +
99 + def _parse_fiche(self, org: dict, event_id: str, html: str) -> dict | None:
100 + title = _TITLE_RE.search(html)
101 + date_raw = _DATE_RE.search(html)
102 + if not title or not date_raw:
103 + return None
104 + date_iso = parse_date_fr(date_raw.group(1))
105 + if not date_iso:
106 + return None
107 + name = clean_text(title.group(1))
108 + if title.group(2):
109 + name = f"{name}{clean_text(title.group(2))}"
110 + addr = _ADDR_RE.search(html)
111 + venue = _VENUE_RE.search(html)
112 + img = _IMG_RE.search(html)
113 + desc = _DESC_RE.search(html)
114 + return {
115 + "external_id": f"{org['code']}-{event_id}",
116 + "url": f"{BASE}/{org['code']}/fr/Event/?eventId={event_id}",
117 + "title": name,
118 + "description": clean_text(desc.group(1)) if desc else "",
119 + "raw_categories": [clean_text(c) for c in _CAT_RE.findall(html)],
120 + "artists": [clean_text(a) for a in _PERF_RE.findall(html)],
121 + "venue": clean_text(venue.group(1)) if venue else "",
122 + "address": clean_text(addr.group(1)) if addr else "",
123 + "city": clean_text(addr.group(2)) if addr else org.get("ville", ""),
124 + "postal_code": clean_text(addr.group(3) or "") if addr else "",
125 + "start_date": date_iso,
126 + "start_time": parse_time(date_raw.group(1)),
127 + "organizer": org.get("nom", ""),
128 + "image": BASE + img.group(1) if img else "",
129 + }
130 +
131 + def fetch(self) -> list[Event]:
132 + cache = self.load_cache()
133 + now = time.time()
134 + meta = cache.get("_meta") or {}
135 + if now - meta.get("ts", 0) <= TTL:
136 + return self._emit(cache) # cache frais : aucun réseau
137 + budget = FICHE_MAX
138 + current: set[str] = {"_meta"}
139 + for org in self._load_orgs():
140 + code = org["code"]
141 + ids = self._event_ids(code)
142 + keys = {f"{code}-{i}": i for i in ids}
143 + current |= set(keys)
144 + to_fetch = sorted(
145 + (k for k in keys
146 + if k not in cache or now - cache[k].get("ts", 0) > REFRESH_AFTER),
147 + key=lambda k: cache.get(k, {}).get("ts", 0))[:max(0, budget)]
148 + budget -= len(to_fetch)
149 + for k in to_fetch:
150 + url = (f"{BASE}/{code}/fr/Event/EventDescription"
151 + f"?eventId={keys[k]}")
152 + try:
153 + row = self._parse_fiche(org, keys[k], self._get_utf8(url))
154 + except Exception:
155 + row = None
156 + if row is None:
157 + print(f"[sorti-ka] ovation : fiche ignorée {url}")
158 + cache[k] = {"ts": now, "row": row}
159 + cache = {k: v for k, v in cache.items() if k in current}
160 + cache["_meta"] = {"ts": now}
161 + self.save_cache(cache)
162 + return self._emit(cache)
163 +
164 + def _emit(self, cache: dict) -> list[Event]:
165 + events: list[Event] = []
166 + for key, entry in cache.items():
167 + if key == "_meta":
168 + continue
169 + r = entry.get("row")
170 + if not r:
171 + continue
172 + events.append(Event(
173 + source=self.source_id,
174 + external_id=r["external_id"],
175 + url=r["url"],
176 + title=r["title"],
177 + description=r["description"],
178 + raw_categories=r["raw_categories"] or [r["title"]],
179 + artists=r["artists"],
180 + venue=r["venue"],
181 + address=r["address"],
182 + city=r["city"],
183 + postal_code=r["postal_code"],
184 + start_date=r["start_date"],
185 + start_time=r["start_time"],
186 + organizer=r["organizer"],
187 + image=r["image"],
188 + ))
189 + return events
added sortika/connectors/ticketacces.py +234 −0
@@ -0,0 +1,234 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/ticketacces.py : TicketAcces.net — billetterie régionale
5 +# québécoise (Phase 4). Gros levier régional : 47 organisations au Québec
6 +# dont l'Abitibi-Témiscamingue (14 !), le Bas-Saint-Laurent, la Côte-Nord,
7 +# Charlevoix, la Gaspésie… (sondé 2026-08-21).
8 +# Source : https://www.ticketacces.net (ColdFusion, encodage cp1252).
9 +# L'annuaire COMPLET des organisations est dans le portail
10 +# (cartes data-nom/data-ville/data-region/data-domaine) ; le
11 +# calendrier AGRÉGÉ multi-organisations expose chaque
12 +# représentation datée en 4 requêtes (catégories 1=Spectacles,
13 +# 2=Cinéma, 3=Sport, 4=Autre).
14 +# Extraction: portail (1 GET, annuaire + filtre Québec — le réseau compte
15 +# 12 orgs au Nouveau-Brunswick et 1 en Ontario, EXCLUES) →
16 +# calendrier par catégorie (4 GET) : titre + sous-titre, date
17 +# et heure FR (« Samedi 22 août 2026 à 12 h 30 »), « Salle,
18 +# Ville », image, lien fiche. Fiches détail incrémentales
19 +# (cache data/ticketacces_cache.json, TA_DETAIL_MAX/sync,
20 +# re-visite 7 j) : og:description, adresse civique, prix
21 +# plancher si les ventes sont ouvertes. 1 Event par
22 +# représentation (une même fiche multi-dates = plusieurs
23 +# séances, patron lepointdevente).
24 +# Accès : billetterie publique. robots.txt (lu 2026-08-21, identique
25 +# sur tous les hôtes *.ticketacces.net) : Crawl-delay: 120 +
26 +# Disallow /achat/ et representations/prix.cfm (jamais
27 +# requêtés) → GET espacés de 120 s PAR HÔTE (www porte le
28 +# portail + les 4 calendriers = 5 GET ≈ 8 min ; les fiches
29 +# vivent sur ~47 sous-domaines distincts, ≤ 1 fiche par hôte
30 +# par passage). Le passage réseau complet n'a lieu qu'à
31 +# l'expiration d'un TTL (TA_TTL_HOURS, 6 h) ; entre deux
32 +# passages, le connecteur réémet son cache. Cookie de détection
33 +# ColdFusion géré par la session (302 suivi). Lien d'achat =
34 +# la fiche officielle chez la source.
35 +# -----------------------------------------------------------------------------
36 +from __future__ import annotations
37 +
38 +import os
39 +import re
40 +import time
41 +
42 +from ..normalize import clean_text, parse_date_fr, parse_time
43 +from ..schema import Event
44 +from .base import BaseConnector
45 +
46 +PORTAL = "https://www.ticketacces.net/fr/portail/index.cfm"
47 +CALENDAR = ("https://www.ticketacces.net/fr/portail/evenements/calendrier.cfm"
48 + "?RepresentationCategoriePrincipaleID={cat}")
49 +CATEGORIES = {1: "Spectacles", 2: "Cinéma", 3: "Sport", 4: "Autre"}
50 +DETAIL_MAX = int(os.environ.get("TA_DETAIL_MAX", "25"))
51 +REFRESH_AFTER = 7 * 86400
52 +TTL = float(os.environ.get("TA_TTL_HOURS", "6")) * 3600
53 +HOST_DELAY = 120.0 # robots.txt : Crawl-delay: 120 (par hôte)
54 +
55 +_ORG_RE = re.compile(
56 + r'data-nom="([^"]*)"\s+data-ville="([^"]*)"\s+data-region="([^"]*)"\s+'
57 + r'data-domaine="([a-z0-9-]+)\.ticketacces\.net"')
58 +_ROW_RE = re.compile(
59 + r'<a href="https://([a-z0-9-]+)\.ticketacces\.net/fr/organisation/'
60 + r'representations/index\.cfm\?EvenementID=(\d+)">'
61 + r'([^<]+)(?:<br\s*/?>([^<]*))?</a>\s*</h3>\s*<p>\s*'
62 + r'<strong>([^<]+)</strong>\s*<br\s*/?>\s*([^<]+?)\s*</p>', re.S)
63 +_IMG_RE = re.compile(
64 + r'EvenementID=(\d+)">\s*<div class="image-crop[^"]*">\s*'
65 + r'<img src="([^"]+)"', re.S)
66 +_OG_DESC_RE = re.compile(r'property="og:description" content="([^"]*)"')
67 +_PRICE_RE = re.compile(r"(\d+(?:,\d{2}))\s*\$")
68 +# hors Québec (constaté 2026-08-21) : 12 organisations au Nouveau-Brunswick
69 +# (comtés) + 1 en Ontario (« Ottawa et sa campagne » = Hawkesbury) — les
70 +# libellés de région du portail n'utilisent pas les noms de provinces.
71 +_EXCLUDE_REGIONS = (
72 + "comte de gloucester", "comte de madawaska", "comte de restigouche",
73 + "kent", "peninsule acadienne", "ottawa et sa campagne",
74 + "nouveau brunswick", "ontario",
75 +)
76 +
77 +
78 +class TicketAccesConnector(BaseConnector):
79 + source_id = "ticketacces"
80 + request_delay = 1.0 # politesse entre HÔTES différents
81 +
82 + def __init__(self) -> None:
83 + super().__init__()
84 + self._host_last: dict[str, float] = {}
85 +
86 + def _get_cp1252(self, url: str) -> str:
87 + """GET décodé cp1252 (ColdFusion), Crawl-delay: 120 honoré PAR HÔTE."""
88 + host = url.split("/")[2]
89 + wait = HOST_DELAY - (time.time() - self._host_last.get(host, 0.0))
90 + if wait > 0:
91 + time.sleep(wait)
92 + resp = self.get(url)
93 + self._host_last[host] = time.time()
94 + return resp.content.decode("cp1252", errors="replace")
95 +
96 + def _orgs_quebec(self) -> dict[str, dict]:
97 + """{sous-domaine: {nom, ville, region}} des organisations AU QUÉBEC."""
98 + html = self._get_cp1252(PORTAL)
99 + orgs: dict[str, dict] = {}
100 + from ..regions import _k
101 + for nom, ville, region, dom in _ORG_RE.findall(html):
102 + if _k(region) in _EXCLUDE_REGIONS:
103 + continue
104 + orgs[dom] = {"nom": clean_text(nom), "ville": clean_text(ville),
105 + "region": clean_text(region)}
106 + return orgs
107 +
108 + def _parse_detail(self, html: str) -> dict:
109 + desc = _OG_DESC_RE.search(html)
110 + # bloc coordonnées de la salle : « |La Chapelle| 620, avenue Plante| … »
111 + plain = re.sub(r"<[^>]+>", "|", html)
112 + plain = re.sub(r"[\t\r\n ]+", " ", plain)
113 + addr = ""
114 + m = re.search(r"\|\s*(\d+[^|]{3,70}?)\s*\|", plain)
115 + if m and re.search(r"(rue|avenue|boulevard|chemin|route|rang|place)",
116 + m.group(1), re.I):
117 + addr = clean_text(m.group(1))
118 + prices = [float(p.replace(",", ".")) for p in _PRICE_RE.findall(plain)]
119 + return {
120 + "description": clean_text(desc.group(1)) if desc else "",
121 + "address": addr,
122 + "price_min": min(prices) if prices else None,
123 + }
124 +
125 + def fetch(self) -> list[Event]:
126 + cache = self.load_cache()
127 + now = time.time()
128 + meta = cache.get("_meta") or {}
129 + if now - meta.get("ts", 0) <= TTL and cache.get("_rows"):
130 + return self._emit(cache) # cache frais : aucun réseau
131 +
132 + orgs = self._orgs_quebec()
133 + if not orgs:
134 + raise RuntimeError("ticketacces : annuaire du portail vide")
135 +
136 + rows: dict[str, dict] = {} # clé de séance → ligne
137 + for cat, label in CATEGORIES.items():
138 + try:
139 + html = self._get_cp1252(CALENDAR.format(cat=cat))
140 + except Exception:
141 + print(f"[sorti-ka] ticketacces : calendrier {label} inaccessible")
142 + continue
143 + images = dict(_IMG_RE.findall(html))
144 + for org, evid, title, subtitle, date_raw, place in _ROW_RE.findall(html):
145 + if org not in orgs: # hors Québec ou org inconnue
146 + continue
147 + date_iso = parse_date_fr(date_raw)
148 + heure = parse_time(date_raw)
149 + if not date_iso:
150 + continue
151 + venue, city = "", orgs[org]["ville"]
152 + place = clean_text(place)
153 + if "," in place:
154 + venue, city = (p.strip() for p in place.rsplit(",", 1))
155 + elif place:
156 + venue = place
157 + key = f"{org}-{evid}-{date_iso}-{heure or ''}"
158 + if key in rows: # le HTML responsive duplique
159 + continue
160 + rows[key] = {
161 + "external_id": key,
162 + "org": org, "evid": evid,
163 + "url": (f"https://{org}.ticketacces.net/fr/organisation/"
164 + f"representations/index.cfm?EvenementID={evid}"),
165 + "title": clean_text(title),
166 + "subtitle": clean_text(subtitle or ""),
167 + "raw_category": label,
168 + "venue": venue, "city": city,
169 + "region": orgs[org]["region"],
170 + "organizer": orgs[org]["nom"],
171 + "start_date": date_iso, "start_time": heure,
172 + "image": images.get(evid, ""),
173 + }
174 +
175 + # fiches détail incrémentales (description, adresse, prix plancher) —
176 + # Crawl-delay par hôte : au plus UNE fiche par organisation par passage
177 + fiches = {f"{r['org']}-{r['evid']}": (r["org"], r["url"])
178 + for r in rows.values()}
179 + details = {k: v for k, v in cache.items()
180 + if k not in ("_meta", "_rows") and k in fiches}
181 + stale = sorted(
182 + (k for k in fiches
183 + if k not in details
184 + or now - details[k].get("ts", 0) > REFRESH_AFTER),
185 + key=lambda k: details.get(k, {}).get("ts", 0))
186 + seen_hosts: set[str] = set()
187 + picked: list[str] = []
188 + for k in stale:
189 + org = fiches[k][0]
190 + if org in seen_hosts:
191 + continue
192 + seen_hosts.add(org)
193 + picked.append(k)
194 + if len(picked) >= DETAIL_MAX:
195 + break
196 + for k in picked:
197 + try:
198 + detail = self._parse_detail(self._get_cp1252(fiches[k][1]))
199 + except Exception:
200 + detail = {}
201 + details[k] = {"ts": now, "detail": detail}
202 +
203 + cache = dict(details)
204 + cache["_meta"] = {"ts": now}
205 + cache["_rows"] = list(rows.values())
206 + self.save_cache(cache)
207 + return self._emit(cache)
208 +
209 + def _emit(self, cache: dict) -> list[Event]:
210 + events: list[Event] = []
211 + for r in cache.get("_rows", []):
212 + detail = cache.get(f"{r['org']}-{r['evid']}", {}).get("detail") or {}
213 + title = r["title"]
214 + if r["subtitle"]:
215 + title = f"{title}{r['subtitle']}"
216 + events.append(Event(
217 + source=self.source_id,
218 + external_id=r["external_id"],
219 + url=r["url"],
220 + title=title,
221 + description=detail.get("description", ""),
222 + raw_categories=[r["raw_category"]],
223 + venue=r["venue"],
224 + address=detail.get("address", ""),
225 + city=r["city"],
226 + region=r["region"], # gardée si déjà administrative
227 + tourist_region=r["region"], # sinon Charlevoix/Bellechasse…
228 + start_date=r["start_date"],
229 + start_time=r["start_time"],
230 + price_min=detail.get("price_min"),
231 + organizer=r["organizer"],
232 + image=r["image"],
233 + ))
234 + return events
added sortika/connectors/tuxedobillet.py +141 −0
@@ -0,0 +1,141 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/tuxedobillet.py : Tuxedo Billet — billetterie québécoise
5 +# multi-tenants (Phase 4). Reprise de la source écartée en Phase 3 (site en
6 +# panne) : l'apex reste vide mais les tenants <org>.tuxedobillet.com vivent.
7 +# Source : SPA Angular sur Firebase par tenant — RTDB verrouillée (401
8 +# même avec jeton anonyme, vérifié 2026-08-21) → rendu
9 +# navigateur OBLIGATOIRE. L'extraction est déléguée à l'acteur
10 +# Apify maison gorgeous_thistle/ka-tuxedo (Playwright, proxy
11 +# résidentiel CA, code dans apify/ka-tuxedo/) qui rend chaque
12 +# tenant et retourne les tuiles spectacles : titre, sous-titre,
13 +# date FR, image, lien /main/<slug>.
14 +# Extraction: 1 run d'acteur par ≈ TUX_TTL_HOURS (20 h par défaut — le
15 +# rendu JS coûte des crédits Apify ; les programmations de
16 +# salles bougent lentement). Résultat mis en cache disque
17 +# (data/tuxedobillet_cache.json) et réémis entre deux runs.
18 +# Tenants configurés dans data/tuxedo_tenants.json (Québec
19 +# seulement, ville vérifiée). La liste ne publie ni heure ni
20 +# prix → jamais inventés (heure « non confirmée » côté front).
21 +# Accès : billetteries publiques ; rendu navigateur modéré (1 page par
22 +# tenant par run, ~1 run/jour), UA/robots gérés par Apify,
23 +# lien d'achat = la fiche officielle du tenant.
24 +# Secrets : APIFY_TOKEN requis (dans .env) — sans lui, le connecteur
25 +# réémet le cache et n'échoue que s'il n'y a aucun cache.
26 +# -----------------------------------------------------------------------------
27 +from __future__ import annotations
28 +
29 +import json
30 +import os
31 +import re
32 +import time
33 +from pathlib import Path
34 +
35 +from ..normalize import clean_text, parse_date_fr
36 +from ..schema import Event
37 +from .base import BaseConnector
38 +
39 +ACTOR = "gorgeous_thistle~ka-tuxedo"
40 +RUN_URL = f"https://api.apify.com/v2/acts/{ACTOR}/runs"
41 +CONFIG_PATH = Path(__file__).resolve().parents[2] / "data" / "tuxedo_tenants.json"
42 +TTL = float(os.environ.get("TUX_TTL_HOURS", "20")) * 3600
43 +POLL_TIMEOUT = int(os.environ.get("TUX_POLL_TIMEOUT", "1500")) # s
44 +# le tile affiche parfois l'anglais selon le fuseau du proxy — mois EN → FR
45 +_EN_MONTHS = {"jan": "jan", "feb": "fev", "mar": "mars", "apr": "avr",
46 + "may": "mai", "jun": "juin", "jul": "juil", "aug": "aout",
47 + "sep": "sept", "oct": "oct", "nov": "nov", "dec": "dec"}
48 +
49 +
50 +class TuxedoBilletConnector(BaseConnector):
51 + source_id = "tuxedobillet"
52 + request_delay = 1.0
53 +
54 + def _load_tenants(self) -> list[dict]:
55 + try:
56 + return json.loads(CONFIG_PATH.read_text(encoding="utf-8"))["tenants"]
57 + except Exception:
58 + return []
59 +
60 + # -- acteur Apify ---------------------------------------------------------
61 + def _run_actor(self, tenants: list[str]) -> list[dict]:
62 + token = os.environ.get("APIFY_TOKEN")
63 + if not token:
64 + raise RuntimeError("APIFY_TOKEN manquant (voir .env)")
65 + headers = {"Authorization": f"Bearer {token}"}
66 + resp = self.session.post(
67 + RUN_URL, headers=headers, timeout=60,
68 + json={"tenants": tenants, "renderWaitMs": 6000})
69 + resp.raise_for_status()
70 + run = resp.json()["data"]
71 + run_id, dataset_id = run["id"], run["defaultDatasetId"]
72 + deadline = time.time() + POLL_TIMEOUT
73 + status = run["status"]
74 + while status in ("READY", "RUNNING") and time.time() < deadline:
75 + time.sleep(15)
76 + status = self.session.get(
77 + f"https://api.apify.com/v2/actor-runs/{run_id}",
78 + headers=headers, timeout=30).json()["data"]["status"]
79 + if status != "SUCCEEDED":
80 + raise RuntimeError(f"run Apify {run_id} : statut {status}")
81 + items = self.session.get(
82 + f"https://api.apify.com/v2/datasets/{dataset_id}/items",
83 + headers=headers, params={"clean": "true"}, timeout=60).json()
84 + return items if isinstance(items, list) else []
85 +
86 + @staticmethod
87 + def _parse_date(raw: str) -> str | None:
88 + """« 4 sept. 2026 » / « 4 Sep 2026 » → ISO (mois EN traduits)."""
89 + k = clean_text(raw).lower()
90 + k = re.sub(r"[a-z]+", lambda m: _EN_MONTHS.get(m.group(0), m.group(0)),
91 + k, count=2)
92 + return parse_date_fr(k)
93 +
94 + def fetch(self) -> list[Event]:
95 + tenants = {t["domaine"]: t for t in self._load_tenants()}
96 + if not tenants:
97 + return []
98 + cache = self.load_cache()
99 + now = time.time()
100 + if now - cache.get("ts", 0) > TTL:
101 + try:
102 + items = self._run_actor(sorted(tenants))
103 + cache = {"ts": now, "items": items}
104 + self.save_cache(cache)
105 + except Exception as exc:
106 + if not cache.get("items"):
107 + raise
108 + print(f"[sorti-ka] tuxedobillet : run Apify échoué ({exc}) — "
109 + "réémission du cache")
110 +
111 + events: list[Event] = []
112 + seen: set[str] = set()
113 + for it in cache.get("items", []):
114 + tenant = tenants.get(it.get("tenant", ""))
115 + slug = it.get("slug", "")
116 + title = clean_text(it.get("title", ""))
117 + if not tenant or not slug or not title:
118 + continue
119 + key = f"{it['tenant']}-{slug}"
120 + if key in seen:
121 + continue
122 + seen.add(key)
123 + date_iso = self._parse_date(it.get("date_raw", ""))
124 + if not date_iso: # tuile sans date sûre
125 + continue
126 + subtitle = clean_text(it.get("subtitle", ""))
127 + events.append(Event(
128 + source=self.source_id,
129 + external_id=key,
130 + url=it.get("url", ""),
131 + # le sous-titre est tantôt l'artiste, tantôt une série — on le
132 + # garde dans le titre mais jamais comme artiste (pas inventé)
133 + title=f"{title}{subtitle}" if subtitle else title,
134 + raw_categories=[title],
135 + venue=tenant["nom"],
136 + city=tenant["ville"],
137 + start_date=date_iso,
138 + organizer=tenant["nom"],
139 + image=it.get("image", ""),
140 + ))
141 + return events
added tests/fixtures/centreduquebec_fiche.html +18 −0
@@ -0,0 +1,18 @@
1 +<!-- Fixture réelle (extrait) : fiche la-balade-gourmande 2026-08-21 -->
2 +<html><head><meta property="og:image" content="https://tourismecentreduquebec.com/wp-content/uploads/2026/05/placeholder_video2.jpg" /></head><body>026 au 4 octobre 2026</h6>
3 + </div>
4 + <div class="event_head_row_contact_infos">
5 +
6 + <div class="event_head_row_contact_infos_adress info">
7 + <p>
8 + <small>Adresse</small>
9 + 103 Rue de Bigarré </p>
10 + </div>
11 +
12 +
13 + <div class="event_head_row_contact_infos_email info">
14 + <p>
15 + <small>Courriel</small>
16 + <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1871767e77587a7974797c7d7f776d6a7579767c7d367b79">[email&#160;protected]</a> </p>
17 + </div>
18 + </body></html>
\ No newline at end of file
added tests/fixtures/centreduquebec_liste.html +128 −0
@@ -0,0 +1,128 @@
1 +<!-- Fixture réelle (extrait) : tourismecentreduquebec.com/evenements/ 2026-08-21 -->
2 +<html><body><div class="events_listing_results">
3 + <div class="events_listing_results_event">
4 +
5 + <div class="events_listing_results_event_date">
6 + <h4>
7 + 1 septembre 2026 au 6 septembre 2026 </h4>
8 + </div>
9 + <div class="events_listing_results_event_name">
10 + <h4>Festival du Boeuf d'Inverness</h4>
11 + </div>
12 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/festival-du-boeuf-dinverness/" data-title="Festival du Boeuf d'Inverness" data-link="https://tourismecentreduquebec.com/evenements/festival-du-boeuf-dinverness/"></a>
13 +
14 + </div>
15 +
16 + <div class="events_listing_results_event">
17 +
18 + <div class="events_listing_results_event_date">
19 + <h4>
20 + 4 septembre 2026 au 5 septembre 2026 </h4>
21 + </div>
22 + <div class="events_listing_results_event_name">
23 + <h4>Le Grand BBQ Warwick</h4>
24 + </div>
25 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/le-grand-bbq-warwick/" data-title="Le Grand BBQ Warwick" data-link="https://tourismecentreduquebec.com/evenements/le-grand-bbq-warwick/"></a>
26 +
27 + </div>
28 +
29 + <div class="events_listing_results_event">
30 +
31 + <div class="events_listing_results_event_date">
32 + <h4>
33 + 12 septembre 2026 au 12 septembre 2026 </h4>
34 + </div>
35 + <div class="events_listing_results_event_name">
36 + <h4>Les Défis des Appalaches</h4>
37 + </div>
38 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/les-defis-des-appalaches/" data-title="Les Défis des Appalaches" data-link="https://tourismecentreduquebec.com/evenements/les-defis-des-appalaches/"></a>
39 +
40 + </div>
41 +
42 + <div class="events_listing_results_event">
43 +
44 + <div class="events_listing_results_event_date">
45 + <h4>
46 + 19 septembre 2026 au 20 septembre 2026 </h4>
47 + </div>
48 + <div class="events_listing_results_event_name">
49 + <h4>Lyst'ART</h4>
50 + </div>
51 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/lystart/" data-title="Lyst'ART" data-link="https://tourismecentreduquebec.com/evenements/lystart/"></a>
52 +
53 + </div>
54 +
55 + <div class="events_listing_results_event">
56 +
57 + <div class="events_listing_results_event_date">
58 + <h4>
59 + 26 septembre 2026 au 27 septembre 2026 </h4>
60 + </div>
61 + <div class="events_listing_results_event_name">
62 + <h4>Gleason en couleurs</h4>
63 + </div>
64 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/gleason-en-couleurs/" data-title="Gleason en couleurs" data-link="https://tourismecentreduquebec.com/evenements/gleason-en-couleurs/"></a>
65 +
66 + </div>
67 +
68 + <div class="events_listing_results_event">
69 +
70 + <div class="events_listing_results_event_date">
71 + <h4>
72 + 3 octobre 2026 au 4 octobre 2026 </h4>
73 + </div>
74 + <div class="events_listing_results_event_name">
75 + <h4>La Balade Gourmande</h4>
76 + </div>
77 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/la-balade-gourmande/" data-title="La Balade Gourmande" data-link="https://tourismecentreduquebec.com/evenements/la-balade-gourmande/"></a>
78 +
79 + </div>
80 +
81 + <div class="events_listing_results_event">
82 +
83 + <div class="events_listing_results_event_date">
84 + <h4>
85 + 3 octobre 2026 au 3 octobre 2026 </h4>
86 + </div>
87 + <div class="events_listing_results_event_name">
88 + <h4>Défi des familles</h4>
89 + </div>
90 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/defi-des-familles/" data-title="Défi des familles" data-link="https://tourismecentreduquebec.com/evenements/defi-des-familles/"></a>
91 +
92 + </div>
93 +
94 + <div class="events_listing_results_event">
95 +
96 + <div class="events_listing_results_event_date">
97 + <h4>
98 + 9 octobre 2026 au 11 octobre 2026 </h4>
99 + </div>
100 + <div class="events_listing_results_event_name">
101 + <h4>Festival Trad Cajun</h4>
102 + </div>
103 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/festival-trad-cajun/" data-title="Festival Trad Cajun" data-link="https://tourismecentreduquebec.com/evenements/festival-trad-cajun/"></a>
104 +
105 + </div>
106 +
107 + <div class="events_listing_results_event">
108 +
109 + <div class="events_listing_results_event_date">
110 + <h4>
111 + 15 novembre 2026 au 22 novembre 2026 </h4>
112 + </div>
113 + <div class="events_listing_results_event_name">
114 + <h4>Challenger Banque Nationale de Drummondville</h4>
115 + </div>
116 + <a class="hover_btn" href="https://tourismecentreduquebec.com/evenements/challenger-banque-nationale-de-drummondville/" data-title="Challenger Banque Nationale de Drummondville" data-link="https://tourismecentreduquebec.com/evenements/challenger-banque-nationale-de-drummondville/"></a>
117 +
118 + </div>
119 +
120 + </div>
121 +
122 + </div>
123 +
124 + </div>
125 +
126 + </section>
127 +
128 + </body></html>
\ No newline at end of file
added tests/fixtures/chaudiereappalaches_fiche.html +2 −0
@@ -0,0 +1,2 @@
1 +<!-- Fixture réelle (extrait) : fiche carrefour-mondial-de-laccordeon 2026-08-21 -->
2 +<html><head><script type="application/ld+json" class="woody-schema-graph">{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebSite","url":"https:\/\/chaudiereappalaches.com","name":"Chaudi\u00e8re-Appalaches pr\u00e8s de Qu\u00e9bec","description":"","potentialAction":[{"@type":"SearchAction","target":"https:\/\/chaudiereappalaches.com\/?query={search_term_string}","query-input":"required name=search_term_string"}]},{"@type":"WebPage","url":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/ete-automne\/festivals-et-evenements\/carrefour-mondial-de-laccordeon-montmagny-fr-6610550\/","headline":"Carrefour mondial de l\u2019Accord\u00e9on (Montmagny) | Chaudi\u00e8re-Appalaches pr\u00e8s de Qu\u00e9bec","inLanguage":"fr-FR","datePublished":"2026-06-22 15:59:06","dateModified":"2026-06-22 15:59:06","description":"Pr\u00e9parez-vous \u00e0 vibrer au son de l\u2019accord\u00e9on comme jamais! Ce festival rassemble les meilleurs musiciens d\u2019ici et d\u2019ailleurs pour des concerts hauts en rythme et en \u00e9motions. Du trad au jazz en passant par le musette, \u00e7a va swinguer fort!\n\nOn y va pour : Danser, s\u2019\u00e9merveiller et d\u00e9couvrir toute la richesse de l\u2019accord\u00e9on dans une ambiance festive et enlevante, dans des concerts autant en salle qu\u2019en plein-air!"},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil Principal","item":"https:\/\/chaudiereappalaches.com\/"},{"@type":"ListItem","position":2,"name":"Planifier votre s\u00e9jour","item":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/"},{"@type":"ListItem","position":3,"name":"\u00c9t\u00e9 | Automne","item":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/ete-automne\/"},{"@type":"ListItem","position":4,"name":"Festivals et \u00e9v\u00e9nements","item":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/ete-automne\/festivals-et-evenements\/"},{"@type":"ListItem","position":5,"name":"Carrefour mondial de l'Accord\u00e9on","item":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/ete-automne\/festivals-et-evenements\/carrefour-mondial-de-laccordeon-montmagny-fr-6610550\/"}]},{"@type":"Event","name":"Carrefour mondial de l'Accord\u00e9on","description":"Pr\u00e9parez-vous \u00e0 vibrer au son de l\u2019accord\u00e9on comme jamais! Ce festival rassemble les meilleurs musiciens d\u2019ici et d\u2019ailleurs pour des concerts hauts en rythme et en \u00e9motions. Du trad au jazz en passant par le musette, \u00e7a va swinguer fort!\n\nOn y va pour : Danser, s\u2019\u00e9merveiller et d\u00e9couvrir toute la richesse de l\u2019accord\u00e9on dans une ambiance festive et enlevante, dans des concerts autant en salle qu\u2019en plein-air!","url":"https:\/\/chaudiereappalaches.com\/planifier-votre-sejour\/ete-automne\/festivals-et-evenements\/carrefour-mondial-de-laccordeon-montmagny-fr-6610550\/","location":{"@type":"Place","name":"Adresse","address":{"@type":"PostalAddress","streetAddress":"301, Boulevard Tach\u00e9 Est","addressLocality":"Montmagny","postalCode":"G5V 1C5","addressCountry":"CANADA"},"geo":{"@type":"GeoCoordinates","latitude":"46.9853178","longitude":"-70.5473411"}},"aggregateRating":{"@type":"AggregateRating","ratingValue":8.8,"ratingCount":102,"bestRating":10},"startDate":"2026-09-04","endDate":"2026-09-06","eventAttendanceMode":"http:\/\/schema.org\/OfflineEventAttendanceMode","eventStatus":"http:\/\/schema.org\/EventScheduled","image":["https:\/\/hubo-cmaj-prod.s3.eu-west-1.amazonaws.com\/medias\/e7f6c45e5e5a4a52d5e2.jpg","https:\/\/hubo-cmaj-prod.s3.eu-west-1.amazonaws.com\/medias\/7859b261ff7640eff760.jpg","https:\/\/hubo-cmaj-prod.s3.eu-west-1.amazonaws.com\/medias\/9f45d2bae29f47574dcc.jpg","https:\/\/hubo-cmaj-prod.s3.eu-west-1.amazonaws.com\/medias\/8955caa34e28f8714e09.jpg","https:\/\/hubo-cmaj-prod.s3.eu-west-1.amazonaws.com\/medias\/53d13eebea671247a6da.jpg"]}]}</script></head><body></body></html>
\ No newline at end of file
added tests/fixtures/chaudiereappalaches_liste.html +14 −0
@@ -0,0 +1,14 @@
1 +<!-- Fixture réelle (extrait) : chaudiereappalaches.com festivals-et-evenements 2026-08-21 -->
2 +<html><body><a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/carrefour-mondial-de-laccordeon-montmagny-fr-6610550/"rel="opener" target="_blank">
3 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/cyclocross-de-levis-levis-fr-5920740/"rel="opener" target="_blank">
4 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festifilm-de-la-beauce-saint-severin-fr-5403946/"rel="opener" target="_blank">
5 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-country-lotbiniere-saint-agapit-fr-5377498/"rel="opener" target="_blank">
6 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-des-violons-dautomne-saint-jean-port-joli-fr-5773290/"rel="opener" target="_blank">
7 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-du-bucheux-saint-pamphile-fr-5462122/"rel="opener" target="_blank">
8 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-du-poulet-saint-damase-de-lislet-fr-5462186/"rel="opener" target="_blank">
9 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-international-du-conte-jos-violon-levis-fr-5983784/"rel="opener" target="_blank">
10 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/festival-promutuel-de-la-releve-de-thetford-mines-thetford-mines-fr-5377514/"rel="opener" target="_blank">
11 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/la-fete-dhiver-de-saint-jean-port-joli-saint-jean-port-joli-fr-6106961/"rel="opener" target="_blank">
12 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/les-grands-feux-loto-quebec-levis-fr-5377531/"rel="opener" target="_blank">
13 +<a href="https://chaudiereappalaches.com/planifier-votre-sejour/ete-automne/festivals-et-evenements/sur-les-traces-de-la-corrivaux-saint-vallier-fr-6659211/"rel="opener" target="_blank">
14 +<a href="?listpage=1" href="?listpage=2" href="?listpage=3">2</a></body></html>
\ No newline at end of file
added tests/fixtures/gaspesie_fiche.html +21 −0
@@ -0,0 +1,21 @@
1 +<!-- Fixture réelle (extrait) : fiche festival-bleubleu 2026-08-21 (via Scrapfly) -->
2 +<html><head><meta property="og:description" content="Le Festival BleuBleu propose, le temps d&#x2019;un week-end, une vingtaine de spectacles de musique aux styles diversifi&#xE9;s et plusieurs activit&#xE9;s..." />
3 +<meta property="og:image" content="https://www.tourisme-gaspesie.com/media/mq2dypnr/festival-bleubleu_mainpicture.jpg?width=1200&height=1200&mode=max" /></head><body><h1 class="my-0">Festival BleuBleu</h1><span class="font-display font-extrabold text-2xl">18 au 21 juin 2026</span>
4 + </div>
5 +
6 + <div data-search-exclude="true" class="mb-4 bg-primary-wash p-6 rounded-base h-remove-margin space-y-2">
7 +
8 +
9 + <h2 class="text-lg font-display font-extrabold mt-0">Festival BleuBleu</h2>
10 +
11 + <p>
12 +Carleton-sur-Mer, Qu&#xE9;bec </p>
13 +
14 + <ul class="mb-0 h-target-blank-hidden">
15 +
16 +
17 +
18 + <li class="flex gap-x-2 items-center mb-2 last:mb-0">
19 + <span class="sr-only">Courriel</span>
20 + <span class="text-secondary-shade">
21 + <svg xmlns="http://www.w3.org</body></html>
\ No newline at end of file
added tests/fixtures/gaspesie_liste.html +29 −0
@@ -0,0 +1,29 @@
1 +<!-- Fixture réelle (extrait) : tourisme-gaspesie.com festival-et-evenements 2026-08-21 (via Scrapfly) -->
2 +<html><body><a href="/fr/festival-et-evenements/artistes-en-fete/">x</a>
3 +<a href="/fr/festival-et-evenements/concerts-a-la-bougie-du-musee-culturel-de-coin-du-banc/">x</a>
4 +<a href="/fr/festival-et-evenements/evenements-gaspesia/">x</a>
5 +<a href="/fr/festival-et-evenements/festi-plage-de-cap-despoir/">x</a>
6 +<a href="/fr/festival-et-evenements/festival-bleubleu/">x</a>
7 +<a href="/fr/festival-et-evenements/festival-d-la-bon-humeur/">x</a>
8 +<a href="/fr/festival-et-evenements/festival-en-chanson-de-petite-vallee/">x</a>
9 +<a href="/fr/festival-et-evenements/festival-fauve-mauve/">x</a>
10 +<a href="/fr/festival-et-evenements/festival-fort-causap/">x</a>
11 +<a href="/fr/festival-et-evenements/festival-gaspesien-de-contes-et-legendes-1/">x</a>
12 +<a href="/fr/festival-et-evenements/festival-gaspesien-de-contes-et-legendes/">x</a>
13 +<a href="/fr/festival-et-evenements/festival-international-de-jardins/">x</a>
14 +<a href="/fr/festival-et-evenements/festival-international-du-journalisme-de-carleton-sur-mer/">x</a>
15 +<a href="/fr/festival-et-evenements/festival-la-viree-trad/">x</a>
16 +<a href="/fr/festival-et-evenements/festival-musique-du-bout-du-monde/">x</a>
17 +<a href="/fr/festival-et-evenements/festival-quad-matapedien/">x</a>
18 +<a href="/fr/festival-et-evenements/fete-de-la-saint-jean-a-perce/">x</a>
19 +<a href="/fr/festival-et-evenements/full-moon-gaspesien/">x</a>
20 +<a href="/fr/festival-et-evenements/furies-festival-de-danse-contemporaine/">x</a>
21 +<a href="/fr/festival-et-evenements/la-fete-des-guitares/">x</a>
22 +<a href="/fr/festival-et-evenements/les-perceides-festival-international-de-cinema-et-dart-de-perce/">x</a>
23 +<a href="/fr/festival-et-evenements/m-dgtl/">x</a>
24 +<a href="/fr/festival-et-evenements/matane-en-lumiere-desjardins/">x</a>
25 +<a href="/fr/festival-et-evenements/noel-en-gaspesie/">x</a>
26 +<a href="/fr/festival-et-evenements/panache-matane/">x</a>
27 +<a href="/fr/festival-et-evenements/rencontres-de-la-photographie-en-gaspesie/">x</a>
28 +<a href="/fr/festival-et-evenements/ultra-trail-des-chic-chocs/">x</a>
29 +<a href="/fr/festival-et-evenements/white-lips/">x</a></body></html>
\ No newline at end of file
added tests/fixtures/monteregie_fiche.html +33 −0
@@ -0,0 +1,33 @@
1 +<!-- Fixture réelle (extrait) : fiche tourisme-monteregie.qc.ca 2026-08-21 -->
2 +<html><head><script type="application/ld+json">{
3 + "@context": "http://schema.org",
4 + "@type": "Event",
5 + "name": "FESTIVAL DE CINÉMA PLAN D'ENSEMBLE",
6 + "description": "Ce Festival célèbre la diversité artistique et culturelle à travers le cinéma. Il vise ainsi à promouvoir l'échange et la coopération culturelle entre les citoyens du Québec. Venez en famille&hellip;",
7 + "address": "",
8 + "telephone": "",
9 + "faxNumber": "",
10 + "email": "",
11 + "url": "https://www.festivaldecinemaplandensemble.com/communique-officiel-3e-edition-du-festival-de-cinema-plan-densemble-fcpe-2026/",
12 + "image": "https://www.tourisme-monteregie.qc.ca/wp-content/uploads/2025/03/DSC09010.jpg",
13 + "priceRange": "",
14 + "geo": "",
15 + "mainEntityOfPage": {
16 + "@type": "WebPage",
17 + "@id": "https://www.tourisme-monteregie.qc.ca/"
18 + },
19 + "startDate": "",
20 + "endDate": "",
21 + "location": {
22 + "@type": "Place",
23 + "name": "FESTIVAL DE CINÉMA PLAN D'ENSEMBLE",
24 + "address": {
25 + "@type": "PostalAddress",
26 + "streetAddress": "15 Boulevard Maple",
27 + "addressLocality": "Châteauguay",
28 + "addressRegion": "QC",
29 + "postalCode": "J6J 3P7",
30 + "addressCountry": "CA"
31 + }
32 + }
33 +}</script></head><body></body></html>
\ No newline at end of file
added tests/fixtures/monteregie_liste.json +83 −0
@@ -0,0 +1,83 @@
1 +[
2 + {
3 + "id": 107680,
4 + "link": "https://www.tourisme-monteregie.qc.ca/evenement/festival-de-cinema-plan-densemble/",
5 + "title": {
6 + "rendered": "FESTIVAL DE CINÉMA PLAN D&rsquo;ENSEMBLE"
7 + },
8 + "content": {
9 + "rendered": "<p>Ce Festival célèbre la diversité artistique et culturelle à travers le cinéma. Il vise ainsi à promouvoir l&rsquo;échange et la coopération culturelle entre les citoyens du Québec. Venez en famille regarder les films des passionnés et professionnels.</p>\n"
10 + },
11 + "acf": {
12 + "syndic_object_id": "EVENMTOV503BKK",
13 + "updated_date": "2026-07-27T17:21:50.54",
14 + "lat": "45.3615440",
15 + "long": "-73.733526",
16 + "website": "https://www.festivaldecinemaplandensemble.com/communique-officiel-3e-edition-du-festival-de-cinema-plan-densemble-fcpe-2026/",
17 + "date": "Du 18 septembre au 20 septembre 2026",
18 + "accreditations": false,
19 + "images": [
20 + {
21 + "url": "https://www.tourisme-monteregie.qc.ca/wp-content/uploads/2025/03/DSC09010.jpg"
22 + }
23 + ]
24 + }
25 + },
26 + {
27 + "id": 106915,
28 + "link": "https://www.tourisme-monteregie.qc.ca/evenement/fete-des-moissons/",
29 + "title": {
30 + "rendered": "FÊTE DES MOISSONS"
31 + },
32 + "content": {
33 + "rendered": "<p>La Fête des Moissons est un rendez-vous incontournable qui célèbre le patrimoine, l&rsquo;agriculture et les traditions locales de Sainte-Martine. Pour sa 14e édition, les 11 et 12 septembre 2026, le Parc des Copains s&rsquo;animera avec une programmation variée pour toute la famille : spectacles, animations, salon des artisans régionaux, mini-ferme, activités familiales, feux d&rsquo;artifice et bien plus. Que ce soit pour découvrir le savoir-faire de nos artisans, profiter de l&rsquo;ambiance festive ou assister aux spectacles en soirée, la Fête des Moissons est l&rsquo;occasion idéale de se rassembler et de célébrer les richesses de notre région.</p>\n"
34 + },
35 + "acf": {
36 + "syndic_object_id": "EVENMTOV503X1B",
37 + "updated_date": "2026-07-13T18:48:21",
38 + "lat": "45.249164",
39 + "long": "-73.7979077",
40 + "date": "Du 11 septembre au 12 septembre 2026",
41 + "accreditations": false,
42 + "website": "https://sainte-martine.ca/evenement/fete-des-moissons-2/",
43 + "social_medias": {
44 + "facebook": "https://www.facebook.com/share/1CeqWd3wJa/",
45 + "flickr": "",
46 + "instagram": "",
47 + "pinterest": "",
48 + "tripadvisor": "",
49 + "twitter": "",
50 + "youtube": ""
51 + },
52 + "images": [
53 + {
54 + "url": "https://www.tourisme-monteregie.qc.ca/wp-content/uploads/2026/07/91b3c77c-845e-4bef-be6b-907f5b67f3dd.jpg"
55 + }
56 + ]
57 + }
58 + },
59 + {
60 + "id": 106874,
61 + "link": "https://www.tourisme-monteregie.qc.ca/evenement/les-jeudis-en-musique/",
62 + "title": {
63 + "rendered": "LES JEUDIS EN MUSIQUE"
64 + },
65 + "content": {
66 + "rendered": "<p>Les jeudis en musique, présenté par Desjardins Caisse du Sud-Ouest de la Montérégie, fait vibrer vos soirées d’été!<br />\nChaque jeudi à 19 h 30 (à l’exception du 13 août), rendez-vous pour une série de spectacles gratuits mettant en lumière des groupes hommage incontournables et des artistes émergents talentueux. En famille ou entre amis, profitez d’une ambiance chaleureuse et festive.</p>\n"
67 + },
68 + "acf": {
69 + "syndic_object_id": "EVENMTOV503WZ0",
70 + "updated_date": "2026-07-14T13:56:03.55",
71 + "lat": "45.361543999999",
72 + "long": "-73.73352659999",
73 + "website": "https://ville.chateauguay.qc.ca/nouvelles/les-jeudis-en-musique-2026/",
74 + "date": "Du 9 juillet au 27 août 2026",
75 + "accreditations": false,
76 + "images": [
77 + {
78 + "url": "https://www.tourisme-monteregie.qc.ca/wp-content/uploads/2026/07/4eb78d48-953a-4fe2-ab75-8edb8df1b9de.jpg"
79 + }
80 + ]
81 + }
82 + }
83 +]
\ No newline at end of file
added tests/fixtures/ovation_allevents.html +37 −0
@@ -0,0 +1,37 @@
1 +<!-- Fixture réelle (extrait) : m.ovation.ca/00001J AllEvents 2026-08-21 -->
2 +<html><body><div class="show" data-event-id="27066S">
3 + <div class="position show-picture">
4 + <img class="image_27066S" src="/product-catalog-images/thumbnails/e5413a63-83d2-4545-acd3-6accd9247244.jpg" alt="Elton songs">
5 + </div>
6 +
7 + <div class="all-show">
8 + <div class="all-show-name">
9 + <div class="text-shortener title" data-parent-max-height="42px">
10 +Elton songs<span class="event-additional-name"> | Hommage &#xE0; Elton John</span>
11 + </div>
12 + </div>
13 + <div class="info-show text-info">
14 + <span class="long-date">
15 +ven. 21 ao&#xFB;t, 20h00 </span>
16 + <br />
17 + <div>
18 + <div class="event-label"><div class="info-supp">Suppl&#xE9;mentaire</div></div>
19 + </div>
20 + Th&#xE9;&#xE2;tre Petit Champlain
21 + <br />
22 +Qu&#xE9;bec </div>
23 + </div>
24 + <div class="clear"></div>
25 + <div class="buy-link">
26 + <div class="button">
27 + <a href="/00001J/fr/Event/Tickets/?eventId=27066S"><span class="fas fa-ticket-alt" aria-hidden="true"></span>&nbsp;&nbsp;Billets</a>
28 + </div>
29 + <div class="more-info button">
30 + <a data-test-id="more-info-button" href="/00001J/fr/Event/?eventId=27066S">
31 + <span class="fa fa-info-circle" aria-hidden="true"></span>&nbsp;Plus d&#x27;info
32 + </a>
33 + </div>
34 + </div>
35 + </div>
36 + <div class="show" data-event-id="27066T">
37 + <div class=""></div></body></html>
\ No newline at end of file
added tests/fixtures/ovation_fiche.html +157 −0
@@ -0,0 +1,157 @@
1 +<!-- Fixture réelle : m.ovation.ca EventDescription eventId=2705UR (Marjo) 2026-08-21 -->
2 +
3 +
4 +
5 +<div id="description-result" class="new-event-description-visual">
6 + <div class="return-event-list">
7 + <a id="backButton"><span class="fas fa-chevron-circle-left"></span>&nbsp;&nbsp;Retour aux r&#xE9;sultats&nbsp;</a>
8 + </div>
9 +
10 + <div id="description-header">
11 + <div id="description">
12 + <div class="title">
13 + Marjo
14 +
15 + <span class="event-additional-name">| J&#x27;l&#xE2;che pas</span>
16 + </div>
17 +
18 + <div class="info-date">
19 + mercredi 26 ao&#xFB;t 2026, 20h00
20 + </div>
21 + <div>
22 + <div class="event-label">Suppl&#xE9;mentaire</div>
23 + </div>
24 + </div>
25 +
26 + <div id="media">
27 + <div id="media-content">
28 + <div class="all-media-content">
29 +
30 + <img src="/product-catalog-images/thumbnails/bff75c10-8e9d-4512-a65f-12d1226e64b3.jpg" alt="Marjo" class="carousel-media">
31 + </div>
32 + <div class="control">
33 + <a class="show-previous"><img src="/images/arrow-previous.png" /></a>
34 + <span class="text-info"></span>
35 + <a class="show-next"><img src="/images/arrow-next.png" /></a>
36 + </div>
37 + </div>
38 + </div>
39 +
40 + <div class="clear"></div>
41 + </div>
42 +
43 + <div id="description-section">
44 + <div>
45 + <span class="text-info label-container-section">
46 + <a class="label-info" href="/00001J/fr/Search/EventsInCategory/music/">
47 + Chanson et Musique
48 + </a>
49 + <a class="label-info" href="/00001J/fr/Search/EventsInCategory/rock/">
50 + Rock
51 + </a>
52 +
53 + <a class="label-info" href="/00001J/fr/Search/EventsByPerformer/marjo/">Marjo</a>
54 + </span>
55 + </div>
56 +
57 +
58 + <div class="event-content">
59 + <div>
60 + Report du ven. 17 oct. 25, 20h00
61 + </div>
62 + </div>
63 +
64 + <div>
65 + <div class="event-presentation text-info">
66 + <div class="text-shortener text-shortener-see-more" data-text-see-more="suite" data-parent-max-height="150px">
67 + <p>Rockeuse émouvante et authentique, <strong>Marjo vous offre ses plus grands succès aux rythmes endiablés</strong>, ainsi que certaines de ses plus belles ballades.<br><br>Accompagnée de ses trois musiciens et fidèle à sa réputation, elle vous électrisera avec son dynamisme et vous transmettra sa passion pour la musique jusqu'à vous faire dresser le poil sur les bras.</p>
68 + </div>
69 + </div>
70 + </div>
71 +
72 + <div class="button-section-content">
73 + <div class="button">
74 + <a data-scroll-id="all-dates-list">
75 + <span class="fas fa-ticket-alt" aria-hidden="true"></span>&nbsp;&nbsp;
76 + Acheter des billets
77 + </a>
78 + </div>
79 + <div class="clear"></div>
80 + </div>
81 +
82 + <div class="new-eventseries-link">
83 + <a data-scroll-id="event-section">
84 + Plus d&#x27;info
85 + </a>
86 + </div>
87 + <div class="new-eventseries-link">
88 + <a href="/00001J/fr/Event/?seriesId=0005S4">
89 + Voir toutes les dates disponibles
90 + </a>
91 + </div>
92 +
93 + <div class="bottom-info-section" id="event-section">
94 + <div class="title">
95 + <a class="no-link" href="/00001J/fr/Search/EventsByVenue/27-270001/">Th&#xE9;&#xE2;tre Petit Champlain</a>
96 +
97 + </div>
98 +68 Rue du Petit Champlain, <a href=/00001J/fr/Search/EventsByCity/quebec/>Québec</a> (Québec) G1K 4H4, Canada
99 +
100 +
101 + <a href="https://maps.google.ca?q=68&#x2B;Rue&#x2B;du&#x2B;Petit&#x2B;Champlain%2c&#x2B;Qu%c3%a9bec%2c&#x2B;G1K&#x2B;4H4%2c&#x2B;Qu%c3%a9bec%2c&#x2B;Canada" target="_blank" rel="noopener">
102 + <img src="/images/google-maps.png?v=2" class="google-maps" alt="Voir sur la carte" width="24" />
103 + </a>
104 +
105 +
106 + <div id="all-dates-list">
107 + <div class="title">
108 +Toutes les dates </div>
109 + <div>
110 + <div class="center-table">
111 + <ul>
112 + <li class="list-date">
113 + <div style="float:left">
114 + mercredi 26 ao&#xFB;t 2026, 20h00
115 + </div>
116 +
117 + <br />
118 +
119 + <span class="event-label">Suppl&#xE9;mentaire</span>
120 +
121 + <div>
122 + </div>
123 + </li>
124 +
125 + <li> <div class="button-ticket">
126 + <a href="/00001J/fr/Event/Tickets/?eventId=2705UR"><span class="fas fa-ticket-alt" aria-hidden="true"></span>&nbsp;Billets</a>
127 + </div>
128 +</li>
129 + </ul>
130 + <ul>
131 + <li class="list-date">
132 + <div style="float:left">
133 + jeudi 27 ao&#xFB;t 2026, 20h00
134 + </div>
135 +
136 + <br />
137 +
138 + <span class="event-label">Suppl&#xE9;mentaire</span>
139 +
140 + <div>
141 + </div>
142 + </li>
143 +
144 + <li> <div class="button-ticket">
145 + <a href="/00001J/fr/Event/Tickets/?eventId=2705US"><span class="fas fa-ticket-alt" aria-hidden="true"></span>&nbsp;Billets</a>
146 + </div>
147 +</li>
148 + </ul>
149 + </div>
150 + </div>
151 + </div>
152 +
153 + </div>
154 + </div>
155 +</div>
156 +
157 +
added tests/fixtures/ticketacces_calendrier.html +52 −0
@@ -0,0 +1,52 @@
1 +<!-- Fixture réelle (extrait) : calendrier.cfm cat 1 (Spectacles) 2026-08-21 -->
2 +<html><body><h1>Spectacles</h1><table><tr>
3 + <td class="col1">
4 +
5 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11177">
6 + <div class="image-crop moyenne">
7 + <img src="https://www.ticketacces.net/organisations/lachapellespectacles/evenements/petites/nouveau-site-web---2026-06-04t124628.944.jpg" alt="" loading="lazy" />
8 + </div>
9 + </a>
10 +
11 + </td>
12 + <td class="col2">
13 + <h3>
14 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11177">Rendez-vous dans l'Parking<br />Spectacles extérieurs gratuits</a>
15 + </h3>
16 + <p>
17 + <strong>Samedi 22 août 2026 à 12 h 30</strong><br />
18 + La Chapelle, Québec
19 + </p>
20 +
21 + <div class="clear">&nbsp;</div>
22 +
23 + </td>
24 + <td class="col3">
25 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11177" class="btn btn-primary">En savoir plus</a>
26 + </td>
27 + </tr> <tr>
28 + <td class="col1">
29 +
30 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11189">
31 + <div class="image-crop moyenne">
32 + <img src="https://www.ticketacces.net/organisations/lachapellespectacles/evenements/petites/nouveau-site-web712.jpg" alt="" loading="lazy" />
33 + </div>
34 + </a>
35 +
36 + </td>
37 + <td class="col2">
38 + <h3>
39 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11189">Les contes éphémères<br />Sarah Paquin</a>
40 + </h3>
41 + <p>
42 + <strong>Samedi 22 août 2026 à 13 h</strong><br />
43 + La Chapelle, Québec
44 + </p>
45 +
46 + <div class="clear">&nbsp;</div>
47 +
48 + </td>
49 + <td class="col3">
50 + <a href="https://lachapellespectacles.ticketacces.net/fr/organisation/representations/index.cfm?EvenementID=11189" class="btn btn-primary">En savoir plus</a>
51 + </td>
52 + </tr> </table></body></html>
\ No newline at end of file
added tests/fixtures/ticketacces_fiche.html +54 −0
@@ -0,0 +1,54 @@
1 +<!-- Fixture réelle (extrait) : fiche EvenementID=11177 (La Chapelle) 2026-08-21 -->
2 +<html><head><meta property="og:description" content="Comme un clin d'oeil à ces chaudes journées d'été de notre enfance, celles où, après la fin des classes, on se donnait rendez-vous dans le stationnement de l'église pour chiller entre amis, nous souhaitons raviver cet esprit simple et rassembleur!
3 +
4 +
5 +Le 22 août prochain, La Société culturelle de Vanier fera vibrer le quartier avec le lancement de la première édition des Rendez-vous dans l'Parking (d'La Chapelle) ! Au menu : animation de rue, contes en plein air, DJ sets et spectacles extérieurs pour petits et grands ! Et c'est gratuit !
6 +
7 +Programmation :
8 +12h30 : DJ Franze
9 +13h : Sarah Paquin - Spectacle de conte (par Route d'Artistes)
10 +14h : SOS Marcel Express (Jeune public)
11 +15h : Flip Fabrique - Vacances
12 +15h30 : DJ Franze
13 +16h : Le Grand Cirque de Ryu
14 +17h : Flip Fabrique - Vacances
15 +17h30 : Guillo show! - Spectacle de slackline en solo
16 +18h15 : Disco Vélo
17 +19h : Prise de parole des partenaires
18 +19h30 : Spectacle de musique dans l'Parking - FouKi, avec Shah Frank en première partie
19 +
20 +Spectacles déambulatoires tout au long de l'après-midi. Le 22 août, Rendez-vous dans l'Parking !
21 +
22 +Merci à nos partenaires :
23 +Caisse Desjardins de Rivières de Québec - Mario Asselin, député de Vanier-Les Rivières - Jean-Yves Duclos, député de Québec-Centre - Clément Bourdeau, conseiller de Vanier-Duberger
24 +
25 +
26 +Informations pratiques :
27 +- Les spectacles seront remis au lendemain en cas de pluie, sauf le spectacle de FouKi qui sera présenté en salle en cas de pluie.
28 +
29 +- Le stationnement sera fermé au public, sauf pour les espaces réservés aux personnes à mobilité réduite. Privilégiez le transport actif, le transport en commun ou le covoiturage. Le trajet 71, reliant la Basse-Ville et Vanier, sera aussi en service dès le 22 août!
30 +
31 +- Foodtrucks et bar sur place."/></head><body>>
32 + </div>
33 + </div>
34 + <div id="bas">
35 +
36 +
37 +<div class="contenu">
38 +
39 +
40 + <div class="row">
41 +
42 + <div class="column small-12 large-4">
43 +
44 + <h3>La Chapelle</h3>
45 + <p>
46 + 620, avenue Plante<br />
47 + Québec (Québec)&nbsp;&nbsp;G1M 3R5
48 + </p>
49 +
50 + <ul class="data-icons">
51 +
52 + <li>
53 + <span class="fa-stack fa-lg">
54 + <span>45,00 $</span> <span>25,00 $</span></body></html>
\ No newline at end of file
added tests/fixtures/ticketacces_portail.html +5 −0
@@ -0,0 +1,5 @@
1 +<!-- Fixture réelle (extrait) : ticketacces.net portail 2026-08-21 (cp1252 ré-encodé UTF-8) -->
2 +<html><body><a href="https://agora.ticketacces.net/fr/organisation/" class="org-card d-flex flex-column h-100" data-nom="Agora des Arts (Billetterie de l'Agora des Arts)" data-ville="Rouyn-Noranda" data-region="Abitibi-Témiscamingue" data-domaine="agora.ticketacces.net"></a>
3 +<a href="https://lachapellespectacles.ticketacces.net/fr/organisation/" class="org-card d-flex flex-column h-100" data-nom="La Chapelle" data-ville="Québec" data-region="Capitale-Nationale" data-domaine="lachapellespectacles.ticketacces.net"></a>
4 +<a href="https://octenbulle.ticketacces.net/fr/organisation/" class="org-card d-flex flex-column h-100" data-nom="Octenbulle" data-ville="Mascouche" data-region="Lanaudière" data-domaine="octenbulle.ticketacces.net"></a>
5 +<a href="https://scrn.ticketacces.net/fr/organisation/" class="org-card d-flex flex-column h-100" data-nom="Société Culturelle Régionale Népisiguit" data-ville="Bathurst" data-region="Comté de Gloucester" data-domaine="scrn.ticketacces.net"></a></body></html>
\ No newline at end of file
added tests/fixtures/tuxedobillet_items.json +29 −0
@@ -0,0 +1,29 @@
1 +[
2 + {
3 + "tenant": "espacetheatre",
4 + "url": "https://espacetheatre.tuxedobillet.com/main/jeunesse-ad-vision",
5 + "slug": "jeunesse-ad-vision",
6 + "title": "Billie du Page et Calamine",
7 + "subtitle": "Spectacle jeunesse AD_Vision",
8 + "date_raw": "4 Sep 2026",
9 + "image": "https://tuxedov1.blob.core.windows.net/tuxedo-espacetheatre-default-rtdb/show/x.jpg"
10 + },
11 + {
12 + "tenant": "espacetheatre",
13 + "url": "https://espacetheatre.tuxedobillet.com/main/boulevard-perusse",
14 + "slug": "boulevard-perusse",
15 + "title": "Boulevard Pérusse",
16 + "subtitle": "François Pérusse",
17 + "date_raw": "17 juin 2027",
18 + "image": "https://tuxedov1.blob.core.windows.net/tuxedo-espacetheatre-default-rtdb/show/y.jpg"
19 + },
20 + {
21 + "tenant": "inconnu-hors-config",
22 + "url": "https://x.tuxedobillet.com/main/z",
23 + "slug": "z",
24 + "title": "Hors config",
25 + "subtitle": "",
26 + "date_raw": "1 Oct 2026",
27 + "image": ""
28 + }
29 +]
\ No newline at end of file
added tests/test_phase4.py +216 −0
@@ -0,0 +1,216 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: tests/test_phase4.py
4 +# Desc: Phase 4 (régions manquantes + billetteries) — tests des nouveaux
5 +# connecteurs sur fixtures réelles figées. Aucun appel réseau
6 +# (CLAUDE.md §18).
7 +# ==============================================================================
8 +from __future__ import annotations
9 +
10 +import json
11 +from pathlib import Path
12 +
13 +from sortika.connectors import CONNECTORS
14 +
15 +FIXTURES = Path(__file__).parent / "fixtures"
16 +
17 +
18 +def _no_cache(monkeypatch, connector):
19 + """Cache disque neutralisé : ni lecture ni écriture pendant les tests."""
20 + monkeypatch.setattr(connector, "load_cache", lambda: {})
21 + monkeypatch.setattr(connector, "save_cache", lambda cache: None)
22 +
23 +
24 +class _Resp:
25 + def __init__(self, text):
26 + self.text = text
27 +
28 +
29 +def test_phase4_registry_autodiscovers():
30 + for sid in ("monteregie", "centreduquebec", "chaudiereappalaches",
31 + "gaspesie"):
32 + assert sid in CONNECTORS, sid
33 +
34 +
35 +def test_monteregie_parses_fixture(monkeypatch):
36 + from sortika.connectors.monteregie import MonteregieConnector
37 + c = MonteregieConnector()
38 + _no_cache(monkeypatch, c)
39 + rows = json.loads(
40 + (FIXTURES / "monteregie_liste.json").read_text(encoding="utf-8"))
41 + fiche = (FIXTURES / "monteregie_fiche.html").read_text(encoding="utf-8")
42 + monkeypatch.setattr(c, "_list_events", lambda: rows)
43 + monkeypatch.setattr(c, "get", lambda url, **kw: _Resp(fiche))
44 + events = [e.finalize() for e in c.fetch()]
45 + assert len(events) == 3
46 + ev = next(e for e in events if "PLAN D" in e.title)
47 + # plage FR de l'ACF → ISO ; GPS de l'ACF ; région via l'ATR
48 + assert (ev.start_date, ev.end_date) == ("2026-09-18", "2026-09-20")
49 + assert ev.lat and ev.lng and ev.region == "Montérégie"
50 + # fiche JSON-LD Event → adresse civique + ville (Châteauguay)
51 + assert ev.city == "Châteauguay" and ev.address == "15 Boulevard Maple"
52 + assert ev.postal_code == "J6J 3P7"
53 + assert ev.website.startswith("http") and ev.image.startswith("http")
54 + assert ev.description # content.rendered nettoyé
55 +
56 +
57 +def test_centreduquebec_parses_fixture(monkeypatch):
58 + from sortika.connectors.centreduquebec import CentreDuQuebecConnector
59 + c = CentreDuQuebecConnector()
60 + _no_cache(monkeypatch, c)
61 + liste = (FIXTURES / "centreduquebec_liste.html").read_text(encoding="utf-8")
62 + fiche = (FIXTURES / "centreduquebec_fiche.html").read_text(encoding="utf-8")
63 + monkeypatch.setattr(
64 + c, "get",
65 + lambda url, **kw: _Resp(liste if url.rstrip("/").endswith("evenements")
66 + else fiche))
67 + events = [e.finalize() for e in c.fetch()]
68 + assert len(events) >= 8 # ~10 fiches sur la liste réelle
69 + ev = next(e for e in events if "Inverness" in e.title)
70 + assert (ev.start_date, ev.end_date) == ("2026-09-01", "2026-09-06")
71 + assert ev.region == "Centre-du-Québec" # dérivée de l'ATR
72 + assert ev.url.startswith("https://tourismecentreduquebec.com/evenements/")
73 + # fiche : bloc « Adresse » + og:description/og:image
74 + assert any(e.address for e in events)
75 + assert any(e.image for e in events)
76 +
77 +
78 +def test_chaudiereappalaches_parses_fixture(monkeypatch):
79 + from sortika.connectors.chaudiereappalaches import (
80 + ChaudiereAppalachesConnector)
81 + c = ChaudiereAppalachesConnector()
82 + _no_cache(monkeypatch, c)
83 + liste = (FIXTURES / "chaudiereappalaches_liste.html").read_text(
84 + encoding="utf-8")
85 + fiche = (FIXTURES / "chaudiereappalaches_fiche.html").read_text(
86 + encoding="utf-8")
87 + monkeypatch.setattr(
88 + c, "get",
89 + lambda url, **kw: _Resp(fiche if "-fr-" in url else liste))
90 + events = [e.finalize() for e in c.fetch()]
91 + # 12 fiches listées → le budget FICHE_MAX en visite 12, toutes parsées
92 + # sur la même fixture de fiche (dates ISO du JSON-LD Event)
93 + assert events
94 + ev = events[0]
95 + assert "Accordéon" in ev.title
96 + assert (ev.start_date, ev.end_date) == ("2026-09-04", "2026-09-06")
97 + assert ev.city == "Montmagny" and ev.region == "Chaudière-Appalaches"
98 + assert ev.address == "301, Boulevard Taché Est"
99 + assert ev.lat and ev.lng # GeoCoordinates du JSON-LD
100 + assert ev.venue == "" # « Adresse » = libellé CMS
101 +
102 +
103 +def test_gaspesie_parses_fixture(monkeypatch):
104 + from sortika.connectors.gaspesie import GaspesieConnector
105 + c = GaspesieConnector()
106 + _no_cache(monkeypatch, c)
107 + liste = (FIXTURES / "gaspesie_liste.html").read_text(encoding="utf-8")
108 + fiche = (FIXTURES / "gaspesie_fiche.html").read_text(encoding="utf-8")
109 + monkeypatch.setattr(
110 + c, "_get_waf",
111 + lambda url: liste if url.rstrip("/").endswith("festival-et-evenements")
112 + else fiche)
113 + events = [e.finalize() for e in c.fetch()]
114 + assert events # fiches sans date écartées
115 + ev = events[0]
116 + assert ev.title == "Festival BleuBleu"
117 + # « 18 au 21 juin 2026 » (sans « du ») → plage complète, jamais inventée
118 + assert (ev.start_date, ev.end_date) == ("2026-06-18", "2026-06-21")
119 + assert ev.city == "Carleton-sur-Mer"
120 + assert ev.region == "Gaspésie–Îles-de-la-Madeleine"
121 + assert ev.description and ev.image
122 +
123 +
124 +# --- billetteries (Phase 4) --------------------------------------------------
125 +
126 +def test_billetteries_registry_autodiscovers():
127 + for sid in ("ticketacces", "ovation", "tuxedobillet"):
128 + assert sid in CONNECTORS, sid
129 +
130 +
131 +def test_ticketacces_parses_fixtures(monkeypatch):
132 + from sortika.connectors.ticketacces import TicketAccesConnector
133 + c = TicketAccesConnector()
134 + _no_cache(monkeypatch, c)
135 + portail = (FIXTURES / "ticketacces_portail.html").read_text(encoding="utf-8")
136 + cal = (FIXTURES / "ticketacces_calendrier.html").read_text(encoding="utf-8")
137 + fiche = (FIXTURES / "ticketacces_fiche.html").read_text(encoding="utf-8")
138 +
139 + def fake_get(url):
140 + if "portail/index" in url:
141 + return portail
142 + if "calendrier" in url:
143 + return cal
144 + return fiche
145 + monkeypatch.setattr(c, "_get_cp1252", fake_get)
146 + events = [e.finalize() for e in c.fetch()]
147 + # le portail-fixture contient 3 orgs QC + 1 hors-Québec (exclue) ; le
148 + # calendrier-fixture liste 2 représentations de La Chapelle (Québec)
149 + assert events
150 + ev = next(e for e in events if "Parking" in e.title)
151 + assert ev.start_date == "2026-08-22" and ev.start_time == "12:30"
152 + assert ev.venue == "La Chapelle" and ev.city == "Québec"
153 + assert ev.region == "Capitale-Nationale"
154 + assert ev.organizer # nom de l'organisation du portail
155 + # fiche : adresse + prix plancher (45,00 $ / 25,00 $ → 25.0)
156 + assert ev.address == "620, avenue Plante"
157 + assert ev.price_min == 25.0
158 + assert ev.description # og:description de la fiche
159 +
160 +
161 +def test_ticketacces_excludes_non_quebec(monkeypatch):
162 + from sortika.connectors.ticketacces import TicketAccesConnector
163 + c = TicketAccesConnector()
164 + portail = (FIXTURES / "ticketacces_portail.html").read_text(encoding="utf-8")
165 + monkeypatch.setattr(c, "_get_cp1252", lambda url: portail)
166 + orgs = c._orgs_quebec()
167 + assert "lachapellespectacles" in orgs and "agora" in orgs
168 + # l'annuaire réseau étiquette le NB par comtés — jamais « Québec »
169 + assert all("edmundston" not in d and d != "scrn" for d in orgs)
170 +
171 +
172 +def test_ovation_parses_fixtures(monkeypatch):
173 + from sortika.connectors.ovation import OvationConnector
174 + c = OvationConnector()
175 + _no_cache(monkeypatch, c)
176 + allev = (FIXTURES / "ovation_allevents.html").read_text(encoding="utf-8")
177 + fiche = (FIXTURES / "ovation_fiche.html").read_text(encoding="utf-8")
178 + monkeypatch.setattr(c, "_load_orgs", lambda: [
179 + {"code": "00001J", "nom": "Théâtre Petit Champlain", "ville": "Québec"}])
180 + monkeypatch.setattr(
181 + c, "_get_utf8",
182 + lambda url: allev if "AllEvents" in url else fiche)
183 + events = [e.finalize() for e in c.fetch()]
184 + assert events
185 + ev = events[0]
186 + # fragment EventDescription : date pleine FR + heure + salle + adresse
187 + assert ev.title.startswith("Marjo")
188 + assert ev.start_date == "2026-08-26" and ev.start_time == "20:00"
189 + assert ev.venue == "Théâtre Petit Champlain"
190 + assert ev.address == "68 Rue du Petit Champlain"
191 + assert ev.city == "Québec" and ev.postal_code == "G1K 4H4"
192 + assert ev.region == "Capitale-Nationale"
193 + assert ev.artists == ["Marjo"] # lien EventsByPerformer, pas inventé
194 + assert "musique" in ev.categories
195 + assert ev.description.startswith("Rockeuse")
196 +
197 +
198 +def test_tuxedobillet_parses_actor_items(monkeypatch):
199 + import json as _json
200 + from sortika.connectors.tuxedobillet import TuxedoBilletConnector
201 + c = TuxedoBilletConnector()
202 + items = _json.loads(
203 + (FIXTURES / "tuxedobillet_items.json").read_text(encoding="utf-8"))
204 + monkeypatch.setattr(c, "load_cache", lambda: {"ts": 10, "items": items})
205 + monkeypatch.setattr(c, "save_cache", lambda cache: None)
206 + monkeypatch.setattr(c, "_run_actor", lambda tenants: items)
207 + events = [e.finalize() for e in c.fetch()]
208 + # 3 items : 2 du tenant configuré (espacetheatre), 1 hors config (écarté)
209 + assert len(events) == 2
210 + ev = next(e for e in events if "Calamine" in e.title)
211 + assert ev.start_date == "2026-09-04" # « 4 Sep 2026 » (mois EN traduit)
212 + assert ev.city == "Mont-Laurier" and ev.region == "Laurentides"
213 + assert ev.venue == "Espace Théâtre"
214 + perusse = next(e for e in events if "Pérusse" in e.title)
215 + assert perusse.start_date == "2027-06-17" # « 17 juin 2027 » (FR)
216 + assert perusse.artists == [] # sous-titre ≠ artiste garanti
217