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>
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&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&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 (é) 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(?:é|é|é)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 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’un week-end, une vingtaine de spectacles de musique aux styles diversifiés et plusieurs activité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é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…", | |
| 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’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’é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’agriculture et les traditions locales de Sainte-Martine. Pour sa 14e édition, les 11 et 12 septembre 2026, le Parc des Copains s’animera avec une programmation variée pour toute la famille : spectacles, animations, salon des artisans régionaux, mini-ferme, activités familiales, feux d’artifice et bien plus. Que ce soit pour découvrir le savoir-faire de nos artisans, profiter de l’ambiance festive ou assister aux spectacles en soirée, la Fête des Moissons est l’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 à Elton John</span> | |
| 11 | + </div> | |
| 12 | + </div> | |
| 13 | + <div class="info-show text-info"> | |
| 14 | + <span class="long-date"> | |
| 15 | +ven. 21 août, 20h00 </span> | |
| 16 | + <br /> | |
| 17 | + <div> | |
| 18 | + <div class="event-label"><div class="info-supp">Supplémentaire</div></div> | |
| 19 | + </div> | |
| 20 | + Théâtre Petit Champlain | |
| 21 | + <br /> | |
| 22 | +Qué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> 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> Plus d'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> Retour aux résultats </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'lâche pas</span> | |
| 16 | + </div> | |
| 17 | + | |
| 18 | + <div class="info-date"> | |
| 19 | + mercredi 26 août 2026, 20h00 | |
| 20 | + </div> | |
| 21 | + <div> | |
| 22 | + <div class="event-label">Supplé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> | |
| 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'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éâ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+Rue+du+Petit+Champlain%2c+Qu%c3%a9bec%2c+G1K+4H4%2c+Qu%c3%a9bec%2c+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ût 2026, 20h00 | |
| 115 | + </div> | |
| 116 | + | |
| 117 | + <br /> | |
| 118 | + | |
| 119 | + <span class="event-label">Supplé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> Billets</a> | |
| 127 | + </div> | |
| 128 | +</li> | |
| 129 | + </ul> | |
| 130 | + <ul> | |
| 131 | + <li class="list-date"> | |
| 132 | + <div style="float:left"> | |
| 133 | + jeudi 27 août 2026, 20h00 | |
| 134 | + </div> | |
| 135 | + | |
| 136 | + <br /> | |
| 137 | + | |
| 138 | + <span class="event-label">Supplé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> 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"> </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"> </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) 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 | ||