SPB Git forge

spb/sorti-ka

Public

Toutes les sorties et tous les événements du Québec, un seul endroit — 7 connecteurs, fiches SSR, design Groupe KA.

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

Sorti·Ka v1 — agrégateur de sorties & événements du Québec

8 connecteurs actifs (Le point de vente, SIT Québec, Ville de Montréal,
evenko, La Vitrine, Atuvu, Sherbrooke, Laval), 15 231 événements actifs
dans les 17 régions, fiches SSR /evenement/{uid} avec JSON-LD, dédup
inter-sources, enrichissement d'images og:image, design system Groupe KA
« éditorial sharp », 19 tests sur fixtures réelles.

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

39 changed files +11,585 −0

added .gitignore +13 −0
@@ -0,0 +1,13 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: .gitignore
4 +# Desc: Exclusions Git — secrets, base de données, caches
5 +# ==============================================================================
6 +.env
7 +data/sortika.db
8 +data/*_cache.json
9 +__pycache__/
10 +*.pyc
11 +.pytest_cache/
12 +.venv/
13 +.DS_Store
added CLAUDE.md +572 −0
@@ -0,0 +1,572 @@
1 +<!--
2 +================================================================================
3 + Author: Simon-Pierre Boucher <contact@spboucher.ai>
4 + File: CLAUDE.md
5 + Project: Resto·Ka — Agrégateur québécois de tous les restaurants (menus + prix)
6 +================================================================================
7 +-->
8 +
9 +# CLAUDE.md — Resto·Ka
10 +
11 +> **Agrégateur exhaustif de tous les restaurants du Québec, avec leurs menus
12 +> complets et leurs prix.**
13 +> Chaque resto, chaque plat, chaque prix — dans les 17 régions, au même endroit,
14 +> comparable et cherchable.
15 +
16 +Ce fichier est la source de vérité pour tout agent Claude (ou humain) qui travaille
17 +sur ce dépôt. Lis-le **en entier** avant d'écrire une seule ligne de code.
18 +
19 +---
20 +
21 +## 0. À LIRE EN PREMIER — Règles non négociables
22 +
23 +1. **En-tête d'auteur obligatoire.** **Chaque fichier** du dépôt (code, config,
24 + doc, script) doit commencer par un en-tête mentionnant l'auteur :
25 + `Simon-Pierre Boucher <contact@spboucher.ai>`. Voir la **section 17** pour le
26 + format exact selon le type de fichier. Aucun fichier ne doit être commité sans
27 + cet en-tête.
28 +
29 +2. **Inspire-toi du dépôt Lou·Ka.** Avant de concevoir ou d'écrire **le moindre
30 + connecteur**, va lire le **dépôt de Lou·Ka** (le site jumeau, agrégateur de
31 + logements locatifs). Ses connecteurs sont le patron de référence : structure
32 + des modules, extraction (Firecrawl / Scrapfly), normalisation, déduplication,
33 + gestion des mises à jour, planification des tâches. **Ne réinvente rien** :
34 + reprends les mêmes conventions, la même architecture de connecteur, les mêmes
35 + utilitaires partagés. Resto·Ka doit être cohérent avec l'écosystème `·Ka`
36 + (Lou·Ka, Immo·Ka, Auto·Ka, Food·Ka, Fabri·Ka, Sorti·Ka). Voir **section 3**.
37 +
38 +3. **Travail de moine.** L'agrégation de menus est un travail minutieux, source par
39 + source, plat par plat, sans rien précipiter. Un menu mal parsé (mauvais prix,
40 + plat manquant, option ignorée) détruit la confiance dans toute la base. Mieux
41 + vaut un connecteur robuste de plus que dix fragiles.
42 +
43 +4. **Vérifie l'existant avant d'ajouter.** Avant d'intégrer un resto ou une source,
44 + confirme qu'il/elle n'est **pas déjà couvert(e)** (voir le registre des sources
45 + et l'index des restaurants, sections 9 et 12). Ne duplique jamais un connecteur
46 + ni une fiche resto. Un restaurant = une fiche canonique, quelles que soient les
47 + sources qui l'alimentent.
48 +
49 +5. **Chaque source a son connecteur sur mesure**, mais les sources qui partagent
50 + une **même plateforme** (livraison, commande en ligne, POS, réservation)
51 + doivent être couvertes par un **connecteur générique templatisé** réutilisable.
52 + Voir **section 8**.
53 +
54 +6. **La provenance du prix est sacrée.** Un prix de livraison (Uber Eats, DoorDash,
55 + Skip) est **majoré de 25-30 %** par rapport à la salle à manger. Ne **jamais**
56 + présenter un prix sans son contexte (`price_context`). Voir **sections 5 et 11**.
57 +
58 +---
59 +
60 +## 1. Mission
61 +
62 +Rendre visible, en un seul endroit, **tous les restaurants du Québec avec leurs
63 +menus et leurs prix réels**, dans les 17 régions — pour que quiconque puisse
64 +répondre à *« qu'est-ce que je mange, où, et combien ça coûte ? »* sans ouvrir
65 +quinze applis.
66 +
67 +### Ce qui nous distingue
68 +
69 +Les plateformes existantes sont soit des **apps de livraison** (Uber Eats, Skip,
70 +DoorDash — menus complets mais prix majorés, périmètre limité aux restos inscrits
71 +et payants), soit des **annuaires** (Google, Yelp, TripAdvisor — avis et coordonnées,
72 +mais **pas de menus structurés ni de prix comparables**), soit des **médias food**
73 +(Tastet, RestoMontréal — éditorial, pas exhaustif).
74 +
75 +Personne n'offre **le menu complet + les prix de tous les restos, comparables entre
76 +eux, partout au Québec.** C'est le créneau de Resto·Ka.
77 +
78 +---
79 +
80 +## 2. Périmètre — TOUS les établissements où l'on mange
81 +
82 +Périmètre maximaliste. Si on peut y commander à manger, ça doit finir dans Resto·Ka :
83 +
84 +- **Restaurants** (tous services : gastronomique, décontracté, familial).
85 +- **Fast-food & chaînes** (menus standardisés, multi-succursales).
86 +- **Cafés & salons de thé** (avec offre alimentaire).
87 +- **Bars & pubs** avec cuisine.
88 +- **Comptoirs pour emporter, cantines, casse-croûtes, food trucks.**
89 +- **Pizzérias, sushis, poulet, burgers, shawarma**, etc.
90 +- **Boulangeries-pâtisseries et traiteurs** avec menu et prix.
91 +- **Microbrasseries et établissements** avec menu de bouffe.
92 +- **Restaurants d'hôtel**, cuisines fantômes (ghost kitchens).
93 +
94 +> Règle d'or : en cas de doute sur l'inclusion, **on inclut** et on catégorise par
95 +> type d'établissement. Le périmètre est large par conception.
96 +
97 +---
98 +
99 +## 3. Dépôts de référence — étudier Lou·Ka AVANT de coder
100 +
101 +Resto·Ka fait partie de la famille `·Ka`. **Le dépôt de Lou·Ka est la référence
102 +canonique pour l'architecture des connecteurs.** Avant tout développement, l'agent
103 +Claude doit :
104 +
105 +1. **Ouvrir le dépôt de Lou·Ka** et localiser le dossier des connecteurs
106 + (typiquement `connectors/`, `scrapers/` ou `sources/`).
107 +2. **Lire 2 ou 3 connecteurs complets** de bout en bout pour comprendre :
108 + - la structure d'un module connecteur (interface commune, méthodes attendues) ;
109 + - l'**extraction** (Firecrawl / Scrapfly, endpoints JSON, sitemaps) ;
110 + - la **normalisation** vers le schéma commun ;
111 + - la **déduplication** (annonces publiées sur plusieurs plateformes) ;
112 + - la gestion des **mises à jour** (nouveautés, changements, retraits) ;
113 + - la **planification** (cadence, files, retries, backoff), les **erreurs** et le
114 + **logging**.
115 +3. **Réutiliser** les utilitaires partagés de Lou·Ka (client Firecrawl/Scrapfly,
116 + helpers de normalisation, déduplication, géocodage) plutôt que d'en réécrire.
117 +4. **Calquer** les conventions de nommage, la structure des dossiers et le style
118 + de code de Lou·Ka. Un dev qui connaît Lou·Ka doit se retrouver immédiatement.
119 +
120 +> Adaptation propre à Resto·Ka : là où Lou·Ka normalise une **annonce**, Resto·Ka
121 +> normalise un **restaurant + un menu imbriqué (catégories → plats → prix →
122 +> options)**. Le schéma est plus profond ; le reste du patron s'applique tel quel.
123 +
124 +> Si tu ne trouves pas le dépôt Lou·Ka ou l'accès aux connecteurs, **arrête-toi et
125 +> demande** plutôt que d'inventer une architecture divergente.
126 +
127 +Écosystème complet, pour contexte :
128 +
129 +| Site | Domaine | Agrège |
130 +|-------------|------------------|-------------------------------------------|
131 +| Lou·Ka | lou-ka.com | Logements à louer |
132 +| Immo·Ka | immo-ka.com | Propriétés à vendre |
133 +| Auto·Ka | auto-ka.com | Véhicules usagés |
134 +| Food·Ka | food-ka.com | Prix d'épicerie & circulaires |
135 +| Fabri·Ka | fabri-ka.com | Produits québécois |
136 +| Sorti·Ka | sorti-ka.com | Sorties & événements |
137 +| **Resto·Ka**| **resto-ka.com** | **Restaurants, menus & prix (ce dépôt)** |
138 +
139 +---
140 +
141 +## 4. Architecture
142 +
143 +Pipeline standard de la famille `·Ka`, adapté aux restaurants et menus :
144 +
145 +```
146 + ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ ┌──────────┐
147 + │ CONNECTEUR │──▶│ EXTRACTION │──▶│ NORMALISATION │──▶│ DÉDUPLICATION │──▶│ STOCKAGE │
148 + │ (par source)│ │ FC / Scrapfly│ │ resto + menu │ │ identité resto│ │ (DB) │
149 + │ │ │ + parse PDF │ │ (schéma commun)│ │ + items │ │ │
150 + └─────────────┘ └──────────────┘ └────────────────┘ └──────────────┘ └────┬─────┘
151 + │
152 + ┌──────────▼──────────┐
153 + │ API / RECHERCHE / │
154 + │ UI (menus & prix) │
155 + └─────────────────────┘
156 +```
157 +
158 +Composants :
159 +
160 +- **Connecteurs** : un module par source (ou par plateforme), interface commune.
161 +- **Orchestrateur** : planifie et exécute (cadence, retries, backoff).
162 +- **Extraction** : Firecrawl & Scrapfly + clients d'API + **parsing de menus PDF /
163 + images** (OCR) pour les menus non structurés.
164 +- **Normalisation** : mappe le brut vers le **schéma Restaurant + Menu** (section 5).
165 +- **Déduplication** : fusionne les restos et les items en double inter-sources.
166 +- **Stockage** : base restos + menus + **historique des prix** (suivi des hausses).
167 +- **API / UI** : recherche géolocalisée, filtres (cuisine, prix, diète), comparaison.
168 +
169 +---
170 +
171 +## 5. Modèle de données — schéma Restaurant + Menu
172 +
173 +### 5.1 Restaurant
174 +
175 +```jsonc
176 +{
177 + "id": "string", // * id interne canonique (post-déduplication)
178 + "source_ids": ["ubereats:abc"], // * id(s) d'origine par source (préfixés)
179 + "name": "string", // * nom normalisé
180 + "chain": "string|null", // marque/chaîne si applicable
181 + "cuisines": ["italien", "pizza"], // * taxonomie cuisine (section 6)
182 + "establishment_type": "restaurant",// restaurant|fast-food|cafe|bar|food-truck|traiteur...
183 + "price_range": "$$", // $ à $$$$ (estimé à partir des prix)
184 + "address": "string", // *
185 + "city": "string", // *
186 + "region": "string", // * une des 17 régions (section 7)
187 + "postal_code": "string",
188 + "lat": 0.0, // * géolocalisation
189 + "lng": 0.0, // *
190 + "phone": "string",
191 + "website": "string",
192 + "hours": { /* horaires par jour */ },
193 + "services": ["salle", "emporter", "livraison"], // dine-in|takeout|delivery
194 + "dietary_options": ["vegan", "sans-gluten", "halal"],
195 + "languages": ["fr", "en"],
196 + "menu": { /* voir 5.2 */ },
197 + "status": "open", // open|temporarily_closed|closed
198 + "first_seen": "ISO-8601", // *
199 + "last_seen": "ISO-8601", // *
200 + "updated_at": "ISO-8601" // *
201 +}
202 +```
203 +
204 +### 5.2 Menu (imbriqué)
205 +
206 +```jsonc
207 +"menu": {
208 + "currency": "CAD",
209 + "price_context": "delivery", // * dine-in | takeout | delivery (voir §6)
210 + "price_source": "ubereats", // * source du prix (traçabilité)
211 + "captured_at": "ISO-8601", // * quand ce menu/prix a été capté
212 + "sections": [
213 + {
214 + "name": "Pizzas",
215 + "items": [
216 + {
217 + "id": "string",
218 + "name": "Margherita", // *
219 + "description": "string",
220 + "price": 16.50, // * prix de l'item (dans price_context)
221 + "currency": "CAD",
222 + "tags": ["vegetarien"],
223 + "options": [ // modificateurs / variantes
224 + {
225 + "group": "Format",
226 + "required": true,
227 + "choices": [
228 + { "name": "10 pouces", "price_delta": 0.00 },
229 + { "name": "14 pouces", "price_delta": 6.00 }
230 + ]
231 + }
232 + ]
233 + }
234 + ]
235 + }
236 + ]
237 +}
238 +```
239 +
240 +Règles :
241 +- `price_context` et `price_source` sont **obligatoires** : un prix sans contexte
242 + est inutilisable (livraison ≠ salle).
243 +- `lat`/`lng` obligatoires → **géocoder** l'adresse si absente.
244 +- Capturer les **options/modificateurs** (formats, suppléments, choix) : ils
245 + changent le prix réel.
246 +- Conserver la **devise** partout (CAD). Au QC, TPS/TVQ s'ajoutent ; ne pas les
247 + inclure dans `price` sauf si la source le fait, et alors le **documenter**.
248 +
249 +---
250 +
251 +## 6. Taxonomies
252 +
253 +### 6.1 Cuisines (multi-valué)
254 +`quebecois` · `francais` · `italien` · `pizza` · `burgers` · `poulet` ·
255 +`bbq-grillades` · `fruits-de-mer` · `sushi-japonais` · `chinois` · `thai` ·
256 +`vietnamien` · `coreen` · `indien` · `libanais-moyen-orient` · `mexicain` ·
257 +`grec` · `mediterraneen` · `dejeuner-brunch` · `cafe-dessert` · `vegetarien-vegan` ·
258 +`fast-food` · `autre`
259 +
260 +### 6.2 Contexte de prix (`price_context`) — CRITIQUE
261 +- `dine-in` — prix en salle (référence idéale).
262 +- `takeout` — prix pour emporter (souvent = salle).
263 +- `delivery` — prix sur plateforme de livraison, **majoré de 25-30 %**.
264 +
265 +> Toujours privilégier `dine-in`/`takeout` (menu du resto lui-même ou sa plateforme
266 +> de commande en ligne) plutôt que `delivery` quand les deux existent. Si on n'a que
267 +> `delivery`, on l'affiche **clairement étiqueté**.
268 +
269 +### 6.3 Diètes / tags
270 +`vegan` · `vegetarien` · `sans-gluten` · `halal` · `casher` · `sans-noix` · `epice`
271 +
272 +---
273 +
274 +## 7. Régions du Québec (obligatoire pour chaque restaurant)
275 +
276 +Bas-Saint-Laurent · Saguenay–Lac-Saint-Jean · Capitale-Nationale · Mauricie ·
277 +Estrie · Montréal · Outaouais · Abitibi-Témiscamingue · Côte-Nord ·
278 +Nord-du-Québec · Gaspésie–Îles-de-la-Madeleine · Chaudière-Appalaches · Laval ·
279 +Lanaudière · Laurentides · Montérégie · Centre-du-Québec.
280 +
281 +Rattacher chaque resto à sa région via la ville/adresse (table de correspondance
282 +ville → région, comme dans Lou·Ka).
283 +
284 +---
285 +
286 +## 8. Philosophie des connecteurs
287 +
288 +- **Un connecteur par source**, taillé sur mesure.
289 +- **Connecteurs templatisés par plateforme** : c'est le levier d'échelle nº 1. Une
290 + plateforme de commande en ligne ou de livraison couvre des **milliers** de restos
291 + avec la même structure → un seul connecteur paramétré les couvre tous (exactement
292 + comme les connecteurs par plateforme d'Auto·Ka et Fabri·Ka).
293 +- **Deux types de sources à combiner** :
294 + 1. **Découverte** (liste des restos + métadonnées) : annuaires, cartes, tourisme.
295 + 2. **Menus + prix** (le cœur) : commande en ligne, livraison, sites de restos, PDF.
296 +- Interface commune (à calquer sur Lou·Ka) : `fetch()` → `normalize()` → `emit()`.
297 + Idempotent et incrémental quand c'est possible.
298 +- **Robustesse** : timeouts, retries + backoff, tolérance aux changements mineurs,
299 + alerte si le nombre d'items d'un menu chute anormalement (connecteur cassé).
300 +
301 +---
302 +
303 +## 9. Catalogue des sources à connecter (priorisé)
304 +
305 +> ⚠️ Vérifie le registre des sources et l'index des restos **avant** d'ajouter.
306 +
307 +### Palier 1 — Plateformes de commande en ligne des restos (MEILLEUR prix + volume)
308 +
309 +Ces plateformes propulsent les **sites/apps des restos eux-mêmes** → menus complets
310 +à des **prix réels (dine-in/takeout, non majorés)**. À templatiser par plateforme :
311 +
312 +- **UEAT** (foodtech de Québec, marque blanche, **très répandue au QC**).
313 +- **Square Online Ordering**, **Toast**, **Clover**, **GloriaFood (Oracle)**,
314 + **Flipdish**, **Foodiv**, **ChowNow**, **Bite (DoorDash)**.
315 +
316 +### Palier 2 — Plateformes de livraison (menus complets, prix MAJORÉS)
317 +
318 +Menus riches et structurés, mais prix `delivery` (+25-30 %), anti-bot fort et CGU
319 +restrictives (voir **section 15** — risque légal réel) :
320 +
321 +- **Uber Eats**, **SkipTheDishes**, **DoorDash**.
322 +- Plateformes québécoises : **RestoLoco** et autres services locaux régionaux.
323 +
324 +> Politique : utiliser la livraison surtout pour la **découverte** et pour les
325 +> restos qu'on ne trouve nulle part ailleurs. Toujours étiqueter `price_context:
326 +> delivery`. Préférer une source `dine-in` dès qu'elle existe.
327 +
328 +### Palier 3 — Sites web des restaurants (menus « maison »)
329 +
330 +- Menus en **HTML** (parse direct) ou en **PDF / image** (→ parsing PDF + OCR,
331 + voir section 10). Détecter la plateforme de commande embarquée (souvent Palier 1).
332 +
333 +### Palier 4 — Découverte & métadonnées (liste des restos)
334 +
335 +- **Google Places / Maps**, **Yelp**, **TripAdvisor** (coordonnées, horaires, type
336 + de cuisine, fourchette de prix — **rarement les menus** ; respecter les CGU/API).
337 +- **Associations touristiques régionales (ATR)**, Tourisme Québec.
338 +- Médias food : **Tastet**, **RestoMontréal** et annuaires régionaux (découverte).
339 +
340 +### Palier 5 — Réservation (métadonnées + parfois menus)
341 +
342 +- **OpenTable**, **LibroReserve** (Québec), **Resy**, **Zenchef** — horaires,
343 + services, parfois menus.
344 +
345 +### Palier 6 — Chaînes (menus standardisés)
346 +
347 +- Sites des grandes chaînes : menus et prix souvent uniformes par bannière et
348 + parfois par région → un connecteur par chaîne couvre toutes les succursales.
349 +
350 +---
351 +
352 +## 10. Extraction — Firecrawl, Scrapfly & parsing de menus
353 +
354 +Les connecteurs s'appuient **souvent sur Firecrawl et Scrapfly** (comme tout
355 +l'écosystème `·Ka`). Règle de choix :
356 +
357 +- **API / endpoint JSON de la plateforme** → toujours privilégié (données
358 + structurées, stables). Beaucoup de plateformes de commande chargent leur menu
359 + via un appel JSON interne : **le trouver et le consommer directement.**
360 +- **Firecrawl** → crawl et extraction en volume de sites relativement ouverts
361 + (sites de restos, annuaires), extraction structurée (markdown/JSON).
362 +- **Scrapfly** → sites protégés, JavaScript lourd, anti-bot, rendu navigateur,
363 + rotation d'IP. **Requis** pour les plateformes de livraison (Uber Eats, Skip,
364 + DoorDash) qui bloquent agressivement.
365 +- **Parsing de menus PDF / images** : de nombreux restos publient un menu en PDF ou
366 + en photo. Prévoir extraction de texte PDF + **OCR** + un post-traitement (LLM) qui
367 + transforme le texte brut en structure `sections → items → price → options`.
368 + Valider systématiquement (un prix mal OCR-isé est pire que pas de prix).
369 +
370 +Chaque connecteur doit **documenter** en tête de fichier son mode d'extraction et
371 +pourquoi. Réutiliser les clients partagés de Lou·Ka.
372 +
373 +---
374 +
375 +## 11. Normalisation
376 +
377 +- Nettoyer noms de restos, sections et plats (HTML résiduel, majuscules, doublons).
378 +- Mapper vers le **schéma Restaurant + Menu** (section 5) intégralement.
379 +- **Cuisines** et **type d'établissement** classés dès la normalisation (section 6).
380 +- **Géocoder** (lat/lng obligatoires) et **rattacher la région** (section 7).
381 +- **Prix** : renseigner **obligatoirement** `price_context` et `price_source` ;
382 + capturer les **options/modificateurs**. Ne jamais mélanger des prix de contextes
383 + différents dans un même menu.
384 +- **Devise** CAD ; documenter le traitement des taxes (TPS/TVQ hors prix au QC).
385 +- Conserver les **`source_ids`** préfixés (ex. `ueat:1234`, `ubereats:abcd`).
386 +
387 +---
388 +
389 +## 12. Déduplication — restaurants ET items
390 +
391 +Un même resto apparaît sur Uber Eats + DoorDash + Skip + son propre site + Google.
392 +
393 +### 12.1 Identité du restaurant
394 +1. **Clé** : nom normalisé + adresse (géoloc arrondie) + téléphone → empreinte.
395 +2. **Approché** : noms proches + même adresse → fusion candidate (journaliser).
396 +3. **Fusion** : un enregistrement canonique, agréger les `source_ids`, choisir la
397 + meilleure source par champ.
398 +4. **Multi-succursales / chaînes** : chaque **adresse** est une fiche resto
399 + distincte, reliée par `chain`. Ne pas fusionner deux succursales.
400 +
401 +### 12.2 Menus & prix
402 +- Ne pas écraser un menu `dine-in` par un menu `delivery` : **conserver les deux
403 + contextes** et présenter le meilleur (dine-in prioritaire), en gardant delivery
404 + étiqueté.
405 +- Dédupliquer les **items** au sein d'un même contexte (même plat listé deux fois).
406 +- **Historiser** les prix : à chaque capture, si le prix d'un item change, garder la
407 + série temporelle (utile pour détecter les hausses et pour la crédibilité).
408 +
409 +S'inspirer de la déduplication de **Lou·Ka** (multi-plateformes) et d'Auto·Ka
410 +(identité forte par clé).
411 +
412 +---
413 +
414 +## 13. Fraîcheur des menus & états
415 +
416 +- **Menus changeants** : prix et plats évoluent (saisonnier, table d'hôte,
417 + spéciaux). Rafraîchir régulièrement et **horodater** chaque capture (`captured_at`).
418 +- **Fermetures** : resto fermé temporairement / définitivement → `status` mis à jour
419 + plutôt que suppression brutale (politique de grâce via `last_seen`, comme Lou·Ka).
420 +- **Items retirés** : disparition d'un plat → le marquer inactif, conserver
421 + l'historique.
422 +- **Spéciaux / table d'hôte** : capturer si structurés, avec leur période de validité.
423 +
424 +---
425 +
426 +## 14. Cadence de rafraîchissement
427 +
428 +- Grandes plateformes (commande en ligne, livraison, chaînes) : **hebdomadaire** au
429 + minimum ; **quotidien** pour les prix sur les gros volumes urbains.
430 +- Sites de restos indépendants : **hebdomadaire à mensuel** (menus stables).
431 +- Découverte (annuaires) : **mensuel** pour repérer nouveaux restos / fermetures.
432 +- Toujours mettre à jour `last_seen` et `captured_at` à chaque passage réussi.
433 +
434 +---
435 +
436 +## 15. Conformité & aspects légaux — À PRENDRE AU SÉRIEUX
437 +
438 +> C'est le point le plus sensible de ce projet. Les plateformes de livraison
439 +> **protègent activement** leurs données (menus/prix) et l'interdisent dans leurs
440 +> CGU. Ne pas traiter ça à la légère.
441 +
442 +- Respecter les **CGU** et `robots.txt` de chaque source. Les plateformes de
443 + livraison sont explicitement restrictives : **minimiser**, privilégier d'autres
444 + sources pour les mêmes restos, et **demander** en cas de doute avant d'industrialiser.
445 +- Privilégier les **sources de première partie** : le **site du resto** et sa
446 + **plateforme de commande** (UEAT, etc.) sont la voie la plus légitime pour le
447 + menu et le prix réel.
448 +- **Rate-limiting** raisonnable, identification honnête, pas de surcharge.
449 +- Utiliser les **API officielles** quand elles existent (Google Places, Yelp) et
450 + en respecter les quotas et conditions d'affichage/attribution.
451 +- Ne republier que ce qui est nécessaire à la découverte (nom, plats, prix, infos
452 + pratiques, lien vers la source). **Attribuer** les sources requises.
453 +- Consigner par source la base légale/mode d'accès retenu (API vs scraping) en tête
454 + de connecteur.
455 +
456 +---
457 +
458 +## 16. Conventions de code
459 +
460 +- Suivre **les mêmes conventions que Lou·Ka** (langage, structure de dossiers,
461 + nommage, linting, formatage). Cohérence > préférences personnelles.
462 +- Un connecteur = un module isolé, testable indépendamment.
463 +- Secrets (clés Firecrawl/Scrapfly, API Google/Yelp) via variables d'environnement /
464 + gestionnaire de secrets. Jamais en clair.
465 +- Logging structuré (source, nb de restos, nb d'items, erreurs, durée).
466 +- Commits atomiques, messages clairs.
467 +
468 +---
469 +
470 +## 17. En-tête d'auteur — OBLIGATOIRE dans chaque fichier
471 +
472 +**Tout fichier** du dépôt doit débuter par un en-tête d'auteur. L'auteur est
473 +toujours **Simon-Pierre Boucher \<contact@spboucher.ai\>**.
474 +
475 +**Python / Shell / YAML / TOML / Dockerfile :**
476 +```python
477 +# ==============================================================================
478 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
479 +# File: <nom_du_fichier>
480 +# Desc: <brève description>
481 +# ==============================================================================
482 +```
483 +
484 +**JavaScript / TypeScript / Go / Rust / Java / C :**
485 +```javascript
486 +// ==============================================================================
487 +// Author: Simon-Pierre Boucher <contact@spboucher.ai>
488 +// File: <nom_du_fichier>
489 +// Desc: <brève description>
490 +// ==============================================================================
491 +```
492 +
493 +**Markdown / HTML / XML :**
494 +```html
495 +<!--
496 + Author: Simon-Pierre Boucher <contact@spboucher.ai>
497 + File: <nom_du_fichier>
498 + Desc: <brève description>
499 +-->
500 +```
501 +
502 +**CSS / SCSS :**
503 +```css
504 +/*
505 + Author: Simon-Pierre Boucher <contact@spboucher.ai>
506 + File: <nom_du_fichier>
507 +*/
508 +```
509 +
510 +Règles :
511 +- **Aucun** fichier commité sans cet en-tête.
512 +- Pour un connecteur, indiquer dans `Desc:` la **source/plateforme couverte**, le
513 + **mode d'extraction** (API / Firecrawl / Scrapfly / PDF-OCR) et le **contexte de
514 + prix** produit (dine-in / takeout / delivery).
515 +- Vérifier la présence de l'en-tête en revue de code (idéalement hook de pré-commit).
516 +
517 +---
518 +
519 +## 18. Tests & qualité
520 +
521 +- Chaque connecteur : **tests unitaires** de normalisation sur des fixtures réelles
522 + (réponses JSON, HTML, PDF de menus figés).
523 +- Tests de **parsing de menus** : vérifier prix, options et sections sur des
524 + échantillons connus (les prix sont l'actif le plus critique).
525 +- Tests de **déduplication** resto + items multi-sources.
526 +- **Validation de schéma** : tout resto/menu émis doit passer la validation avant
527 + stockage ; rejeter un menu sans `price_context`/`price_source`.
528 +- **Monitoring** : alerte si le nb d'items d'un resto ou le volume d'une source
529 + chute anormalement (connecteur probablement cassé).
530 +
531 +---
532 +
533 +## 19. Checklist — ajouter un nouveau connecteur / restaurant
534 +
535 +1. [ ] Lire les connecteurs de référence dans le **dépôt Lou·Ka** (section 3).
536 +2. [ ] Vérifier que la source/le resto **n'est pas déjà couvert** (registres).
537 +3. [ ] Identifier la **plateforme** : peut-on templatiser/réutiliser un connecteur ?
538 +4. [ ] Choisir le **mode d'extraction** : API > site du resto > Firecrawl > Scrapfly ;
539 + PDF/OCR si menu non structuré (section 10).
540 +5. [ ] Déterminer le **`price_context`** produit et le documenter.
541 +6. [ ] Implémenter `fetch()` → `normalize()` → `emit()` (interface commune).
542 +7. [ ] Mapper **tout** le schéma resto + menu, géocoder, région, cuisines, options.
543 +8. [ ] Brancher la **déduplication** resto + items (section 12).
544 +9. [ ] Gérer **fraîcheur, fermetures, items retirés, historique des prix** (§13).
545 +10. [ ] Vérifier la **conformité** CGU/robots et consigner la base d'accès (§15).
546 +11. [ ] Ajouter l'**en-tête d'auteur** (section 17).
547 +12. [ ] Écrire les **tests** + fixtures, dont le parsing de prix (section 18).
548 +13. [ ] Enregistrer la source dans le **registre** avec sa cadence.
549 +14. [ ] Valider volume et qualité sur un premier run, journaliser.
550 +
551 +---
552 +
553 +## 20. Roadmap / MVP
554 +
555 +**MVP** : templatiser **un** connecteur pour la plateforme de commande en ligne la
556 +plus répandue au QC (**UEAT**) → menus complets à prix réels (`dine-in`/`takeout`)
557 +pour des milliers de restos d'un coup. Ajouter la découverte via Google Places/Yelp
558 +pour la liste et les métadonnées, et le parsing des menus PDF des sites de restos.
559 +UI de recherche géolocalisée avec filtres (cuisine, prix, diète, service) et
560 +affichage clair du **contexte de prix**.
561 +
562 +**Ensuite** : plateformes de livraison (avec étiquetage strict), chaînes,
563 +réservation, régions moins couvertes. Puis historique des prix, comparaison entre
564 +restos, et cross-linking avec les autres sites `·Ka`.
565 +
566 +**Nord stratégique** : être **le seul** à offrir menus **et** prix **comparables**
567 +de **tous** les restos du Québec — là où la livraison est chère et partielle, les
568 +annuaires sans menus, et les médias food non exhaustifs.
569 +
570 +---
571 +
572 +*Fin de CLAUDE.md — Resto·Ka. Auteur : Simon-Pierre Boucher \<contact@spboucher.ai\>.*
added README.md +141 −0
@@ -0,0 +1,141 @@
1 +<!--
2 + Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 + File: README.md
4 + Desc: Documentation du dépôt Sorti-Ka — agrégateur de sorties & événements
5 +-->
6 +
7 +<div align="center">
8 +
9 +# Sorti·Ka
10 +
11 +### Toutes les sorties et tous les événements du Québec. Un seul endroit.
12 +
13 +**[www.sorti-ka.com](https://www.sorti-ka.com)**
14 +
15 +![Python](https://img.shields.io/badge/Python-3.14-141814?style=for-the-badge&logo=python&logoColor=d9f26b)
16 +![FastAPI](https://img.shields.io/badge/FastAPI-API_+_SSR-141814?style=for-the-badge&logo=fastapi&logoColor=d9f26b)
17 +![SQLite](https://img.shields.io/badge/SQLite-storage-141814?style=for-the-badge&logo=sqlite&logoColor=d9f26b)
18 +![Frontend](https://img.shields.io/badge/Frontend-SPA_sans_build-141814?style=for-the-badge&logo=html5&logoColor=d9f26b)
19 +
20 +![Événements](https://img.shields.io/badge/%C3%A9v%C3%A9nements_actifs-15_231-1c5c41?style=flat-square)
21 +![À venir](https://img.shields.io/badge/%C3%A0_venir-11_559-1c5c41?style=flat-square)
22 +![Gratuits](https://img.shields.io/badge/gratuits-3_159-1c5c41?style=flat-square)
23 +![Connecteurs](https://img.shields.io/badge/connecteurs_actifs-8-1c5c41?style=flat-square)
24 +![Régions](https://img.shields.io/badge/r%C3%A9gions-17%2F17-1c5c41?style=flat-square)
25 +![Villes](https://img.shields.io/badge/villes-428-1c5c41?style=flat-square)
26 +![Tests](https://img.shields.io/badge/tests-19_pass-1c5c41?style=flat-square)
27 +
28 +*Agrégateur indépendant de sorties & événements — concerts, festivals, spectacles,
29 +expositions, sport, famille — dans les 17 régions administratives du Québec, avec
30 +dates, lieux, gratuité, fiche par événement et lien direct vers la billetterie ou
31 +la source originale. Mise à jour horaire, automatiquement.*
32 +
33 +<img src="docs/screenshots/accueil.png" alt="Page d'accueil Sorti-Ka" width="920">
34 +
35 +</div>
36 +
37 +---
38 +
39 +## Pourquoi Sorti·Ka ?
40 +
41 +Trouver quoi faire au Québec, c'est ouvrir dix sites : les billetteries ne montrent
42 +que leurs propres spectacles, les calendriers municipaux que leur ville, les sites
43 +touristiques que leurs membres. **Sorti·Ka retourne le problème** : un connecteur
44 +dédié par source visite chaque calendrier, normalise chaque événement vers un
45 +schéma unique, déduplique les doublons inter-sources et détecte les changements
46 +en continu.
47 +
48 +Sorti·Ka n'est pas une billetterie : c'est un **index fidèle**. Aucun prix inventé,
49 +aucune date devinée — chaque fiche est attribuée à sa source et renvoie vers la
50 +fiche originale pour les billets. Membre de la famille **·Ka**
51 +([Lou·Ka](https://www.lou-ka.com), Immo·Ka, Auto·Ka, Food·Ka, Fabri·Ka, Resto·Ka),
52 +architecture calquée sur le patron de référence Lou·Ka.
53 +
54 +## Visite guidée
55 +
56 +| | |
57 +|:---:|:---:|
58 +| **Recherche filtrée** — région, catégorie, période, gratuité, texte libre | **Fiche par événement** — dates, lieu, prix, carte, similaires, SSR + JSON-LD |
59 +| <img src="docs/screenshots/accueil.png" alt="Accueil" width="440"> | <img src="docs/screenshots/fiche.png" alt="Fiche événement" width="440"> |
60 +
61 +## L'architecture en 30 secondes
62 +
63 +```
64 + connecteurs (1 module / source) normalisation (Event) SQLite (diff + grâce)
65 + sortika/connectors/*.py → schema.py + normalize.py → db.py
66 + ▲ auto-découverts + regions.py (MAMH) │
67 + │ ▼
68 + watcher horaire (PM2) frontend SPA + fiches ← API FastAPI + SSR SEO
69 + ingest.py watch frontend/index.html web.py + seo.py
70 +```
71 +
72 +| Couche | Rôle | Fichiers |
73 +|---|---|---|
74 +| **Connecteurs** | 1 module Python par source : API JSON internes (Algolia evenko, Tourinsoft, CKAN), listes HTML, Scrapfly (Cloudflare), caches incrémentaux | `sortika/connectors/*.py` |
75 +| **Schéma** | `Event` standardisé : titre, catégories canoniques, lieu, ville, **région (17)**, lat/lng, dates ISO, gratuité/prix avec `price_label` source | `sortika/schema.py` |
76 +| **Normalisation** | dates FR/ISO, prix, catégories (14), villes, entités HTML | `sortika/normalize.py` |
77 +| **Régions** | Répertoire des municipalités MAMH : 1 250 villes + arrondissements + alias, homonymes résolus par population | `sortika/regions.py` + `data/villes_regions.json` |
78 +| **Persistance** | upsert par hash de contenu, délai de grâce 2 syncs, alerte de dérive, historique de syncs | `sortika/db.py` |
79 +| **Dédup** | empreinte titre + ville + date : un même concert publié par 3 sources = **une carte**, la fiche la plus riche gagne | `schema.dedup_key()` + `web.py` |
80 +| **API + SSR** | recherche/filtres JSON, fiches `/evenement/{uid}` servies avec `<head>` unique + **JSON-LD Event**, robots, sitemaps | `sortika/web.py` + `sortika/seo.py` |
81 +| **Frontend** | SPA sans build, routeur History API, design system **Groupe KA « éditorial sharp »** | `frontend/index.html` |
82 +
83 +## Les 8 connecteurs actifs
84 +
85 +| Source | Type / accès | Extraction | Volume |
86 +|---|---|---|---|
87 +| **Le point de vente** | billetterie QC — robots `Allow: *` | listes HTML paginées (~65 pages) | ~2 500 concerts, humour, théâtre — toute la province |
88 +| **SIT Québec** (Ministère du Tourisme) | données ouvertes CC 4.0 | API JSON Tourinsoft | ~1 600 événements touristiques, toutes les régions |
89 +| **Ville de Montréal** | données ouvertes CC BY 4.0 | API CKAN datastore | ~5 600 événements municipaux |
90 +| **evenko** | promoteur — robots `Allow: /api/search*` | proxy Algolia percé (`body=base64({params:[q,opts],lang})`) | ~460 grands concerts (Centre Bell, Place Bell, MTELUS…) |
91 +| **La Vitrine** | Content-Signal `search=yes, use=reference` | sitemap + fiches HTML (dates de tournée dédupliquées par id de représentation), cache incrémental | ~5 100 représentations |
92 +| **Atuvu.ca** | Cloudflare → **Scrapfly**, robots `Allow: /` | sitemaps lastmod + fiches, cache incrémental plafonné | rattrapage progressif |
93 +| **Ville de Sherbrooke** | calendrier municipal public | JSON embarqué `TRANSFER_CACHE` — 1 requête | ~270 événements avec images et gratuité |
94 +| **Ville de Laval** | données ouvertes CC 4.0 | JSON officiel | ~290 activités |
95 +
96 +Sources écartées après vérification (jeux périmés) : Longueuil (2017), Saguenay (2022),
97 +Repentigny (2021) — consignées au registre `data/sources.json` avec leur statut.
98 +
99 +## Démarrage rapide
100 +
101 +```bash
102 +python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
103 +.venv/bin/python -m pytest tests/ -q # 19 tests (fixtures réelles, zéro réseau)
104 +.venv/bin/python run.py sync # ingestion des 8 sources
105 +.venv/bin/python run.py web --port 8120 # API + frontend + SSR
106 +.venv/bin/python run.py watch --interval 3600 # boucle d'ingestion horaire
107 +```
108 +
109 +Secrets : `SCRAPFLY_API_KEY` dans `.env` (requis seulement pour Atuvu).
110 +
111 +## API
112 +
113 +| Route | Description |
114 +|---|---|
115 +| `GET /api/events` | recherche — `q`, `region`, `city`, `category`, `free`, `from`, `to`, `sort`, pagination ; dédupliquée inter-sources |
116 +| `GET /api/events/{uid}` | fiche complète d'un événement |
117 +| `GET /api/stats` | volumes par région et par catégorie |
118 +| `GET /api/sources` | registre des sources + événements actifs + dernière synchro |
119 +| `GET /evenement/{uid}` | fiche HTML SSR (title/og/canonical + **JSON-LD Event**) |
120 +| `GET /sitemap.xml` · `/robots.txt` | référencement |
121 +| `GET /docs` | Swagger |
122 +
123 +## Production
124 +
125 +Nœud **M3U96a** (MacLustr) — PM2 : `sorti-ka-web` (uvicorn :8120),
126 +`sorti-ka-sync` (watch **horaire** — mise à jour continue), `sorti-ka-ngrok`
127 +(`www.sorti-ka.com`). Registre : `~/Desktop/cluster-skill/cluster-deployments.json`.
128 +
129 +## Principes
130 +
131 +1. **Index fidèle** — rien d'inventé : prix et dates viennent de la source, avec
132 + `price_label` original conservé et attribution systématique.
133 +2. **Sources légitimes d'abord** — données ouvertes officielles et sites au
134 + robots.txt permissif ; CGU/licences consignées en tête de chaque connecteur.
135 +3. **Travail de moine** — un connecteur robuste à la fois, testé sur fixtures
136 + réelles, avec alerte de dérive quand une source casse.
137 +4. **Cohérence ·Ka** — architecture Lou·Ka, design system Groupe KA.
138 +
139 +---
140 +
141 +© 2026 Sorti·Ka — Simon-Pierre Boucher \<contact@spboucher.ai\>
added data/sources.json +129 −0
@@ -0,0 +1,129 @@
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)",
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)",
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",
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)",
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": "actif"
47 + },
48 + {
49 + "id": "sherbrooke",
50 + "name": "Ville de Sherbrooke — Calendrier des événements",
51 + "url": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites",
52 + "type": "municipal",
53 + "extraction": "JSON embarqué window['TRANSFER_CACHE'] (CMS Maruche) — 1 requête, ~270 événements avec images, gratuité, catégories",
54 + "acces_legal": "page municipale publique (référencée sur Données Québec) — GET direct, identification honnête",
55 + "couverture": "Événements municipaux et culturels, Sherbrooke (Estrie)",
56 + "cadence": "horaire (watcher PM2)",
57 + "statut": "actif"
58 + },
59 + {
60 + "id": "longueuil",
61 + "name": "Ville de Longueuil — Événements",
62 + "url": "https://www.donneesquebec.ca/recherche/dataset/evenements",
63 + "type": "donnees-ouvertes",
64 + "extraction": "CSV (Données Québec)",
65 + "acces_legal": "Licence CC 4.0 (Données Québec)",
66 + "couverture": "Longueuil (Montérégie)",
67 + "cadence": "n/d",
68 + "statut": "abandonnee",
69 + "note": "Jeu de données périmé — dernière date d'événement : 2017-03 (vérifié 2026-08-17)"
70 + },
71 + {
72 + "id": "saguenay",
73 + "name": "Ville de Saguenay — Événements culturels",
74 + "url": "https://www.donneesquebec.ca/recherche/dataset/sag_evenements-culturels",
75 + "type": "donnees-ouvertes",
76 + "extraction": "CSV (Données Québec)",
77 + "acces_legal": "Licence CC 4.0 (Données Québec)",
78 + "couverture": "Saguenay",
79 + "cadence": "n/d",
80 + "statut": "abandonnee",
81 + "note": "Jeu de données périmé — dernière date d'événement : 2022-06 (vérifié 2026-08-17)"
82 + },
83 + {
84 + "id": "repentigny",
85 + "name": "Ville de Repentigny — Événements",
86 + "url": "https://www.donneesquebec.ca/recherche/dataset/evenement-sur-le-territoire-de-la-ville",
87 + "type": "donnees-ouvertes",
88 + "extraction": "CSV (Données Québec)",
89 + "acces_legal": "Licence CC 4.0 (Données Québec)",
90 + "couverture": "Repentigny (Lanaudière)",
91 + "cadence": "n/d",
92 + "statut": "abandonnee",
93 + "note": "Jeu de données périmé — dernière date d'événement : 2021-09 (vérifié 2026-08-17)"
94 + },
95 + {
96 + "id": "evenko",
97 + "name": "evenko — grands concerts et festivals",
98 + "url": "https://evenko.ca/fr/evenements",
99 + "type": "promoteur",
100 + "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",
101 + "acces_legal": "robots.txt : Allow /api/search* (explicite) — lien billets = URL Ticketmaster officielle",
102 + "couverture": "Grands concerts (Centre Bell, Place Bell, MTELUS…), festivals (Osheaga, ÎleSoniq…)",
103 + "cadence": "horaire (watcher PM2)",
104 + "statut": "actif"
105 + },
106 + {
107 + "id": "atuvu",
108 + "name": "Atuvu.ca — calendrier culturel",
109 + "url": "https://atuvu.ca/",
110 + "type": "agregateur",
111 + "extraction": "Scrapfly (Cloudflare) — sitemaps lastmod + fiches .event-play, cache incrémental data/atuvu_cache.json, plafond 120 fiches/sync",
112 + "acces_legal": "robots.txt : Allow / + sitemap public",
113 + "couverture": "Sorties culturelles, Grand Montréal",
114 + "cadence": "horaire (watcher PM2, incrémental)",
115 + "statut": "actif"
116 + },
117 + {
118 + "id": "lavitrine",
119 + "name": "La Vitrine culturelle",
120 + "url": "https://www.lavitrine.com/",
121 + "type": "billetterie",
122 + "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",
123 + "acces_legal": "robots.txt : Allow / + Content-Signal search=yes, use=reference — usage index + lien vers la fiche originale",
124 + "couverture": "Spectacles et sorties culturelles, Grand Montréal",
125 + "cadence": "horaire (watcher PM2, incrémental)",
126 + "statut": "actif"
127 + }
128 + ]
129 +}
\ No newline at end of file
added data/villes_regions.json +1255 −0
@@ -0,0 +1,1255 @@
1 +{
2 +"_comment": "Sorti-Ka — table ville → région administrative, générée depuis le Répertoire des municipalités (MAMH, données ouvertes : MUN.csv + ARR.csv arrondissements) + alias fréquents. Auteur : Simon-Pierre Boucher — contact@spboucher.ai. Clés normalisées (minuscules, sans accents, séparateurs → espace).",
3 +"villes": {
4 +"abercorn": "Estrie",
5 +"acton vale": "Montérégie",
6 +"adstock": "Chaudière-Appalaches",
7 +"aguanish": "Côte-Nord",
8 +"ahuntsic cartierville": "Montréal",
9 +"akulivik": "Nord-du-Québec",
10 +"akwesasne": "Montérégie",
11 +"albanel": "Saguenay–Lac-Saint-Jean",
12 +"albertville": "Bas-Saint-Laurent",
13 +"alleyn et cawood": "Outaouais",
14 +"alma": "Saguenay–Lac-Saint-Jean",
15 +"amherst": "Laurentides",
16 +"amos": "Abitibi-Témiscamingue",
17 +"amqui": "Bas-Saint-Laurent",
18 +"ange gardien": "Montérégie",
19 +"anjou": "Montréal",
20 +"armagh": "Chaudière-Appalaches",
21 +"arundel": "Laurentides",
22 +"ascot corner": "Estrie",
23 +"aston jonction": "Centre-du-Québec",
24 +"auclair": "Bas-Saint-Laurent",
25 +"audet": "Estrie",
26 +"aumond": "Outaouais",
27 +"aupaluk": "Nord-du-Québec",
28 +"austin": "Estrie",
29 +"authier": "Abitibi-Témiscamingue",
30 +"authier nord": "Abitibi-Témiscamingue",
31 +"ayer s cliff": "Estrie",
32 +"baie atibenne": "Lanaudière",
33 +"baie comeau": "Côte-Nord",
34 +"baie d hudson": "Nord-du-Québec",
35 +"baie d urfe": "Montréal",
36 +"baie de la bouteille": "Lanaudière",
37 +"baie des chaloupes": "Laurentides",
38 +"baie des sables": "Bas-Saint-Laurent",
39 +"baie du febvre": "Centre-du-Québec",
40 +"baie johan beetz": "Côte-Nord",
41 +"baie obaoca": "Lanaudière",
42 +"baie saint paul": "Capitale-Nationale",
43 +"baie sainte catherine": "Capitale-Nationale",
44 +"baie trinite": "Côte-Nord",
45 +"barkmere": "Laurentides",
46 +"barnston ouest": "Estrie",
47 +"barraute": "Abitibi-Témiscamingue",
48 +"batiscan": "Mauricie",
49 +"beaconsfield": "Montréal",
50 +"bearn": "Abitibi-Témiscamingue",
51 +"beauceville": "Chaudière-Appalaches",
52 +"beauharnois": "Montérégie",
53 +"beaulac garthby": "Chaudière-Appalaches",
54 +"beaumont": "Chaudière-Appalaches",
55 +"beauport": "Capitale-Nationale",
56 +"beaupre": "Capitale-Nationale",
57 +"becancour": "Centre-du-Québec",
58 +"bedford": "Estrie",
59 +"begin": "Saguenay–Lac-Saint-Jean",
60 +"belcourt": "Abitibi-Témiscamingue",
61 +"belle riviere": "Saguenay–Lac-Saint-Jean",
62 +"belleterre": "Abitibi-Témiscamingue",
63 +"beloeil": "Montérégie",
64 +"berry": "Abitibi-Témiscamingue",
65 +"berthier sur mer": "Chaudière-Appalaches",
66 +"berthierville": "Lanaudière",
67 +"bethanie": "Montérégie",
68 +"biencourt": "Bas-Saint-Laurent",
69 +"blainville": "Laurentides",
70 +"blanc sablon": "Côte-Nord",
71 +"blue sea": "Outaouais",
72 +"boileau": "Outaouais",
73 +"bois des filion": "Laurentides",
74 +"bois franc": "Outaouais",
75 +"boisbriand": "Laurentides",
76 +"boischatel": "Capitale-Nationale",
77 +"bolton est": "Estrie",
78 +"bolton ouest": "Estrie",
79 +"bonaventure": "Gaspésie–Îles-de-la-Madeleine",
80 +"bonne esperance": "Côte-Nord",
81 +"bonsecours": "Estrie",
82 +"boucherville": "Montérégie",
83 +"bouchette": "Outaouais",
84 +"bowman": "Outaouais",
85 +"brebeuf": "Laurentides",
86 +"brigham": "Estrie",
87 +"bristol": "Outaouais",
88 +"brome": "Estrie",
89 +"bromont": "Estrie",
90 +"brompton rock forest saint elie deauville": "Estrie",
91 +"brossard": "Montérégie",
92 +"brownsburg chatham": "Laurentides",
93 +"bryson": "Outaouais",
94 +"bury": "Estrie",
95 +"cacouna": "Bas-Saint-Laurent",
96 +"calixa lavallee": "Montérégie",
97 +"calumet": "Laurentides",
98 +"campbell s bay": "Outaouais",
99 +"candiac": "Montérégie",
100 +"caniapiscau": "Côte-Nord",
101 +"cantley": "Outaouais",
102 +"cap chat": "Gaspésie–Îles-de-la-Madeleine",
103 +"cap saint ignace": "Chaudière-Appalaches",
104 +"cap sante": "Capitale-Nationale",
105 +"caplan": "Gaspésie–Îles-de-la-Madeleine",
106 +"carignan": "Montérégie",
107 +"carleton sur mer": "Gaspésie–Îles-de-la-Madeleine",
108 +"cascades malignes": "Outaouais",
109 +"cascapedia saint jules": "Gaspésie–Îles-de-la-Madeleine",
110 +"causapscal": "Bas-Saint-Laurent",
111 +"cayamant": "Outaouais",
112 +"chambly": "Montérégie",
113 +"chambord": "Saguenay–Lac-Saint-Jean",
114 +"champlain": "Mauricie",
115 +"champneuf": "Abitibi-Témiscamingue",
116 +"chandler": "Gaspésie–Îles-de-la-Madeleine",
117 +"chapais": "Nord-du-Québec",
118 +"charette": "Mauricie",
119 +"charlemagne": "Lanaudière",
120 +"charlesbourg": "Capitale-Nationale",
121 +"chartierville": "Estrie",
122 +"chateau richer": "Capitale-Nationale",
123 +"chateauguay": "Montérégie",
124 +"chazel": "Abitibi-Témiscamingue",
125 +"chelsea": "Outaouais",
126 +"cheneville": "Outaouais",
127 +"chertsey": "Lanaudière",
128 +"chesterville": "Centre-du-Québec",
129 +"chibougamau": "Nord-du-Québec",
130 +"chichester": "Outaouais",
131 +"chicoutimi": "Saguenay–Lac-Saint-Jean",
132 +"chisasibi": "Nord-du-Québec",
133 +"chute aux outardes": "Côte-Nord",
134 +"chute saint philippe": "Laurentides",
135 +"clarenceville": "Montérégie",
136 +"clarendon": "Outaouais",
137 +"clerval": "Abitibi-Témiscamingue",
138 +"cleveland": "Estrie",
139 +"cloridorme": "Gaspésie–Îles-de-la-Madeleine",
140 +"coaticook": "Estrie",
141 +"collines du basque": "Gaspésie–Îles-de-la-Madeleine",
142 +"colombier": "Côte-Nord",
143 +"compton": "Estrie",
144 +"contrecoeur": "Montérégie",
145 +"cookshire eaton": "Estrie",
146 +"cote des neiges notre dame de grace": "Montréal",
147 +"cote nord du golfe du saint laurent": "Côte-Nord",
148 +"cote saint luc": "Montréal",
149 +"coteau du lac": "Montérégie",
150 +"coucoucache": "Mauricie",
151 +"coulee des adolphe": "Gaspésie–Îles-de-la-Madeleine",
152 +"courcelles saint evariste": "Chaudière-Appalaches",
153 +"cowansville": "Estrie",
154 +"crabtree": "Lanaudière",
155 +"danville": "Estrie",
156 +"daveluyville": "Centre-du-Québec",
157 +"degelis": "Bas-Saint-Laurent",
158 +"deleage": "Outaouais",
159 +"delson": "Montérégie",
160 +"denholm": "Outaouais",
161 +"depot echouani": "Outaouais",
162 +"desbiens": "Saguenay–Lac-Saint-Jean",
163 +"deschaillons sur saint laurent": "Centre-du-Québec",
164 +"deschambault grondines": "Capitale-Nationale",
165 +"desjardins": "Chaudière-Appalaches",
166 +"deux montagnes": "Laurentides",
167 +"disraeli": "Chaudière-Appalaches",
168 +"dixville": "Estrie",
169 +"dolbeau mistassini": "Saguenay–Lac-Saint-Jean",
170 +"dollard des ormeaux": "Montréal",
171 +"doncaster": "Laurentides",
172 +"donnacona": "Capitale-Nationale",
173 +"dorval": "Montréal",
174 +"dosquet": "Chaudière-Appalaches",
175 +"drummondville": "Centre-du-Québec",
176 +"dudswell": "Estrie",
177 +"duhamel": "Outaouais",
178 +"duhamel ouest": "Abitibi-Témiscamingue",
179 +"dundee": "Montérégie",
180 +"dunham": "Estrie",
181 +"duparquet": "Abitibi-Témiscamingue",
182 +"dupuy": "Abitibi-Témiscamingue",
183 +"durham sud": "Centre-du-Québec",
184 +"east angus": "Estrie",
185 +"east broughton": "Chaudière-Appalaches",
186 +"east farnham": "Estrie",
187 +"east hereford": "Estrie",
188 +"eastmain": "Nord-du-Québec",
189 +"eastman": "Estrie",
190 +"eeyou istchee baie james": "Nord-du-Québec",
191 +"egan sud": "Outaouais",
192 +"elgin": "Montérégie",
193 +"entrelacs": "Lanaudière",
194 +"escuminac": "Gaspésie–Îles-de-la-Madeleine",
195 +"esprit saint": "Bas-Saint-Laurent",
196 +"essipit": "Côte-Nord",
197 +"esterel": "Laurentides",
198 +"farnham": "Estrie",
199 +"fassett": "Outaouais",
200 +"ferland et boilleau": "Saguenay–Lac-Saint-Jean",
201 +"ferme neuve": "Laurentides",
202 +"fermont": "Côte-Nord",
203 +"fleurimont": "Estrie",
204 +"forestville": "Côte-Nord",
205 +"fort coulonge": "Outaouais",
206 +"fortierville": "Centre-du-Québec",
207 +"fossambault sur le lac": "Capitale-Nationale",
208 +"frampton": "Chaudière-Appalaches",
209 +"franklin": "Montérégie",
210 +"franquelin": "Côte-Nord",
211 +"frelighsburg": "Estrie",
212 +"frontenac": "Estrie",
213 +"fugereville": "Abitibi-Témiscamingue",
214 +"gallichan": "Abitibi-Témiscamingue",
215 +"gaspe": "Gaspésie–Îles-de-la-Madeleine",
216 +"gatineau": "Outaouais",
217 +"gesgapegiag": "Gaspésie–Îles-de-la-Madeleine",
218 +"girardville": "Saguenay–Lac-Saint-Jean",
219 +"godbout": "Côte-Nord",
220 +"godmanchester": "Montérégie",
221 +"gore": "Laurentides",
222 +"gracefield": "Outaouais",
223 +"granby": "Estrie",
224 +"grand metis": "Bas-Saint-Laurent",
225 +"grand remous": "Outaouais",
226 +"grand saint esprit": "Centre-du-Québec",
227 +"grande riviere": "Gaspésie–Îles-de-la-Madeleine",
228 +"grande vallee": "Gaspésie–Îles-de-la-Madeleine",
229 +"grandes piles": "Mauricie",
230 +"greenfield park": "Montérégie",
231 +"grenville": "Laurentides",
232 +"grenville sur la rouge": "Laurentides",
233 +"gros mecatina": "Côte-Nord",
234 +"grosse ile": "Gaspésie–Îles-de-la-Madeleine",
235 +"grosses roches": "Bas-Saint-Laurent",
236 +"guerin": "Abitibi-Témiscamingue",
237 +"ham nord": "Centre-du-Québec",
238 +"ham sud": "Estrie",
239 +"hampden": "Estrie",
240 +"hampstead": "Montréal",
241 +"harrington": "Laurentides",
242 +"hatley": "Estrie",
243 +"havelock": "Montérégie",
244 +"havre saint pierre": "Côte-Nord",
245 +"hebertville": "Saguenay–Lac-Saint-Jean",
246 +"hemmingford": "Montérégie",
247 +"henryville": "Montérégie",
248 +"herouxville": "Mauricie",
249 +"hinchinbrooke": "Montérégie",
250 +"honfleur": "Chaudière-Appalaches",
251 +"hope": "Gaspésie–Îles-de-la-Madeleine",
252 +"hope town": "Gaspésie–Îles-de-la-Madeleine",
253 +"howick": "Montérégie",
254 +"huberdeau": "Laurentides",
255 +"hudson": "Montérégie",
256 +"huntingdon": "Montérégie",
257 +"ile bizard": "Montréal",
258 +"ile des soeurs": "Montréal",
259 +"inukjuak": "Nord-du-Québec",
260 +"inverness": "Centre-du-Québec",
261 +"irlande": "Chaudière-Appalaches",
262 +"ivry sur le lac": "Laurentides",
263 +"ivujivik": "Nord-du-Québec",
264 +"joliette": "Lanaudière",
265 +"jonquiere": "Saguenay–Lac-Saint-Jean",
266 +"kahnawake": "Montérégie",
267 +"kamouraska": "Bas-Saint-Laurent",
268 +"kangiqsualujjuaq": "Nord-du-Québec",
269 +"kangiqsujuaq": "Nord-du-Québec",
270 +"kangirsuk": "Nord-du-Québec",
271 +"kataskomiq": "Bas-Saint-Laurent",
272 +"kawawachikamach": "Côte-Nord",
273 +"kazabazua": "Outaouais",
274 +"kebaowek": "Abitibi-Témiscamingue",
275 +"kiamika": "Laurentides",
276 +"kingsbury": "Estrie",
277 +"kingsey falls": "Centre-du-Québec",
278 +"kinnear s mills": "Chaudière-Appalaches",
279 +"kipawa": "Abitibi-Témiscamingue",
280 +"kirkland": "Montréal",
281 +"kitigan zibi": "Outaouais",
282 +"knowlton": "Estrie",
283 +"kuujjuaq": "Nord-du-Québec",
284 +"kuujjuarapik": "Nord-du-Québec",
285 +"l ancienne lorette": "Capitale-Nationale",
286 +"l anse saint jean": "Saguenay–Lac-Saint-Jean",
287 +"l ascension": "Laurentides",
288 +"l ascension de notre seigneur": "Saguenay–Lac-Saint-Jean",
289 +"l ascension de patapedia": "Gaspésie–Îles-de-la-Madeleine",
290 +"l assomption": "Lanaudière",
291 +"l avenir": "Centre-du-Québec",
292 +"l epiphanie": "Lanaudière",
293 +"l ile bizard sainte genevieve": "Montréal",
294 +"l ile cadieux": "Montérégie",
295 +"l ile d anticosti": "Côte-Nord",
296 +"l ile dorval": "Montréal",
297 +"l ile du grand calumet": "Outaouais",
298 +"l ile perrot": "Montérégie",
299 +"l isle aux allumettes": "Outaouais",
300 +"l isle aux coudres": "Capitale-Nationale",
301 +"l isle verte": "Bas-Saint-Laurent",
302 +"l islet": "Chaudière-Appalaches",
303 +"la baie": "Saguenay–Lac-Saint-Jean",
304 +"la bostonnais": "Mauricie",
305 +"la cite limoilou": "Capitale-Nationale",
306 +"la conception": "Laurentides",
307 +"la corne": "Abitibi-Témiscamingue",
308 +"la dore": "Saguenay–Lac-Saint-Jean",
309 +"la durantaye": "Chaudière-Appalaches",
310 +"la guadeloupe": "Chaudière-Appalaches",
311 +"la haute saint charles": "Capitale-Nationale",
312 +"la macaza": "Laurentides",
313 +"la malbaie": "Capitale-Nationale",
314 +"la martre": "Gaspésie–Îles-de-la-Madeleine",
315 +"la minerve": "Laurentides",
316 +"la morandiere rochebaucourt": "Abitibi-Témiscamingue",
317 +"la motte": "Abitibi-Témiscamingue",
318 +"la patrie": "Estrie",
319 +"la peche": "Outaouais",
320 +"la pocatiere": "Bas-Saint-Laurent",
321 +"la prairie": "Montérégie",
322 +"la presentation": "Montérégie",
323 +"la redemption": "Bas-Saint-Laurent",
324 +"la reine": "Abitibi-Témiscamingue",
325 +"la romaine": "Côte-Nord",
326 +"la sarre": "Abitibi-Témiscamingue",
327 +"la trinite des monts": "Bas-Saint-Laurent",
328 +"la tuque": "Mauricie",
329 +"la visitation de l ile dupas": "Lanaudière",
330 +"la visitation de yamaska": "Centre-du-Québec",
331 +"labelle": "Laurentides",
332 +"labrecque": "Saguenay–Lac-Saint-Jean",
333 +"lac a la croix": "Bas-Saint-Laurent",
334 +"lac achouakan": "Saguenay–Lac-Saint-Jean",
335 +"lac akonapwehikan": "Laurentides",
336 +"lac alfred": "Bas-Saint-Laurent",
337 +"lac ashuapmushuan": "Saguenay–Lac-Saint-Jean",
338 +"lac au brochet": "Côte-Nord",
339 +"lac au saumon": "Bas-Saint-Laurent",
340 +"lac aux sables": "Mauricie",
341 +"lac bazinet": "Laurentides",
342 +"lac beauport": "Capitale-Nationale",
343 +"lac blanc": "Capitale-Nationale",
344 +"lac boisbouscache": "Bas-Saint-Laurent",
345 +"lac bouchette": "Saguenay–Lac-Saint-Jean",
346 +"lac boule": "Mauricie",
347 +"lac brome": "Estrie",
348 +"lac cabasta": "Lanaudière",
349 +"lac casault": "Bas-Saint-Laurent",
350 +"lac chicobi": "Abitibi-Témiscamingue",
351 +"lac croche": "Capitale-Nationale",
352 +"lac de la bidiere": "Laurentides",
353 +"lac de la maison de pierre": "Laurentides",
354 +"lac de la pomme": "Laurentides",
355 +"lac delage": "Capitale-Nationale",
356 +"lac des aigles": "Bas-Saint-Laurent",
357 +"lac des dix milles": "Lanaudière",
358 +"lac des eaux mortes": "Bas-Saint-Laurent",
359 +"lac des ecorces": "Laurentides",
360 +"lac des plages": "Outaouais",
361 +"lac des seize iles": "Laurentides",
362 +"lac despinassy": "Abitibi-Témiscamingue",
363 +"lac devenyns": "Lanaudière",
364 +"lac douaire": "Laurentides",
365 +"lac drolet": "Estrie",
366 +"lac du cerf": "Laurentides",
367 +"lac du taureau": "Lanaudière",
368 +"lac duparquet": "Abitibi-Témiscamingue",
369 +"lac edouard": "Mauricie",
370 +"lac ernest": "Laurentides",
371 +"lac etchemin": "Chaudière-Appalaches",
372 +"lac frontiere": "Chaudière-Appalaches",
373 +"lac granet": "Abitibi-Témiscamingue",
374 +"lac huron": "Bas-Saint-Laurent",
375 +"lac jacques cartier": "Capitale-Nationale",
376 +"lac jerome": "Côte-Nord",
377 +"lac john": "Côte-Nord",
378 +"lac juillet": "Côte-Nord",
379 +"lac lapeyrere": "Capitale-Nationale",
380 +"lac legendre": "Lanaudière",
381 +"lac lenotre": "Outaouais",
382 +"lac marguerite": "Laurentides",
383 +"lac masketsi": "Mauricie",
384 +"lac matapedia": "Bas-Saint-Laurent",
385 +"lac matawin": "Lanaudière",
386 +"lac megantic": "Estrie",
387 +"lac metei": "Abitibi-Témiscamingue",
388 +"lac minaki": "Lanaudière",
389 +"lac ministuk": "Saguenay–Lac-Saint-Jean",
390 +"lac moncouche": "Saguenay–Lac-Saint-Jean",
391 +"lac moselle": "Outaouais",
392 +"lac nilgaut": "Outaouais",
393 +"lac normand": "Mauricie",
394 +"lac oscar": "Laurentides",
395 +"lac pikauba": "Capitale-Nationale",
396 +"lac poulin": "Chaudière-Appalaches",
397 +"lac pythonga": "Outaouais",
398 +"lac rapide": "Outaouais",
399 +"lac saguay": "Laurentides",
400 +"lac saint joseph": "Capitale-Nationale",
401 +"lac saint paul": "Laurentides",
402 +"lac sainte marie": "Outaouais",
403 +"lac sante": "Lanaudière",
404 +"lac sergent": "Capitale-Nationale",
405 +"lac superieur": "Laurentides",
406 +"lac tremblant nord": "Laurentides",
407 +"lac vacher": "Côte-Nord",
408 +"lac wagwabika": "Laurentides",
409 +"lac walker": "Côte-Nord",
410 +"lachine": "Montréal",
411 +"lachute": "Laurentides",
412 +"lacolle": "Montérégie",
413 +"laforce": "Abitibi-Témiscamingue",
414 +"lalemant": "Saguenay–Lac-Saint-Jean",
415 +"lamarche": "Saguenay–Lac-Saint-Jean",
416 +"lambton": "Estrie",
417 +"landrienne": "Abitibi-Témiscamingue",
418 +"laniel": "Abitibi-Témiscamingue",
419 +"lanoraie": "Lanaudière",
420 +"lantier": "Laurentides",
421 +"larouche": "Saguenay–Lac-Saint-Jean",
422 +"lasalle": "Montréal",
423 +"latulipe et gaboury": "Abitibi-Témiscamingue",
424 +"launay": "Abitibi-Témiscamingue",
425 +"laurier station": "Chaudière-Appalaches",
426 +"laurierville": "Centre-du-Québec",
427 +"laval": "Laval",
428 +"lavaltrie": "Lanaudière",
429 +"laverlochere angliers": "Abitibi-Témiscamingue",
430 +"lawrenceville": "Estrie",
431 +"le plateau mont royal": "Montréal",
432 +"le sud ouest": "Montréal",
433 +"le vieux longueuil": "Montérégie",
434 +"lebel sur quevillon": "Nord-du-Québec",
435 +"leclercville": "Chaudière-Appalaches",
436 +"lefebvre": "Centre-du-Québec",
437 +"lejeune": "Bas-Saint-Laurent",
438 +"lemieux": "Centre-du-Québec",
439 +"lennoxville": "Estrie",
440 +"lery": "Montérégie",
441 +"les bergeronnes": "Côte-Nord",
442 +"les cedres": "Montérégie",
443 +"les chutes de la chaudiere est": "Chaudière-Appalaches",
444 +"les chutes de la chaudiere ouest": "Chaudière-Appalaches",
445 +"les coteaux": "Montérégie",
446 +"les eboulements": "Capitale-Nationale",
447 +"les escoumins": "Côte-Nord",
448 +"les hauteurs": "Bas-Saint-Laurent",
449 +"les iles de la madeleine": "Gaspésie–Îles-de-la-Madeleine",
450 +"les lacs du temiscamingue": "Abitibi-Témiscamingue",
451 +"les mechins": "Bas-Saint-Laurent",
452 +"les nations": "Estrie",
453 +"les rivieres": "Capitale-Nationale",
454 +"levis": "Chaudière-Appalaches",
455 +"lingwick": "Estrie",
456 +"linton": "Capitale-Nationale",
457 +"listuguj": "Gaspésie–Îles-de-la-Madeleine",
458 +"litchfield": "Outaouais",
459 +"lochaber": "Outaouais",
460 +"lochaber partie ouest": "Outaouais",
461 +"longue pointe de mingan": "Côte-Nord",
462 +"longue rive": "Côte-Nord",
463 +"longueil": "Montérégie",
464 +"longueuil": "Montérégie",
465 +"lorraine": "Laurentides",
466 +"lorrainville": "Abitibi-Témiscamingue",
467 +"lotbiniere": "Chaudière-Appalaches",
468 +"louiseville": "Mauricie",
469 +"low": "Outaouais",
470 +"lyster": "Centre-du-Québec",
471 +"macamic": "Abitibi-Témiscamingue",
472 +"macnider": "Bas-Saint-Laurent",
473 +"maddington falls": "Centre-du-Québec",
474 +"magog": "Estrie",
475 +"malartic": "Abitibi-Témiscamingue",
476 +"maliotenam": "Côte-Nord",
477 +"manawan": "Lanaudière",
478 +"mandeville": "Lanaudière",
479 +"maniwaki": "Outaouais",
480 +"manseau": "Centre-du-Québec",
481 +"mansfield et pontefract": "Outaouais",
482 +"maria": "Gaspésie–Îles-de-la-Madeleine",
483 +"maricourt": "Estrie",
484 +"marieville": "Montérégie",
485 +"marsoui": "Gaspésie–Îles-de-la-Madeleine",
486 +"marston": "Estrie",
487 +"martinville": "Estrie",
488 +"mascouche": "Lanaudière",
489 +"mashteuiatsh": "Saguenay–Lac-Saint-Jean",
490 +"maskinonge": "Mauricie",
491 +"massueville": "Montérégie",
492 +"matagami": "Nord-du-Québec",
493 +"matane": "Bas-Saint-Laurent",
494 +"matapedia": "Gaspésie–Îles-de-la-Madeleine",
495 +"matchi manitou": "Abitibi-Témiscamingue",
496 +"matimekosh": "Côte-Nord",
497 +"mayo": "Outaouais",
498 +"mcmasterville": "Montérégie",
499 +"melbourne": "Estrie",
500 +"mercier": "Montérégie",
501 +"mercier hochelaga maisonneuve": "Montréal",
502 +"messines": "Outaouais",
503 +"metabetchouan lac a la croix": "Saguenay–Lac-Saint-Jean",
504 +"metis sur mer": "Bas-Saint-Laurent",
505 +"milan": "Estrie",
506 +"mille isles": "Laurentides",
507 +"mingan": "Côte-Nord",
508 +"mirabel": "Laurentides",
509 +"mistissini": "Nord-du-Québec",
510 +"moffet": "Abitibi-Témiscamingue",
511 +"mont albert": "Gaspésie–Îles-de-la-Madeleine",
512 +"mont alexandre": "Gaspésie–Îles-de-la-Madeleine",
513 +"mont apica": "Saguenay–Lac-Saint-Jean",
514 +"mont blanc": "Laurentides",
515 +"mont carmel": "Bas-Saint-Laurent",
516 +"mont elie": "Capitale-Nationale",
517 +"mont joli": "Bas-Saint-Laurent",
518 +"mont laurier": "Laurentides",
519 +"mont royal": "Montréal",
520 +"mont saint gregoire": "Montérégie",
521 +"mont saint hilaire": "Montérégie",
522 +"mont saint michel": "Laurentides",
523 +"mont saint pierre": "Gaspésie–Îles-de-la-Madeleine",
524 +"mont tremblant": "Laurentides",
525 +"mont valin": "Saguenay–Lac-Saint-Jean",
526 +"montcalm": "Laurentides",
527 +"montcerf lytton": "Outaouais",
528 +"montebello": "Outaouais",
529 +"montmagny": "Chaudière-Appalaches",
530 +"montpellier": "Outaouais",
531 +"montreal": "Montréal",
532 +"montreal est": "Montréal",
533 +"montreal nord": "Montréal",
534 +"montreal ouest": "Montréal",
535 +"morin heights": "Laurentides",
536 +"mtl": "Montréal",
537 +"mulgrave et derry": "Outaouais",
538 +"murdochville": "Gaspésie–Îles-de-la-Madeleine",
539 +"namur": "Outaouais",
540 +"nantes": "Estrie",
541 +"napierville": "Montérégie",
542 +"natashquan": "Côte-Nord",
543 +"nedelec": "Abitibi-Témiscamingue",
544 +"nemaska": "Nord-du-Québec",
545 +"neuville": "Capitale-Nationale",
546 +"new carlisle": "Gaspésie–Îles-de-la-Madeleine",
547 +"new richmond": "Gaspésie–Îles-de-la-Madeleine",
548 +"newport": "Estrie",
549 +"nicolet": "Centre-du-Québec",
550 +"nominingue": "Laurentides",
551 +"normandin": "Saguenay–Lac-Saint-Jean",
552 +"normetal": "Abitibi-Témiscamingue",
553 +"north hatley": "Estrie",
554 +"notre dame auxiliatrice de buckland": "Chaudière-Appalaches",
555 +"notre dame de bonsecours": "Outaouais",
556 +"notre dame de ham": "Centre-du-Québec",
557 +"notre dame de l ile perrot": "Montérégie",
558 +"notre dame de la merci": "Lanaudière",
559 +"notre dame de la paix": "Outaouais",
560 +"notre dame de la salette": "Outaouais",
561 +"notre dame de lorette": "Saguenay–Lac-Saint-Jean",
562 +"notre dame de montauban": "Mauricie",
563 +"notre dame de pontmain": "Laurentides",
564 +"notre dame de stanbridge": "Estrie",
565 +"notre dame des anges": "Capitale-Nationale",
566 +"notre dame des bois": "Estrie",
567 +"notre dame des monts": "Capitale-Nationale",
568 +"notre dame des neiges": "Bas-Saint-Laurent",
569 +"notre dame des pins": "Chaudière-Appalaches",
570 +"notre dame des prairies": "Lanaudière",
571 +"notre dame des sept douleurs": "Bas-Saint-Laurent",
572 +"notre dame du bon conseil": "Centre-du-Québec",
573 +"notre dame du laus": "Laurentides",
574 +"notre dame du mont carmel": "Mauricie",
575 +"notre dame du nord": "Abitibi-Témiscamingue",
576 +"notre dame du portage": "Bas-Saint-Laurent",
577 +"notre dame du rosaire": "Chaudière-Appalaches",
578 +"notre dame du sacre coeur d issoudun": "Chaudière-Appalaches",
579 +"nouvelle": "Gaspésie–Îles-de-la-Madeleine",
580 +"noyan": "Montérégie",
581 +"obedjiwan": "Mauricie",
582 +"odanak": "Centre-du-Québec",
583 +"ogden": "Estrie",
584 +"oka": "Laurentides",
585 +"orford": "Estrie",
586 +"ormstown": "Montérégie",
587 +"otter lake": "Outaouais",
588 +"otterburn park": "Montérégie",
589 +"ouje bougoumou": "Nord-du-Québec",
590 +"outremont": "Montréal",
591 +"p a t": "Montréal",
592 +"packington": "Bas-Saint-Laurent",
593 +"padoue": "Bas-Saint-Laurent",
594 +"palmarolle": "Abitibi-Témiscamingue",
595 +"papineauville": "Outaouais",
596 +"parisville": "Centre-du-Québec",
597 +"paspebiac": "Gaspésie–Îles-de-la-Madeleine",
598 +"passes dangereuses": "Saguenay–Lac-Saint-Jean",
599 +"perce": "Gaspésie–Îles-de-la-Madeleine",
600 +"peribonka": "Saguenay–Lac-Saint-Jean",
601 +"pessamit": "Côte-Nord",
602 +"petit lac sainte anne": "Bas-Saint-Laurent",
603 +"petit mecatina": "Côte-Nord",
604 +"petit saguenay": "Saguenay–Lac-Saint-Jean",
605 +"petite riviere saint francois": "Capitale-Nationale",
606 +"petite vallee": "Gaspésie–Îles-de-la-Madeleine",
607 +"picard": "Bas-Saint-Laurent",
608 +"piedmont": "Laurentides",
609 +"pierrefonds": "Montréal",
610 +"pierrefonds roxboro": "Montréal",
611 +"pierreville": "Centre-du-Québec",
612 +"pike river": "Estrie",
613 +"pikogan": "Abitibi-Témiscamingue",
614 +"pincourt": "Montérégie",
615 +"piopolis": "Estrie",
616 +"plaisance": "Outaouais",
617 +"plessisville": "Centre-du-Québec",
618 +"pohenegamook": "Bas-Saint-Laurent",
619 +"pointe a la croix": "Gaspésie–Îles-de-la-Madeleine",
620 +"pointe aux outardes": "Côte-Nord",
621 +"pointe aux trembles": "Montréal",
622 +"pointe calumet": "Laurentides",
623 +"pointe claire": "Montréal",
624 +"pointe des cascades": "Montérégie",
625 +"pointe fortune": "Montérégie",
626 +"pointe lebel": "Côte-Nord",
627 +"pont rouge": "Capitale-Nationale",
628 +"pontiac": "Outaouais",
629 +"port cartier": "Côte-Nord",
630 +"port daniel gascons": "Gaspésie–Îles-de-la-Madeleine",
631 +"portage du fort": "Outaouais",
632 +"portneuf": "Capitale-Nationale",
633 +"portneuf sur mer": "Côte-Nord",
634 +"potton": "Estrie",
635 +"poularies": "Abitibi-Témiscamingue",
636 +"preissac": "Abitibi-Témiscamingue",
637 +"prevost": "Laurentides",
638 +"price": "Bas-Saint-Laurent",
639 +"princeville": "Centre-du-Québec",
640 +"puvirnituq": "Nord-du-Québec",
641 +"quaqtaq": "Nord-du-Québec",
642 +"quebec": "Capitale-Nationale",
643 +"quebec city": "Capitale-Nationale",
644 +"racine": "Estrie",
645 +"ragueneau": "Côte-Nord",
646 +"rapide danseur": "Abitibi-Témiscamingue",
647 +"rapides des joachims": "Outaouais",
648 +"rawdon": "Lanaudière",
649 +"remigny": "Abitibi-Témiscamingue",
650 +"repentigny": "Lanaudière",
651 +"reservoir dozois": "Abitibi-Témiscamingue",
652 +"richelieu": "Montérégie",
653 +"richmond": "Estrie",
654 +"rigaud": "Montérégie",
655 +"rimouski": "Bas-Saint-Laurent",
656 +"ripon": "Outaouais",
657 +"ristigouche sud est": "Gaspésie–Îles-de-la-Madeleine",
658 +"riviere a claude": "Gaspésie–Îles-de-la-Madeleine",
659 +"riviere a pierre": "Capitale-Nationale",
660 +"riviere au tonnerre": "Côte-Nord",
661 +"riviere aux outardes": "Côte-Nord",
662 +"riviere beaudette": "Montérégie",
663 +"riviere bleue": "Bas-Saint-Laurent",
664 +"riviere bonaventure": "Gaspésie–Îles-de-la-Madeleine",
665 +"riviere bonjour": "Bas-Saint-Laurent",
666 +"riviere de la savane": "Mauricie",
667 +"riviere des prairies pointe aux trembles": "Montréal",
668 +"riviere du loup": "Bas-Saint-Laurent",
669 +"riviere eternite": "Saguenay–Lac-Saint-Jean",
670 +"riviere heva": "Abitibi-Témiscamingue",
671 +"riviere koksoak": "Nord-du-Québec",
672 +"riviere mistassini": "Saguenay–Lac-Saint-Jean",
673 +"riviere mouchalagane": "Côte-Nord",
674 +"riviere nipissis": "Côte-Nord",
675 +"riviere nouvelle": "Gaspésie–Îles-de-la-Madeleine",
676 +"riviere ojima": "Abitibi-Témiscamingue",
677 +"riviere ouelle": "Bas-Saint-Laurent",
678 +"riviere patapedia est": "Bas-Saint-Laurent",
679 +"riviere rouge": "Laurentides",
680 +"riviere saint jean": "Côte-Nord",
681 +"riviere vaseuse": "Bas-Saint-Laurent",
682 +"roberval": "Saguenay–Lac-Saint-Jean",
683 +"roquemaure": "Abitibi-Témiscamingue",
684 +"rosemere": "Laurentides",
685 +"rosemont la petite patrie": "Montréal",
686 +"rougemont": "Montérégie",
687 +"routhierville": "Bas-Saint-Laurent",
688 +"rouyn noranda": "Abitibi-Témiscamingue",
689 +"roxboro": "Montréal",
690 +"roxton": "Montérégie",
691 +"roxton falls": "Montérégie",
692 +"roxton pond": "Estrie",
693 +"ruisseau des mineurs": "Bas-Saint-Laurent",
694 +"ruisseau ferguson": "Gaspésie–Îles-de-la-Madeleine",
695 +"sacre coeur": "Côte-Nord",
696 +"sacre coeur de jesus": "Chaudière-Appalaches",
697 +"sagard": "Capitale-Nationale",
698 +"saguenay": "Saguenay–Lac-Saint-Jean",
699 +"saint adalbert": "Chaudière-Appalaches",
700 +"saint adelme": "Bas-Saint-Laurent",
701 +"saint adelphe": "Mauricie",
702 +"saint adolphe d howard": "Laurentides",
703 +"saint adrien": "Estrie",
704 +"saint adrien d irlande": "Chaudière-Appalaches",
705 +"saint agapit": "Chaudière-Appalaches",
706 +"saint aime": "Montérégie",
707 +"saint aime des lacs": "Capitale-Nationale",
708 +"saint aime du lac des iles": "Laurentides",
709 +"saint alban": "Capitale-Nationale",
710 +"saint albert": "Centre-du-Québec",
711 +"saint alexandre": "Montérégie",
712 +"saint alexandre de kamouraska": "Bas-Saint-Laurent",
713 +"saint alexandre des lacs": "Bas-Saint-Laurent",
714 +"saint alexis": "Lanaudière",
715 +"saint alexis de matapedia": "Gaspésie–Îles-de-la-Madeleine",
716 +"saint alexis des monts": "Mauricie",
717 +"saint alfred": "Chaudière-Appalaches",
718 +"saint alphonse": "Gaspésie–Îles-de-la-Madeleine",
719 +"saint alphonse de granby": "Estrie",
720 +"saint alphonse rodriguez": "Lanaudière",
721 +"saint amable": "Montérégie",
722 +"saint ambroise": "Saguenay–Lac-Saint-Jean",
723 +"saint ambroise de kildare": "Lanaudière",
724 +"saint anaclet de lessard": "Bas-Saint-Laurent",
725 +"saint andre avellin": "Outaouais",
726 +"saint andre d argenteuil": "Laurentides",
727 +"saint andre de kamouraska": "Bas-Saint-Laurent",
728 +"saint andre de restigouche": "Gaspésie–Îles-de-la-Madeleine",
729 +"saint andre du lac saint jean": "Saguenay–Lac-Saint-Jean",
730 +"saint anicet": "Montérégie",
731 +"saint anselme": "Chaudière-Appalaches",
732 +"saint antoine de l isle aux grues": "Chaudière-Appalaches",
733 +"saint antoine de tilly": "Chaudière-Appalaches",
734 +"saint antoine sur richelieu": "Montérégie",
735 +"saint antonin": "Bas-Saint-Laurent",
736 +"saint apollinaire": "Chaudière-Appalaches",
737 +"saint armand": "Estrie",
738 +"saint arsene": "Bas-Saint-Laurent",
739 +"saint athanase": "Bas-Saint-Laurent",
740 +"saint aubert": "Chaudière-Appalaches",
741 +"saint augustin de desmaures": "Capitale-Nationale",
742 +"saint augustin de woburn": "Estrie",
743 +"saint barnabe": "Mauricie",
744 +"saint barnabe sud": "Montérégie",
745 +"saint barthelemy": "Lanaudière",
746 +"saint basile": "Capitale-Nationale",
747 +"saint basile le grand": "Montérégie",
748 +"saint benjamin": "Chaudière-Appalaches",
749 +"saint benoit du lac": "Estrie",
750 +"saint benoit labre": "Chaudière-Appalaches",
751 +"saint bernard": "Chaudière-Appalaches",
752 +"saint bernard de lacolle": "Montérégie",
753 +"saint bernard de michaudville": "Montérégie",
754 +"saint blaise sur richelieu": "Montérégie",
755 +"saint bonaventure": "Centre-du-Québec",
756 +"saint boniface": "Mauricie",
757 +"saint bruno de guigues": "Abitibi-Témiscamingue",
758 +"saint bruno de kamouraska": "Bas-Saint-Laurent",
759 +"saint bruno de montarville": "Montérégie",
760 +"saint calixte": "Lanaudière",
761 +"saint camille": "Estrie",
762 +"saint camille de lellis": "Chaudière-Appalaches",
763 +"saint casimir": "Capitale-Nationale",
764 +"saint celestin": "Centre-du-Québec",
765 +"saint cesaire": "Montérégie",
766 +"saint charles borromee": "Lanaudière",
767 +"saint charles de bellechasse": "Chaudière-Appalaches",
768 +"saint charles de bourget": "Saguenay–Lac-Saint-Jean",
769 +"saint charles garnier": "Bas-Saint-Laurent",
770 +"saint charles sur richelieu": "Montérégie",
771 +"saint christophe d arthabaska": "Centre-du-Québec",
772 +"saint chrysostome": "Montérégie",
773 +"saint claude": "Estrie",
774 +"saint clement": "Bas-Saint-Laurent",
775 +"saint cleophas": "Bas-Saint-Laurent",
776 +"saint cleophas de brandon": "Lanaudière",
777 +"saint clet": "Montérégie",
778 +"saint colomban": "Laurentides",
779 +"saint come": "Lanaudière",
780 +"saint come liniere": "Chaudière-Appalaches",
781 +"saint constant": "Montérégie",
782 +"saint cuthbert": "Lanaudière",
783 +"saint cyprien de napierville": "Montérégie",
784 +"saint cyrille de lessard": "Chaudière-Appalaches",
785 +"saint cyrille de wendover": "Centre-du-Québec",
786 +"saint damase de l islet": "Chaudière-Appalaches",
787 +"saint damien": "Lanaudière",
788 +"saint damien de buckland": "Chaudière-Appalaches",
789 +"saint david": "Montérégie",
790 +"saint david de falardeau": "Saguenay–Lac-Saint-Jean",
791 +"saint denis de brompton": "Estrie",
792 +"saint denis de la bouteillerie": "Bas-Saint-Laurent",
793 +"saint denis sur richelieu": "Montérégie",
794 +"saint didace": "Lanaudière",
795 +"saint dominique": "Montérégie",
796 +"saint dominique du rosaire": "Abitibi-Témiscamingue",
797 +"saint edmond de grantham": "Centre-du-Québec",
798 +"saint edmond les plaines": "Saguenay–Lac-Saint-Jean",
799 +"saint edouard": "Montérégie",
800 +"saint edouard de fabre": "Abitibi-Témiscamingue",
801 +"saint edouard de lotbiniere": "Chaudière-Appalaches",
802 +"saint edouard de maskinonge": "Mauricie",
803 +"saint elie de caxton": "Mauricie",
804 +"saint eloi": "Bas-Saint-Laurent",
805 +"saint elphege": "Centre-du-Québec",
806 +"saint elzear de temiscouata": "Bas-Saint-Laurent",
807 +"saint emile de suffolk": "Outaouais",
808 +"saint ephrem de beauce": "Chaudière-Appalaches",
809 +"saint epiphane": "Bas-Saint-Laurent",
810 +"saint esprit": "Lanaudière",
811 +"saint etienne de beauharnois": "Montérégie",
812 +"saint etienne de bolton": "Estrie",
813 +"saint etienne des gres": "Mauricie",
814 +"saint eugene": "Centre-du-Québec",
815 +"saint eugene d argentenay": "Saguenay–Lac-Saint-Jean",
816 +"saint eugene de guigues": "Abitibi-Témiscamingue",
817 +"saint eugene de ladriere": "Bas-Saint-Laurent",
818 +"saint eusebe": "Bas-Saint-Laurent",
819 +"saint eustache": "Laurentides",
820 +"saint fabien": "Bas-Saint-Laurent",
821 +"saint fabien de panet": "Chaudière-Appalaches",
822 +"saint felicien": "Saguenay–Lac-Saint-Jean",
823 +"saint felix d otis": "Saguenay–Lac-Saint-Jean",
824 +"saint felix de kingsey": "Centre-du-Québec",
825 +"saint felix de valois": "Lanaudière",
826 +"saint ferdinand": "Centre-du-Québec",
827 +"saint ferreol les neiges": "Capitale-Nationale",
828 +"saint flavien": "Chaudière-Appalaches",
829 +"saint fortunat": "Chaudière-Appalaches",
830 +"saint francois d assise": "Gaspésie–Îles-de-la-Madeleine",
831 +"saint francois de l ile d orleans": "Capitale-Nationale",
832 +"saint francois de la riviere du sud": "Chaudière-Appalaches",
833 +"saint francois de sales": "Saguenay–Lac-Saint-Jean",
834 +"saint francois du lac": "Centre-du-Québec",
835 +"saint francois xavier de brompton": "Estrie",
836 +"saint francois xavier de viger": "Bas-Saint-Laurent",
837 +"saint frederic": "Chaudière-Appalaches",
838 +"saint fulgence": "Saguenay–Lac-Saint-Jean",
839 +"saint gabriel": "Lanaudière",
840 +"saint gabriel de brandon": "Lanaudière",
841 +"saint gabriel de rimouski": "Bas-Saint-Laurent",
842 +"saint gabriel de valcartier": "Capitale-Nationale",
843 +"saint gabriel lalemant": "Bas-Saint-Laurent",
844 +"saint gedeon": "Saguenay–Lac-Saint-Jean",
845 +"saint gedeon de beauce": "Chaudière-Appalaches",
846 +"saint georges": "Chaudière-Appalaches",
847 +"saint georges de windsor": "Estrie",
848 +"saint gerard majella": "Montérégie",
849 +"saint germain de grantham": "Centre-du-Québec",
850 +"saint germain de kamouraska": "Bas-Saint-Laurent",
851 +"saint gervais": "Chaudière-Appalaches",
852 +"saint gilbert": "Capitale-Nationale",
853 +"saint gilles": "Chaudière-Appalaches",
854 +"saint godefroi": "Gaspésie–Îles-de-la-Madeleine",
855 +"saint guillaume": "Centre-du-Québec",
856 +"saint guillaume nord": "Lanaudière",
857 +"saint henri": "Chaudière-Appalaches",
858 +"saint henri de taillon": "Saguenay–Lac-Saint-Jean",
859 +"saint hermenegilde": "Estrie",
860 +"saint hilaire de dorset": "Chaudière-Appalaches",
861 +"saint hilarion": "Capitale-Nationale",
862 +"saint hippolyte": "Laurentides",
863 +"saint honore": "Saguenay–Lac-Saint-Jean",
864 +"saint honore de shenley": "Chaudière-Appalaches",
865 +"saint honore de temiscouata": "Bas-Saint-Laurent",
866 +"saint hubert": "Montérégie",
867 +"saint hubert de riviere du loup": "Bas-Saint-Laurent",
868 +"saint hugues": "Montérégie",
869 +"saint hyacinthe": "Montérégie",
870 +"saint ignace de loyola": "Lanaudière",
871 +"saint ignace de stanbridge": "Estrie",
872 +"saint irenee": "Capitale-Nationale",
873 +"saint isidore de clifton": "Estrie",
874 +"saint jacques": "Lanaudière",
875 +"saint jacques de leeds": "Chaudière-Appalaches",
876 +"saint jacques le majeur de wolfestown": "Chaudière-Appalaches",
877 +"saint jacques le mineur": "Montérégie",
878 +"saint janvier de joly": "Chaudière-Appalaches",
879 +"saint jean baptiste": "Montérégie",
880 +"saint jean de brebeuf": "Chaudière-Appalaches",
881 +"saint jean de cherbourg": "Bas-Saint-Laurent",
882 +"saint jean de dieu": "Bas-Saint-Laurent",
883 +"saint jean de l ile d orleans": "Capitale-Nationale",
884 +"saint jean de la lande": "Bas-Saint-Laurent",
885 +"saint jean de matha": "Lanaudière",
886 +"saint jean port joli": "Chaudière-Appalaches",
887 +"saint jean sur richelieu": "Montérégie",
888 +"saint jerome": "Laurentides",
889 +"saint joachim": "Capitale-Nationale",
890 +"saint joachim de shefford": "Estrie",
891 +"saint joseph de beauce": "Chaudière-Appalaches",
892 +"saint joseph de coleraine": "Chaudière-Appalaches",
893 +"saint joseph de kamouraska": "Bas-Saint-Laurent",
894 +"saint joseph de lepage": "Bas-Saint-Laurent",
895 +"saint joseph de sorel": "Montérégie",
896 +"saint joseph des erables": "Chaudière-Appalaches",
897 +"saint joseph du lac": "Laurentides",
898 +"saint jude": "Montérégie",
899 +"saint jules": "Chaudière-Appalaches",
900 +"saint julien": "Chaudière-Appalaches",
901 +"saint just de bretenieres": "Chaudière-Appalaches",
902 +"saint juste du lac": "Bas-Saint-Laurent",
903 +"saint justin": "Mauricie",
904 +"saint lambert": "Montérégie",
905 +"saint lambert de lauzon": "Chaudière-Appalaches",
906 +"saint laurent": "Montréal",
907 +"saint laurent de l ile d orleans": "Capitale-Nationale",
908 +"saint lazare": "Montérégie",
909 +"saint lazare de bellechasse": "Chaudière-Appalaches",
910 +"saint leandre": "Bas-Saint-Laurent",
911 +"saint leon de standon": "Chaudière-Appalaches",
912 +"saint leonard": "Montréal",
913 +"saint leonard d aston": "Centre-du-Québec",
914 +"saint leonard de portneuf": "Capitale-Nationale",
915 +"saint liboire": "Montérégie",
916 +"saint liguori": "Lanaudière",
917 +"saint lin laurentides": "Lanaudière",
918 +"saint louis": "Montérégie",
919 +"saint louis de blandford": "Centre-du-Québec",
920 +"saint louis de gonzague du cap tourmente": "Capitale-Nationale",
921 +"saint louis du ha! ha!": "Bas-Saint-Laurent",
922 +"saint luc de bellechasse": "Chaudière-Appalaches",
923 +"saint luc de vincennes": "Mauricie",
924 +"saint lucien": "Centre-du-Québec",
925 +"saint ludger": "Estrie",
926 +"saint ludger de milot": "Saguenay–Lac-Saint-Jean",
927 +"saint magloire": "Chaudière-Appalaches",
928 +"saint majorique de grantham": "Centre-du-Québec",
929 +"saint malachie": "Chaudière-Appalaches",
930 +"saint malo": "Estrie",
931 +"saint marc de figuery": "Abitibi-Témiscamingue",
932 +"saint marc des carrieres": "Capitale-Nationale",
933 +"saint marc du lac long": "Bas-Saint-Laurent",
934 +"saint marc sur richelieu": "Montérégie",
935 +"saint marcel": "Chaudière-Appalaches",
936 +"saint marcel de richelieu": "Montérégie",
937 +"saint marcellin": "Bas-Saint-Laurent",
938 +"saint martin": "Chaudière-Appalaches",
939 +"saint mathias sur richelieu": "Montérégie",
940 +"saint mathieu": "Montérégie",
941 +"saint mathieu d harricana": "Abitibi-Témiscamingue",
942 +"saint mathieu de beloeil": "Montérégie",
943 +"saint mathieu de rioux": "Bas-Saint-Laurent",
944 +"saint mathieu du parc": "Mauricie",
945 +"saint maurice": "Mauricie",
946 +"saint maxime du mont louis": "Gaspésie–Îles-de-la-Madeleine",
947 +"saint medard": "Bas-Saint-Laurent",
948 +"saint michel": "Montérégie",
949 +"saint michel de bellechasse": "Chaudière-Appalaches",
950 +"saint michel des saints": "Lanaudière",
951 +"saint michel du squatec": "Bas-Saint-Laurent",
952 +"saint modeste": "Bas-Saint-Laurent",
953 +"saint moise": "Bas-Saint-Laurent",
954 +"saint narcisse": "Mauricie",
955 +"saint narcisse de beaurivage": "Chaudière-Appalaches",
956 +"saint narcisse de rimouski": "Bas-Saint-Laurent",
957 +"saint nazaire": "Saguenay–Lac-Saint-Jean",
958 +"saint nazaire d acton": "Montérégie",
959 +"saint nazaire de dorchester": "Chaudière-Appalaches",
960 +"saint neree de bellechasse": "Chaudière-Appalaches",
961 +"saint noel": "Bas-Saint-Laurent",
962 +"saint norbert": "Lanaudière",
963 +"saint norbert d arthabaska": "Centre-du-Québec",
964 +"saint octave de metis": "Bas-Saint-Laurent",
965 +"saint odilon de cranbourne": "Chaudière-Appalaches",
966 +"saint omer": "Chaudière-Appalaches",
967 +"saint ours": "Montérégie",
968 +"saint pacome": "Bas-Saint-Laurent",
969 +"saint pamphile": "Chaudière-Appalaches",
970 +"saint pascal": "Bas-Saint-Laurent",
971 +"saint patrice de beaurivage": "Chaudière-Appalaches",
972 +"saint patrice de sherrington": "Montérégie",
973 +"saint paul": "Lanaudière",
974 +"saint paul d abbotsford": "Montérégie",
975 +"saint paul de l ile aux noix": "Montérégie",
976 +"saint paul de la croix": "Bas-Saint-Laurent",
977 +"saint paul de montminy": "Chaudière-Appalaches",
978 +"saint paulin": "Mauricie",
979 +"saint philemon": "Chaudière-Appalaches",
980 +"saint philibert": "Chaudière-Appalaches",
981 +"saint philippe": "Montérégie",
982 +"saint philippe de neri": "Bas-Saint-Laurent",
983 +"saint pie": "Montérégie",
984 +"saint pie de guire": "Centre-du-Québec",
985 +"saint pierre": "Lanaudière",
986 +"saint pierre baptiste": "Centre-du-Québec",
987 +"saint pierre de broughton": "Chaudière-Appalaches",
988 +"saint pierre de l ile d orleans": "Capitale-Nationale",
989 +"saint pierre de la riviere du sud": "Chaudière-Appalaches",
990 +"saint pierre de lamy": "Bas-Saint-Laurent",
991 +"saint pierre les becquets": "Centre-du-Québec",
992 +"saint placide": "Laurentides",
993 +"saint polycarpe": "Montérégie",
994 +"saint prime": "Saguenay–Lac-Saint-Jean",
995 +"saint prosper": "Chaudière-Appalaches",
996 +"saint prosper de champlain": "Mauricie",
997 +"saint raphael": "Chaudière-Appalaches",
998 +"saint raymond": "Capitale-Nationale",
999 +"saint remi": "Montérégie",
1000 +"saint remi de tingwick": "Centre-du-Québec",
1001 +"saint rene": "Chaudière-Appalaches",
1002 +"saint rene de matane": "Bas-Saint-Laurent",
1003 +"saint robert": "Montérégie",
1004 +"saint robert bellarmin": "Estrie",
1005 +"saint roch de l achigan": "Lanaudière",
1006 +"saint roch de mekinac": "Mauricie",
1007 +"saint roch de richelieu": "Montérégie",
1008 +"saint roch des aulnaies": "Chaudière-Appalaches",
1009 +"saint roch ouest": "Lanaudière",
1010 +"saint romain": "Estrie",
1011 +"saint rosaire": "Centre-du-Québec",
1012 +"saint samuel": "Centre-du-Québec",
1013 +"saint sauveur": "Laurentides",
1014 +"saint severe": "Mauricie",
1015 +"saint simon": "Montérégie",
1016 +"saint simon de rimouski": "Bas-Saint-Laurent",
1017 +"saint simon les mines": "Chaudière-Appalaches",
1018 +"saint sixte": "Outaouais",
1019 +"saint stanislas de kostka": "Montérégie",
1020 +"saint sulpice": "Lanaudière",
1021 +"saint sylvere": "Centre-du-Québec",
1022 +"saint sylvestre": "Chaudière-Appalaches",
1023 +"saint telesphore": "Montérégie",
1024 +"saint tharcisius": "Bas-Saint-Laurent",
1025 +"saint theodore d acton": "Montérégie",
1026 +"saint theophile": "Chaudière-Appalaches",
1027 +"saint thomas": "Lanaudière",
1028 +"saint thomas didyme": "Saguenay–Lac-Saint-Jean",
1029 +"saint thuribe": "Capitale-Nationale",
1030 +"saint tite": "Mauricie",
1031 +"saint tite des caps": "Capitale-Nationale",
1032 +"saint ubalde": "Capitale-Nationale",
1033 +"saint ulric": "Bas-Saint-Laurent",
1034 +"saint urbain": "Capitale-Nationale",
1035 +"saint urbain premier": "Montérégie",
1036 +"saint valentin": "Montérégie",
1037 +"saint valere": "Centre-du-Québec",
1038 +"saint valerien": "Bas-Saint-Laurent",
1039 +"saint valerien de milton": "Montérégie",
1040 +"saint vallier": "Chaudière-Appalaches",
1041 +"saint venant de paquette": "Estrie",
1042 +"saint vianney": "Bas-Saint-Laurent",
1043 +"saint victor": "Chaudière-Appalaches",
1044 +"saint wenceslas": "Centre-du-Québec",
1045 +"saint zacharie": "Chaudière-Appalaches",
1046 +"saint zenon": "Lanaudière",
1047 +"saint zenon du lac humqui": "Bas-Saint-Laurent",
1048 +"saint zephirin de courval": "Centre-du-Québec",
1049 +"saint zotique": "Montérégie",
1050 +"sainte adele": "Laurentides",
1051 +"sainte agathe de lotbiniere": "Chaudière-Appalaches",
1052 +"sainte agathe des monts": "Laurentides",
1053 +"sainte angele de merici": "Bas-Saint-Laurent",
1054 +"sainte angele de monnoir": "Montérégie",
1055 +"sainte angele de premont": "Mauricie",
1056 +"sainte anne de beaupre": "Capitale-Nationale",
1057 +"sainte anne de bellevue": "Montréal",
1058 +"sainte anne de la perade": "Mauricie",
1059 +"sainte anne de la rochelle": "Estrie",
1060 +"sainte anne de sabrevois": "Montérégie",
1061 +"sainte anne de sorel": "Montérégie",
1062 +"sainte anne des lacs": "Laurentides",
1063 +"sainte anne des monts": "Gaspésie–Îles-de-la-Madeleine",
1064 +"sainte anne des plaines": "Laurentides",
1065 +"sainte anne du lac": "Laurentides",
1066 +"sainte apolline de patton": "Chaudière-Appalaches",
1067 +"sainte aurelie": "Chaudière-Appalaches",
1068 +"sainte barbe": "Montérégie",
1069 +"sainte beatrix": "Lanaudière",
1070 +"sainte brigide d iberville": "Montérégie",
1071 +"sainte brigitte de laval": "Capitale-Nationale",
1072 +"sainte brigitte des saults": "Centre-du-Québec",
1073 +"sainte catherine": "Montérégie",
1074 +"sainte catherine de hatley": "Estrie",
1075 +"sainte catherine de la jacques cartier": "Capitale-Nationale",
1076 +"sainte cecile de levrard": "Centre-du-Québec",
1077 +"sainte cecile de milton": "Estrie",
1078 +"sainte cecile de whitton": "Estrie",
1079 +"sainte christine": "Montérégie",
1080 +"sainte christine d auvergne": "Capitale-Nationale",
1081 +"sainte claire": "Chaudière-Appalaches",
1082 +"sainte clotilde": "Montérégie",
1083 +"sainte clotilde de beauce": "Chaudière-Appalaches",
1084 +"sainte clotilde de horton": "Centre-du-Québec",
1085 +"sainte croix": "Chaudière-Appalaches",
1086 +"sainte edwidge de clifton": "Estrie",
1087 +"sainte elisabeth": "Lanaudière",
1088 +"sainte elisabeth de proulx": "Saguenay–Lac-Saint-Jean",
1089 +"sainte elizabeth de warwick": "Centre-du-Québec",
1090 +"sainte emelie de l energie": "Lanaudière",
1091 +"sainte eulalie": "Centre-du-Québec",
1092 +"sainte euphemie sur riviere du sud": "Chaudière-Appalaches",
1093 +"sainte famille de l ile d orleans": "Capitale-Nationale",
1094 +"sainte flavie": "Bas-Saint-Laurent",
1095 +"sainte florence": "Bas-Saint-Laurent",
1096 +"sainte foy sillery cap rouge": "Capitale-Nationale",
1097 +"sainte genevieve de batiscan": "Mauricie",
1098 +"sainte genevieve de berthier": "Lanaudière",
1099 +"sainte germaine boule": "Abitibi-Témiscamingue",
1100 +"sainte gertrude manneville": "Abitibi-Témiscamingue",
1101 +"sainte hedwidge": "Saguenay–Lac-Saint-Jean",
1102 +"sainte helene de bagot": "Montérégie",
1103 +"sainte helene de chester": "Centre-du-Québec",
1104 +"sainte helene de kamouraska": "Bas-Saint-Laurent",
1105 +"sainte helene de mancebourg": "Abitibi-Témiscamingue",
1106 +"sainte henedine": "Chaudière-Appalaches",
1107 +"sainte irene": "Bas-Saint-Laurent",
1108 +"sainte jeanne d arc": "Saguenay–Lac-Saint-Jean",
1109 +"sainte jeanne d arc de la mitis": "Bas-Saint-Laurent",
1110 +"sainte julie": "Montérégie",
1111 +"sainte julienne": "Lanaudière",
1112 +"sainte justine": "Chaudière-Appalaches",
1113 +"sainte justine de newton": "Montérégie",
1114 +"sainte louise": "Chaudière-Appalaches",
1115 +"sainte luce": "Bas-Saint-Laurent",
1116 +"sainte lucie de beauregard": "Chaudière-Appalaches",
1117 +"sainte lucie des laurentides": "Laurentides",
1118 +"sainte madeleine": "Montérégie",
1119 +"sainte madeleine de la riviere madeleine": "Gaspésie–Îles-de-la-Madeleine",
1120 +"sainte marcelline de kildare": "Lanaudière",
1121 +"sainte marguerite": "Chaudière-Appalaches",
1122 +"sainte marguerite du lac masson": "Laurentides",
1123 +"sainte marguerite marie": "Bas-Saint-Laurent",
1124 +"sainte marie": "Chaudière-Appalaches",
1125 +"sainte marie de blandford": "Centre-du-Québec",
1126 +"sainte marie madeleine": "Montérégie",
1127 +"sainte marie salome": "Lanaudière",
1128 +"sainte marthe": "Montérégie",
1129 +"sainte marthe sur le lac": "Laurentides",
1130 +"sainte martine": "Montérégie",
1131 +"sainte melanie": "Lanaudière",
1132 +"sainte paule": "Bas-Saint-Laurent",
1133 +"sainte petronille": "Capitale-Nationale",
1134 +"sainte praxede": "Chaudière-Appalaches",
1135 +"sainte rita": "Bas-Saint-Laurent",
1136 +"sainte rose de watford": "Chaudière-Appalaches",
1137 +"sainte rose du nord": "Saguenay–Lac-Saint-Jean",
1138 +"sainte seraphine": "Centre-du-Québec",
1139 +"sainte sophie": "Laurentides",
1140 +"sainte sophie d halifax": "Centre-du-Québec",
1141 +"sainte sophie de levrard": "Centre-du-Québec",
1142 +"sainte thecle": "Mauricie",
1143 +"sainte therese": "Laurentides",
1144 +"sainte therese de gaspe": "Gaspésie–Îles-de-la-Madeleine",
1145 +"sainte therese de la gatineau": "Outaouais",
1146 +"sainte ursule": "Mauricie",
1147 +"sainte victoire de sorel": "Montérégie",
1148 +"saints anges": "Chaudière-Appalaches",
1149 +"saints martyrs canadiens": "Centre-du-Québec",
1150 +"salaberry de valleyfield": "Montérégie",
1151 +"salluit": "Nord-du-Québec",
1152 +"sault au cochon": "Capitale-Nationale",
1153 +"sayabec": "Bas-Saint-Laurent",
1154 +"schefferville": "Côte-Nord",
1155 +"scotstown": "Estrie",
1156 +"scott": "Chaudière-Appalaches",
1157 +"senneterre": "Abitibi-Témiscamingue",
1158 +"senneville": "Montréal",
1159 +"sept iles": "Côte-Nord",
1160 +"shannon": "Capitale-Nationale",
1161 +"shawinigan": "Mauricie",
1162 +"shawville": "Outaouais",
1163 +"sheenboro": "Outaouais",
1164 +"shefford": "Estrie",
1165 +"sherbrooke": "Estrie",
1166 +"shigawake": "Gaspésie–Îles-de-la-Madeleine",
1167 +"sorel tracy": "Montérégie",
1168 +"st sauveur": "Laurentides",
1169 +"stanbridge east": "Estrie",
1170 +"stanbridge station": "Estrie",
1171 +"stanstead": "Estrie",
1172 +"stanstead est": "Estrie",
1173 +"ste adele": "Laurentides",
1174 +"stoke": "Estrie",
1175 +"stoneham et tewkesbury": "Capitale-Nationale",
1176 +"stornoway": "Estrie",
1177 +"stratford": "Estrie",
1178 +"stukely sud": "Estrie",
1179 +"sutton": "Estrie",
1180 +"tadoussac": "Côte-Nord",
1181 +"taschereau": "Abitibi-Témiscamingue",
1182 +"tasiujaq": "Nord-du-Québec",
1183 +"temiscaming": "Abitibi-Témiscamingue",
1184 +"temiscouata sur le lac": "Bas-Saint-Laurent",
1185 +"terrasse vaudreuil": "Montérégie",
1186 +"terrebonne": "Lanaudière",
1187 +"thetford mines": "Chaudière-Appalaches",
1188 +"thorne": "Outaouais",
1189 +"thurso": "Outaouais",
1190 +"timiskaming": "Abitibi-Témiscamingue",
1191 +"tingwick": "Centre-du-Québec",
1192 +"toponyme a venir": "Nord-du-Québec",
1193 +"tourville": "Chaudière-Appalaches",
1194 +"trecesson": "Abitibi-Témiscamingue",
1195 +"tres saint redempteur": "Montérégie",
1196 +"tres saint sacrement": "Montérégie",
1197 +"tring jonction": "Chaudière-Appalaches",
1198 +"trois pistoles": "Bas-Saint-Laurent",
1199 +"trois rives": "Mauricie",
1200 +"trois rivieres": "Mauricie",
1201 +"uashat": "Côte-Nord",
1202 +"ulverton": "Estrie",
1203 +"umiujaq": "Nord-du-Québec",
1204 +"upton": "Montérégie",
1205 +"val alain": "Chaudière-Appalaches",
1206 +"val brillant": "Bas-Saint-Laurent",
1207 +"val d or": "Abitibi-Témiscamingue",
1208 +"val david": "Laurentides",
1209 +"val des bois": "Outaouais",
1210 +"val des lacs": "Laurentides",
1211 +"val des monts": "Outaouais",
1212 +"val des sources": "Estrie",
1213 +"val joli": "Estrie",
1214 +"val morin": "Laurentides",
1215 +"val racine": "Estrie",
1216 +"val saint gilles": "Abitibi-Témiscamingue",
1217 +"valcourt": "Estrie",
1218 +"vallee jonction": "Chaudière-Appalaches",
1219 +"varennes": "Montérégie",
1220 +"vaudreuil dorion": "Montérégie",
1221 +"vaudreuil sur le lac": "Montérégie",
1222 +"venise en quebec": "Montérégie",
1223 +"vercheres": "Montérégie",
1224 +"verdun": "Montréal",
1225 +"victoriaville": "Centre-du-Québec",
1226 +"vieux montreal": "Montréal",
1227 +"vieux quebec": "Capitale-Nationale",
1228 +"ville marie": "Abitibi-Témiscamingue",
1229 +"ville st laurent": "Montréal",
1230 +"villeray saint michel parc extension": "Montréal",
1231 +"villeroy": "Centre-du-Québec",
1232 +"waltham": "Outaouais",
1233 +"warden": "Estrie",
1234 +"warwick": "Centre-du-Québec",
1235 +"waskaganish": "Nord-du-Québec",
1236 +"waswanipi": "Nord-du-Québec",
1237 +"waterloo": "Estrie",
1238 +"waterville": "Estrie",
1239 +"weedon": "Estrie",
1240 +"wemindji": "Nord-du-Québec",
1241 +"wemotaci": "Mauricie",
1242 +"wendake": "Capitale-Nationale",
1243 +"wentworth": "Laurentides",
1244 +"wentworth nord": "Laurentides",
1245 +"westbury": "Estrie",
1246 +"westmount": "Montréal",
1247 +"whapmagoostui": "Nord-du-Québec",
1248 +"wickham": "Centre-du-Québec",
1249 +"windsor": "Estrie",
1250 +"wolinak": "Centre-du-Québec",
1251 +"wotton": "Estrie",
1252 +"yamachiche": "Mauricie",
1253 +"yamaska": "Montérégie"
1254 +}
1255 +}
\ No newline at end of file
added docs/screenshots/accueil.png +0 −0

Binary file not shown.

added docs/screenshots/fiche.png +0 −0

Binary file not shown.

added frontend/index.html +586 −0
@@ -0,0 +1,586 @@
1 +<!--
2 + Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 + File: index.html
4 + Desc: Frontend Sorti-Ka — recherche + fiches événement (SPA sans build,
5 + routeur History API, consomme /api). Design system Groupe KA
6 + « éditorial sharp » : papier #f5f3ee · encre #141814 · vert #1c5c41 ·
7 + lime #d9f26b · Space Grotesk / Inter / JetBrains Mono · bordures
8 + encre + ombres décalées + grain de film. SSR : sortika/seo.py
9 + injecte <head> et contenu dans les placeholders SSR_HEAD/SSR_BODY.
10 +-->
11 +<!DOCTYPE html>
12 +<html lang="fr">
13 +<head>
14 +<meta charset="utf-8">
15 +<meta name="viewport" content="width=device-width, initial-scale=1">
16 +<title>Sorti·Ka — Toutes les sorties et tous les événements du Québec</title>
17 +<meta name="description" content="Agrégateur indépendant de sorties et d'événements — concerts, festivals, spectacles, expositions, famille — dans les 17 régions du Québec, avec dates, lieux et gratuité.">
18 +<!--SSR_HEAD-->
19 +<link rel="preconnect" href="https://fonts.googleapis.com">
20 +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
21 +<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
22 +<style>
23 +:root{
24 + /* système de design Groupe KA « éditorial sharp » */
25 + --paper:#f5f3ee; --surface:#fff; --surface-2:#faf9f5;
26 + --ink:#141814; --ink-2:#4d5551; --ink-3:#8b928c;
27 + --line:rgba(20,24,20,.14); --line-strong:rgba(20,24,20,.85);
28 + --green:#1c5c41; --green-deep:#123f2e;
29 + --lime:#d9f26b; --lime-soft:#f0f9d2;
30 + --amber:#e8a33d; --amber-soft:#fdf3e2;
31 + --danger:#b3423a; --danger-soft:#fbe9e7;
32 + --r-card:10px; --r-ctl:6px;
33 + --shadow-flat:0 1px 2px rgba(20,24,20,.05);
34 + --shadow-off:6px 6px 0 var(--ink);
35 + --shadow-off-soft:8px 8px 0 rgba(20,24,20,.08);
36 + --font-display:"Space Grotesk",system-ui,sans-serif;
37 + --font-body:"Inter",system-ui,sans-serif;
38 + --font-mono:"JetBrains Mono",ui-monospace,monospace;
39 +}
40 +*{box-sizing:border-box}
41 +html{scroll-behavior:smooth}
42 +body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font-body);
43 + font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}
44 +body::before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.35;z-index:9999;
45 + background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.06'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E")}
46 +body>*{position:relative}
47 +h1,h2,h3,h4{font-family:var(--font-display);letter-spacing:-.03em;margin:0}
48 +a{color:inherit;text-decoration:none}
49 +button{font-family:inherit}
50 +img{display:block}
51 +::selection{background:var(--lime);color:var(--ink)}
52 +:focus-visible{outline:2px solid var(--green);outline-offset:2px}
53 +.container{max-width:1240px;margin:0 auto;padding:0 24px}
54 +@media(max-width:640px){.container{padding:0 16px}}
55 +
56 +/* ---------- micro-typo signature ---------- */
57 +.kicker{display:inline-flex;align-items:center;gap:10px;font-family:var(--font-mono);
58 + font-size:11.5px;font-weight:500;text-transform:uppercase;letter-spacing:.12em;color:var(--green)}
59 +.kicker::before{content:"";width:22px;height:2px;background:var(--green)}
60 +.klabel{font-family:var(--font-mono);font-size:10px;font-weight:700;
61 + text-transform:uppercase;letter-spacing:.1em;color:var(--ink-3)}
62 +.hl{display:inline-block;background:var(--lime);border-radius:8px;padding:0 10px 2px;transform:rotate(-1deg)}
63 +
64 +/* ---------- cartes & boutons signature ---------- */
65 +.gk-card{background:var(--surface);border:1.5px solid var(--ink);
66 + border-radius:var(--r-card);box-shadow:var(--shadow-off-soft)}
67 +.gk-hover{transition:transform .18s ease,box-shadow .18s ease}
68 +.gk-hover:hover{transform:translate(-3px,-3px);box-shadow:var(--shadow-off)}
69 +.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;
70 + padding:10px 20px;border:1.5px solid var(--ink);border-radius:var(--r-ctl);
71 + font-family:var(--font-display);font-size:14px;font-weight:700;cursor:pointer;
72 + transition:background .15s ease,transform .1s ease,box-shadow .1s ease}
73 +.btn:active{transform:translate(2px,2px);box-shadow:none!important}
74 +.btn-primary{background:var(--ink);color:var(--lime);box-shadow:4px 4px 0 rgba(20,24,20,.25)}
75 +.btn-primary:hover{background:var(--green-deep)}
76 +.btn-ghost{background:var(--surface);color:var(--ink);box-shadow:4px 4px 0 rgba(20,24,20,.12)}
77 +.btn-ghost:hover{background:var(--lime-soft)}
78 +
79 +/* ---------- header / ticker ---------- */
80 +.ticker{background:var(--ink);color:rgba(245,243,238,.85);overflow:hidden;
81 + font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
82 + padding:7px 0;white-space:nowrap}
83 +.ticker-track{display:inline-flex;animation:ticker 48s linear infinite;will-change:transform}
84 +.ticker span{padding:0 26px;position:relative}
85 +.ticker span::after{content:"◆";position:absolute;right:-6px;color:var(--lime);font-size:8px;top:3px}
86 +@keyframes ticker{from{transform:translateX(0)}to{transform:translateX(-50%)}}
87 +@media(prefers-reduced-motion:reduce){.ticker-track{animation:none}*{transition-duration:.01ms!important}}
88 +.header{position:sticky;top:0;z-index:50;background:rgba(245,243,238,.9);
89 + backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1.5px solid var(--ink)}
90 +.header-inner{display:flex;align-items:center;gap:18px;height:64px}
91 +.brand{font-family:var(--font-display);font-weight:700;font-size:24px;letter-spacing:-.03em;
92 + display:flex;align-items:baseline;line-height:1;cursor:pointer}
93 +.brand .ka{background:var(--lime);border-radius:6px;padding:0 6px 2px;margin-left:2px;transform:rotate(-1deg)}
94 +.brand-tag{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;
95 + letter-spacing:.08em;color:var(--ink-3);margin-left:8px}
96 +@media(max-width:900px){.brand-tag{display:none}}
97 +.nav{margin-left:auto;display:flex;gap:6px}
98 +.nav a{padding:8px 14px;border:1.5px solid transparent;border-radius:var(--r-ctl);
99 + font-family:var(--font-display);font-weight:700;font-size:13.5px;transition:all .15s ease}
100 +.nav a:hover{border-color:var(--ink);background:var(--lime-soft)}
101 +
102 +/* ---------- hero ---------- */
103 +.hero{padding:56px 0 20px}
104 +@media(max-width:640px){.hero{padding:34px 0 12px}}
105 +.hero h1{font-size:clamp(34px,5.2vw,58px);font-weight:700;line-height:1.05;max-width:940px;margin-top:16px}
106 +.hero p.lede{color:var(--ink-2);font-size:16.5px;max-width:660px;margin:18px 0 0}
107 +.stat-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:26px}
108 +.stat-chip{background:var(--surface);border:1.5px solid var(--ink);border-radius:var(--r-ctl);
109 + padding:8px 14px;font-family:var(--font-mono);font-size:11.5px;color:var(--ink-2);
110 + display:flex;gap:8px;align-items:center;box-shadow:3px 3px 0 rgba(20,24,20,.12)}
111 +.stat-chip b{color:var(--ink);font-weight:700}
112 +.stat-chip .pulse{width:8px;height:8px;border-radius:50%;background:var(--green);
113 + box-shadow:0 0 0 4px var(--lime-soft);animation:pulse 2.4s ease infinite}
114 +@keyframes pulse{50%{box-shadow:0 0 0 7px rgba(28,92,65,.16)}}
115 +
116 +/* ---------- filtres ---------- */
117 +.filterbar{padding:14px;margin:28px 0 6px}
118 +.f-primary{display:flex;gap:10px;align-items:stretch;flex-wrap:wrap}
119 +.f-search{flex:1 1 240px;min-width:0;max-width:460px;display:flex;align-items:center;gap:8px;
120 + border:1.5px solid var(--ink);border-radius:var(--r-ctl);background:var(--surface-2);
121 + padding:0 14px;min-height:46px;transition:background .15s ease,box-shadow .15s ease}
122 +.f-search:focus-within{background:var(--surface);box-shadow:3px 3px 0 var(--lime)}
123 +.f-search input{border:none;background:none;outline:none;flex:1;min-width:0;
124 + font-size:14px;color:var(--ink);font-family:inherit}
125 +.f-ctl{display:flex;flex-direction:column;justify-content:center;gap:2px;
126 + border:1.5px solid var(--ink);border-radius:var(--r-ctl);background:var(--surface);
127 + padding:6px 14px 5px;min-height:46px;cursor:pointer;transition:box-shadow .15s ease}
128 +.f-ctl:focus-within{box-shadow:3px 3px 0 var(--lime)}
129 +.f-ctl select{border:none;background:none;outline:none;font-family:var(--font-display);
130 + font-size:13.5px;font-weight:700;color:var(--ink);padding:0;margin-left:-2px;max-width:220px}
131 +.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
132 +.chip{border:1.5px solid var(--ink);background:var(--surface);border-radius:var(--r-ctl);
133 + padding:6px 12px;font-family:var(--font-display);font-size:12.5px;font-weight:700;
134 + cursor:pointer;color:var(--ink);line-height:1;transition:all .12s ease}
135 +.chip:hover{background:var(--lime-soft)}
136 +.chip.on{background:var(--ink);color:var(--lime);box-shadow:3px 3px 0 rgba(20,24,20,.25)}
137 +.chip.free.on{background:var(--green);border-color:var(--green);color:#fff}
138 +
139 +/* ---------- résultats ---------- */
140 +.meta-line{display:flex;align-items:center;gap:10px;margin:20px 2px 10px;
141 + font-family:var(--font-mono);font-size:11.5px;text-transform:uppercase;
142 + letter-spacing:.08em;color:var(--ink-3)}
143 +.meta-line b{color:var(--ink)}
144 +.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;margin:8px 0 34px}
145 +.card{overflow:hidden;display:flex;flex-direction:column;cursor:pointer}
146 +.card-img{position:relative;height:150px;border-bottom:1.5px solid var(--ink);
147 + background:repeating-linear-gradient(-45deg,var(--lime-soft),var(--lime-soft) 12px,var(--surface-2) 12px,var(--surface-2) 24px);overflow:hidden}
148 +.card-img img{width:100%;height:100%;object-fit:cover}
149 +.badge-date{position:absolute;left:10px;bottom:10px;font-family:var(--font-mono);
150 + font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
151 + color:var(--ink);background:var(--lime);border:1.5px solid var(--ink);
152 + border-radius:var(--r-ctl);padding:4px 9px;white-space:nowrap;box-shadow:2px 2px 0 var(--ink)}
153 +.card-body{padding:15px 16px 14px;display:flex;flex-direction:column;gap:8px;flex:1}
154 +.card-top{display:flex;justify-content:space-between;gap:8px;align-items:flex-start}
155 +.badge-free{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;
156 + color:#fff;background:var(--green);border-radius:var(--r-ctl);padding:4px 8px;flex:none}
157 +.badge-price{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;
158 + color:var(--ink);background:var(--amber-soft);border:1px solid var(--amber);
159 + border-radius:var(--r-ctl);padding:3px 8px;flex:none}
160 +.card h3{font-size:17px;font-weight:700;line-height:1.25}
161 +.card .desc{font-size:12.5px;color:var(--ink-2);display:-webkit-box;
162 + -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
163 +.card .where{font-size:12.5px;color:var(--ink-3)}
164 +.card .where b{color:var(--ink-2);font-weight:600}
165 +.card .cats{display:flex;flex-wrap:wrap;gap:6px;margin-top:auto;padding-top:4px}
166 +.cat{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;
167 + text-transform:uppercase;color:var(--ink-2);border:1px solid var(--line-strong);
168 + border-radius:var(--r-ctl);padding:2px 7px}
169 +.card .src{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;
170 + letter-spacing:.04em;color:var(--ink-3);border-top:1px dashed var(--line-strong);
171 + padding-top:10px;margin-top:6px;display:flex;justify-content:space-between}
172 +.card .src .go{color:var(--green);font-weight:700}
173 +.more{display:block;margin:0 auto 54px}
174 +.empty{padding:60px 10px;text-align:center;color:var(--ink-3);grid-column:1/-1}
175 +.empty b{font-family:var(--font-display);font-size:19px;color:var(--ink);display:block;margin-bottom:6px}
176 +
177 +/* ---------- fiche événement ---------- */
178 +.detail{padding:34px 0 50px}
179 +.back{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono);
180 + font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
181 + color:var(--green);cursor:pointer;margin-bottom:20px}
182 +.back:hover{text-decoration:underline}
183 +.detail-grid{display:grid;grid-template-columns:1.5fr 1fr;gap:26px;align-items:start}
184 +@media(max-width:860px){.detail-grid{grid-template-columns:1fr}}
185 +.detail-hero{overflow:hidden}
186 +.detail-hero .img{height:340px;border-bottom:1.5px solid var(--ink);
187 + background:repeating-linear-gradient(-45deg,var(--lime-soft),var(--lime-soft) 14px,var(--surface-2) 14px,var(--surface-2) 28px)}
188 +.detail-hero .img img{width:100%;height:100%;object-fit:cover}
189 +.detail-hero .body{padding:24px 26px 26px}
190 +.detail-hero h1{font-size:clamp(26px,3.4vw,40px);line-height:1.08;margin:10px 0 14px}
191 +.detail-hero .desc{color:var(--ink-2);font-size:15px;white-space:pre-line}
192 +.detail-cats{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
193 +.side{display:flex;flex-direction:column;gap:16px}
194 +.side .box{padding:18px 20px}
195 +.fact{display:flex;justify-content:space-between;gap:14px;padding:9px 0;border-bottom:1px dashed var(--line-strong);font-size:13.5px}
196 +.fact:last-child{border-bottom:none}
197 +.fact .k{font-family:var(--font-mono);font-size:10px;font-weight:700;
198 + text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);padding-top:3px;flex:none}
199 +.fact .v{text-align:right;font-weight:600}
200 +.side .btn{width:100%}
201 +.maplink{font-family:var(--font-mono);font-size:11px;color:var(--green);font-weight:700;text-transform:uppercase}
202 +.maplink:hover{text-decoration:underline}
203 +.similar h2{font-size:22px;margin:44px 0 4px}
204 +
205 +/* ---------- sources / footer ---------- */
206 +.sources{background:var(--surface-2);border-top:1.5px solid var(--ink);padding:44px 0 38px}
207 +.sources h2{font-size:26px;margin-top:12px}
208 +.sources p.lede2{color:var(--ink-2);font-size:14.5px;max-width:720px;margin:10px 0 0}
209 +.src-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;margin-top:22px}
210 +.src-card{padding:16px 18px}
211 +.src-card h4{font-size:15px;margin-bottom:5px}
212 +.src-card .lic{font-family:var(--font-mono);font-size:9.5px;font-weight:700;
213 + color:var(--green);text-transform:uppercase;letter-spacing:.06em}
214 +.src-card .n{font-size:12.5px;color:var(--ink-3);margin-top:6px}
215 +.src-card .n b{color:var(--green-deep)}
216 +footer{background:var(--ink);color:rgba(245,243,238,.72);padding:36px 0 46px;font-size:13px}
217 +footer .fam{display:flex;flex-wrap:wrap;gap:4px 14px;margin-bottom:14px}
218 +footer .fam a{font-family:var(--font-display);font-weight:700;color:#f5f3ee;padding:4px 0}
219 +footer .fam a:hover{color:var(--lime)}
220 +footer .fam .lbl{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;
221 + letter-spacing:.08em;color:rgba(245,243,238,.45);align-self:center}
222 +footer a.mail{color:var(--lime)}
223 +#ssr{display:none}
224 +.hidden{display:none!important}
225 +</style>
226 +</head>
227 +<body>
228 +
229 +<div id="ssr"><!--SSR_BODY--></div>
230 +
231 +<div class="ticker"><div class="ticker-track">
232 + <span>Concerts</span><span>Festivals</span><span>Humour</span><span>Théâtre</span>
233 + <span>Expositions</span><span>Danse</span><span>Sport</span><span>Famille</span>
234 + <span>Plein air</span><span>17 régions du Québec</span>
235 + <span>Concerts</span><span>Festivals</span><span>Humour</span><span>Théâtre</span>
236 + <span>Expositions</span><span>Danse</span><span>Sport</span><span>Famille</span>
237 + <span>Plein air</span><span>17 régions du Québec</span>
238 +</div></div>
239 +
240 +<header class="header">
241 + <div class="container header-inner">
242 + <a class="brand" data-nav href="/">Sorti<span class="ka">·Ka</span></a>
243 + <span class="brand-tag">Sorties &amp; événements — Québec</span>
244 + <nav class="nav">
245 + <a href="/#sources">Sources</a>
246 + <a href="/docs" target="_blank" rel="noopener">API</a>
247 + </nav>
248 + </div>
249 +</header>
250 +
251 +<!-- ======== VUE LISTE ======== -->
252 +<main id="view-list">
253 + <section class="hero container">
254 + <span class="kicker">Agrégateur indépendant · mise à jour horaire</span>
255 + <h1>Toutes les sorties du Québec, <span class="hl">un seul endroit.</span></h1>
256 + <p class="lede">Concerts, festivals, spectacles, expositions, sport, famille — agrégés
257 + depuis des billetteries et des sources ouvertes officielles, dans les 17 régions.
258 + Chaque fiche renvoie vers la billetterie ou la source originale.</p>
259 + <div class="stat-row" id="stats"></div>
260 + </section>
261 +
262 + <section class="container">
263 + <div class="filterbar gk-card">
264 + <div class="f-primary">
265 + <label class="f-search">🔎<input type="text" id="q" placeholder="Artiste, événement, salle, ville…"></label>
266 + <label class="f-ctl"><span class="klabel">Région</span>
267 + <select id="region"><option value="">Toutes les régions</option></select>
268 + </label>
269 + <label class="f-ctl"><span class="klabel">Quand</span>
270 + <select id="period">
271 + <option value="">À venir (tout)</option>
272 + <option value="today">Aujourd'hui</option>
273 + <option value="weekend">Ce week-end</option>
274 + <option value="7">7 prochains jours</option>
275 + <option value="30">30 prochains jours</option>
276 + </select>
277 + </label>
278 + </div>
279 + <div class="chips" id="cats"></div>
280 + </div>
281 +
282 + <div class="meta-line"><span id="count">Chargement…</span></div>
283 + <section class="grid" id="grid"></section>
284 + <button class="more btn btn-primary" id="more" hidden>Afficher plus d'événements</button>
285 + </section>
286 +
287 + <section class="sources" id="sources">
288 + <div class="container">
289 + <span class="kicker">Index fidèle · attribution systématique</span>
290 + <h2>Les sources</h2>
291 + <p class="lede2">Rien d'inventé : chaque événement est attribué à sa source et lié
292 + vers la fiche originale. Billetteries au robots.txt permissif et
293 + <b>données ouvertes officielles</b> (licences Creative Commons).</p>
294 + <div class="src-grid" id="src-grid"></div>
295 + </div>
296 + </section>
297 +</main>
298 +
299 +<!-- ======== VUE FICHE ÉVÉNEMENT ======== -->
300 +<main id="view-detail" class="hidden">
301 + <div class="container detail">
302 + <span class="back" id="back">← Toutes les sorties</span>
303 + <div class="detail-grid">
304 + <article class="gk-card detail-hero">
305 + <div class="img" id="d-img"></div>
306 + <div class="body">
307 + <span class="klabel" id="d-cats-label"></span>
308 + <h1 id="d-title"></h1>
309 + <div class="desc" id="d-desc"></div>
310 + <div class="detail-cats" id="d-cats"></div>
311 + </div>
312 + </article>
313 + <aside class="side">
314 + <div class="gk-card box" id="d-facts"></div>
315 + <a class="btn btn-primary" id="d-cta" target="_blank" rel="noopener">Billets · fiche originale ↗</a>
316 + <div class="gk-card box" id="d-src"></div>
317 + </aside>
318 + </div>
319 + <section class="similar">
320 + <span class="kicker">À voir aussi</span>
321 + <h2 id="d-similar-title">Événements similaires</h2>
322 + <div class="grid" id="d-similar"></div>
323 + </section>
324 + </div>
325 +</main>
326 +
327 +<footer>
328 + <div class="container">
329 + <div class="fam">
330 + <span class="lbl">Famille ·Ka</span>
331 + <a href="https://www.lou-ka.com" target="_blank" rel="noopener">Lou·Ka</a>
332 + <a href="https://www.immo-ka.com" target="_blank" rel="noopener">Immo·Ka</a>
333 + <a href="https://www.auto-ka.com" target="_blank" rel="noopener">Auto·Ka</a>
334 + <a href="https://www.food-ka.com" target="_blank" rel="noopener">Food·Ka</a>
335 + <a href="https://www.fabri-ka.com" target="_blank" rel="noopener">Fabri·Ka</a>
336 + <a href="https://www.toit-ka.com" target="_blank" rel="noopener">Toit·Ka</a>
337 + <a href="https://www.forma-ka.com" target="_blank" rel="noopener">Forma·Ka</a>
338 + <a href="https://www.groupe-ka.com" target="_blank" rel="noopener">Groupe KA</a>
339 + </div>
340 + <div>Données : SIT Québec (MTO, CC 4.0) · Ville de Montréal (CC BY 4.0) · Ville de Laval (CC 4.0) ·
341 + Le point de vente · evenko · Atuvu.ca · La Vitrine · Répertoire des municipalités (MAMH).</div>
342 + <div style="margin-top:8px">© 2026 Sorti·Ka — Simon-Pierre Boucher ·
343 + <a class="mail" href="mailto:contact@spboucher.ai">contact@spboucher.ai</a></div>
344 + </div>
345 +</footer>
346 +
347 +<script>
348 +/* Sorti-Ka — frontend + routeur. Auteur : Simon-Pierre Boucher <contact@spboucher.ai> */
349 +"use strict";
350 +const $ = id => document.getElementById(id);
351 +const state = { q:"", region:"", category:"", free:false, from:"", to:"", offset:0, limit:30, total:0 };
352 +
353 +const CAT_LABELS = {
354 + "festival":"Festivals","musique":"Musique","arts-scene":"Arts de la scène",
355 + "exposition-musee":"Expos & musées","cinema":"Cinéma","sport":"Sport",
356 + "plein-air":"Plein air","famille":"Famille","gastronomie":"Gastronomie",
357 + "marche-foire":"Marchés & foires","conference":"Conférences & ateliers",
358 + "communautaire":"Communautaire","patrimoine":"Patrimoine","autre":"Autre"
359 +};
360 +const SRC_LABELS = {
361 + "sitq":"SIT Québec — Ministère du Tourisme","montreal":"Ville de Montréal",
362 + "laval":"Ville de Laval","lepointdevente":"Le point de vente",
363 + "evenko":"evenko","atuvu":"Atuvu.ca","lavitrine":"La Vitrine","sherbrooke":"Ville de Sherbrooke"
364 +};
365 +const MONTHS = ["jan","fév","mar","avr","mai","juin","juil","août","sep","oct","nov","déc"];
366 +const MONTHS_FULL = ["janvier","février","mars","avril","mai","juin","juillet",
367 + "août","septembre","octobre","novembre","décembre"];
368 +
369 +function fmtDate(iso, full=false){
370 + if(!iso) return "Date à confirmer";
371 + const [y,m,d] = iso.split("-").map(Number);
372 + return `${d} ${(full?MONTHS_FULL:MONTHS)[m-1]} ${y}`;
373 +}
374 +function dateBadge(ev, full=false){
375 + if(!ev.start_date) return "Date à confirmer";
376 + if(ev.end_date && ev.end_date !== ev.start_date)
377 + return `${fmtDate(ev.start_date, full)} → ${fmtDate(ev.end_date, full)}`;
378 + return fmtDate(ev.start_date, full);
379 +}
380 +function esc(s){ const d=document.createElement("div"); d.textContent=s||""; return d.innerHTML; }
381 +function priceBadge(ev){
382 + if(ev.is_free === true) return `<span class="badge-free">Gratuit</span>`;
383 + if(ev.price_min != null) return `<span class="badge-price">Dès ${ev.price_min.toFixed(2).replace(".",",")} $</span>`;
384 + if(ev.is_free === false) return `<span class="badge-price">Payant</span>`;
385 + return "";
386 +}
387 +
388 +function card(ev){
389 + const where = [ev.venue, ev.city, ev.region].filter(Boolean)
390 + .map((x,i)=> i===1?`<b>${esc(x)}</b>`:esc(x)).join(" · ");
391 + const cats = (ev.categories||[]).filter(c=>c!=="autre").slice(0,3)
392 + .map(c=>`<span class="cat">${esc(CAT_LABELS[c]||c)}</span>`).join("");
393 + const img = ev.image
394 + ? `<img src="${esc(ev.image)}" alt="" loading="lazy" onerror="this.remove()">` : "";
395 + return `<a class="card gk-card gk-hover" data-nav href="/evenement/${esc(ev.uid)}">
396 + <div class="card-img">${img}<span class="badge-date">${dateBadge(ev)}</span></div>
397 + <div class="card-body">
398 + <div class="card-top"><h3>${esc(ev.title)}</h3>${priceBadge(ev)}</div>
399 + ${ev.description?`<div class="desc">${esc(ev.description)}</div>`:""}
400 + <div class="where">${where||"Lieu à confirmer"}</div>
401 + <div class="cats">${cats}</div>
402 + <div class="src"><span>${SRC_LABELS[ev.source]||ev.source}</span><span class="go">Fiche →</span></div>
403 + </div>
404 + </a>`;
405 +}
406 +
407 +function params(){
408 + const p = new URLSearchParams();
409 + if(state.q) p.set("q", state.q);
410 + if(state.region) p.set("region", state.region);
411 + if(state.category) p.set("category", state.category);
412 + if(state.free) p.set("free","true");
413 + if(state.from) p.set("from", state.from);
414 + if(state.to) p.set("to", state.to);
415 + p.set("limit", state.limit); p.set("offset", state.offset);
416 + return p.toString();
417 +}
418 +
419 +async function load(reset=true){
420 + if(reset){ state.offset = 0; }
421 + const data = await (await fetch(`/api/events?${params()}`)).json();
422 + state.total = data.total;
423 + const html = data.events.map(card).join("");
424 + if(reset){
425 + $("grid").innerHTML = html ||
426 + `<div class="empty"><b>Aucun événement trouvé</b>Essayez d'élargir les filtres ou la période.</div>`;
427 + } else {
428 + $("grid").insertAdjacentHTML("beforeend", html);
429 + }
430 + $("count").innerHTML = `<b>${data.total.toLocaleString("fr-CA")}</b>&nbsp;événement${data.total>1?"s":""} à venir`;
431 + $("more").hidden = state.offset + data.events.length >= data.total;
432 + bindNav($("grid"));
433 +}
434 +
435 +function setPeriod(v){
436 + const today = new Date(); const iso = d => d.toISOString().slice(0,10);
437 + state.from = ""; state.to = "";
438 + if(v==="today"){ state.from = iso(today); state.to = iso(today); }
439 + else if(v==="weekend"){
440 + const day = today.getDay();
441 + const sat = new Date(today);
442 + if(day !== 6 && day !== 0) sat.setDate(today.getDate() + (6 - day));
443 + const sun = new Date(sat); sun.setDate(sat.getDate() + (day === 0 ? 0 : 1));
444 + state.from = iso(day===6||day===0 ? today : sat); state.to = iso(sun);
445 + }
446 + else if(v){ const end = new Date(today); end.setDate(today.getDate()+Number(v)); state.from = iso(today); state.to = iso(end); }
447 +}
448 +
449 +/* ---------- fiche événement ---------- */
450 +async function showDetail(uid){
451 + let ev;
452 + try{ ev = await (await fetch(`/api/events/${encodeURIComponent(uid)}`)).json(); }
453 + catch(e){ go("/"); return; }
454 + if(ev.detail){ go("/"); return; }
455 +
456 + document.title = `${ev.title} — ${ev.city || "Québec"} | Sorti·Ka`;
457 + $("d-img").innerHTML = ev.image
458 + ? `<img src="${esc(ev.image)}" alt="" onerror="this.remove()">` : "";
459 + $("d-cats-label").textContent = (ev.categories||[]).filter(c=>c!=="autre")
460 + .map(c=>CAT_LABELS[c]||c).join(" · ") || "Événement";
461 + $("d-title").textContent = ev.title;
462 + $("d-desc").textContent = ev.description || "";
463 + $("d-cats").innerHTML = (ev.raw_categories||[]).slice(0,5).filter(c=>c.length<40)
464 + .map(c=>`<span class="cat">${esc(c)}</span>`).join("");
465 +
466 + const facts = [];
467 + facts.push(["Quand", dateBadge(ev, true)]);
468 + if(ev.venue) facts.push(["Lieu", esc(ev.venue)]);
469 + if(ev.address) facts.push(["Adresse", esc(ev.address)]);
470 + if(ev.city) facts.push(["Ville", esc(ev.city)]);
471 + if(ev.region) facts.push(["Région", esc(ev.region)]);
472 + if(ev.is_free === true) facts.push(["Prix", `<span class="badge-free">Gratuit</span>`]);
473 + else if(ev.price_min != null) facts.push(["Prix", `Dès ${ev.price_min.toFixed(2).replace(".",",")} $ CAD`]);
474 + else if(ev.is_free === false) facts.push(["Prix", "Payant — voir la billetterie"]);
475 + if(ev.price_label) facts.push(["Détail prix", esc(ev.price_label)]);
476 + if(ev.audience) facts.push(["Public", esc(ev.audience)]);
477 + if(ev.organizer) facts.push(["Organisé par", esc(ev.organizer)]);
478 + if(ev.lat && ev.lng) facts.push(["Carte",
479 + `<a class="maplink" target="_blank" rel="noopener" href="https://www.google.com/maps?q=${ev.lat},${ev.lng}">Voir sur la carte ↗</a>`]);
480 + $("d-facts").innerHTML = facts.map(([k,v]) =>
481 + `<div class="fact"><span class="k">${k}</span><span class="v">${v}</span></div>`).join("");
482 +
483 + $("d-cta").href = ev.url;
484 + $("d-src").innerHTML = `<span class="klabel">Source & attribution</span>
485 + <div style="margin-top:6px;font-size:13px">${SRC_LABELS[ev.source]||ev.source}</div>
486 + <div style="font-family:var(--font-mono);font-size:10px;color:var(--ink-3);margin-top:4px">
487 + Capté le ${new Date((ev.updated_at||0)*1000).toLocaleDateString("fr-CA")} ·
488 + identifiant ${esc(ev.uid)}</div>`;
489 +
490 + // événements similaires : même catégorie prioritaire, sinon même ville
491 + const cat = (ev.categories||[]).filter(c=>c!=="autre")[0] || "";
492 + const q = new URLSearchParams({limit: 7});
493 + if(cat) q.set("category", cat); else if(ev.city) q.set("city", ev.city);
494 + if(ev.region && cat) q.set("region", ev.region);
495 + try{
496 + const sim = await (await fetch(`/api/events?${q}`)).json();
497 + const others = sim.events.filter(e=>e.uid!==ev.uid).slice(0,6);
498 + $("d-similar").innerHTML = others.map(card).join("") ||
499 + `<div class="empty">Rien de similaire pour l'instant.</div>`;
500 + bindNav($("d-similar"));
501 + }catch(e){ $("d-similar").innerHTML = ""; }
502 +
503 + $("view-list").classList.add("hidden");
504 + $("view-detail").classList.remove("hidden");
505 + window.scrollTo({top:0});
506 +}
507 +
508 +function showList(){
509 + document.title = "Sorti·Ka — Toutes les sorties et tous les événements du Québec";
510 + $("view-detail").classList.add("hidden");
511 + $("view-list").classList.remove("hidden");
512 +}
513 +
514 +/* ---------- routeur ---------- */
515 +function route(){
516 + const m = location.pathname.match(/^\/evenement\/(.+)$/);
517 + if(m) showDetail(decodeURIComponent(m[1]));
518 + else showList();
519 +}
520 +function go(path){
521 + history.pushState({}, "", path);
522 + route();
523 +}
524 +function bindNav(root){
525 + (root||document).querySelectorAll("a[data-nav]").forEach(a => a.onclick = e => {
526 + if(e.metaKey||e.ctrlKey||e.shiftKey) return;
527 + e.preventDefault(); go(a.getAttribute("href"));
528 + });
529 +}
530 +window.addEventListener("popstate", route);
531 +$("back").onclick = () => go("/");
532 +
533 +/* ---------- init ---------- */
534 +async function init(){
535 + const regions = await (await fetch("/api/regions")).json();
536 + $("region").insertAdjacentHTML("beforeend",
537 + regions.map(r=>`<option>${esc(r)}</option>`).join(""));
538 +
539 + const cats = await (await fetch("/api/categories")).json();
540 + $("cats").innerHTML =
541 + `<span class="chip free" id="chip-free">✦ Gratuit</span>` +
542 + cats.filter(c=>c!=="autre").map(c=>`<span class="chip" data-cat="${c}">${CAT_LABELS[c]||c}</span>`).join("");
543 + $("chip-free").onclick = e => { state.free=!state.free; e.target.classList.toggle("on",state.free); load(); };
544 + document.querySelectorAll(".chip[data-cat]").forEach(ch => ch.onclick = () => {
545 + const c = ch.dataset.cat;
546 + state.category = state.category===c ? "" : c;
547 + document.querySelectorAll(".chip[data-cat]").forEach(x=>x.classList.toggle("on", x.dataset.cat===state.category));
548 + load();
549 + });
550 +
551 + try{
552 + const s = await (await fetch("/api/stats")).json();
553 + const nReg = Object.keys(s.by_region).filter(r=>r!=="Non rattachée").length;
554 + $("stats").innerHTML = [
555 + `<span class="stat-chip"><span class="pulse"></span><b>${s.upcoming.toLocaleString("fr-CA")}</b> événements à venir</span>`,
556 + `<span class="stat-chip"><b>${s.free_upcoming.toLocaleString("fr-CA")}</b> gratuits</span>`,
557 + `<span class="stat-chip"><b>${nReg}</b> régions</span>`,
558 + `<span class="stat-chip"><b>${s.cities.toLocaleString("fr-CA")}</b> villes</span>`,
559 + ].join("");
560 + }catch(e){}
561 +
562 + try{
563 + const reg = await (await fetch("/api/sources")).json();
564 + $("src-grid").innerHTML = (reg.sources||[]).filter(s=>s.statut==="actif"||s.active_events>0).map(s=>`
565 + <div class="src-card gk-card">
566 + <h4>${esc(s.name)}</h4>
567 + <div class="lic">${esc((s.acces_legal||"").split("—")[0])}</div>
568 + <div class="n"><b>${(s.active_events||0).toLocaleString("fr-CA")}</b> événements actifs · ${esc((s.extraction||"").split("(")[0])}</div>
569 + </div>`).join("");
570 + }catch(e){}
571 +
572 + let t; $("q").addEventListener("input", e=>{
573 + clearTimeout(t); t=setTimeout(()=>{ state.q=e.target.value.trim(); load(); }, 280);
574 + });
575 + $("region").onchange = e => { state.region = e.target.value; load(); };
576 + $("period").onchange = e => { setPeriod(e.target.value); load(); };
577 + $("more").onclick = () => { state.offset += state.limit; load(false); };
578 + bindNav(document);
579 +
580 + await load();
581 + route();
582 +}
583 +init();
584 +</script>
585 +</body>
586 +</html>
added requirements.txt +9 −0
@@ -0,0 +1,9 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: requirements.txt
4 +# Desc: Dépendances Sorti-Ka (mêmes briques que Lou-Ka : FastAPI + requests)
5 +# ==============================================================================
6 +fastapi>=0.115
7 +uvicorn>=0.30
8 +requests>=2.32
9 +pytest>=8.0
added run.py +51 −0
@@ -0,0 +1,51 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: run.py
4 +# Desc: CLI Sorti-Ka — sync (ingestion), watch (boucle périodique),
5 +# web (API FastAPI + frontend). Patron Lou-Ka : run.py.
6 +# ==============================================================================
7 +from __future__ import annotations
8 +
9 +import argparse
10 +import sys
11 +
12 +
13 +def main() -> int:
14 + parser = argparse.ArgumentParser(
15 + prog="sorti-ka",
16 + description="Sorti-Ka — toutes les sorties et tous les événements du Québec")
17 + sub = parser.add_subparsers(dest="cmd", required=True)
18 +
19 + p_sync = sub.add_parser("sync", help="exécuter l'ingestion (toutes les sources)")
20 + p_sync.add_argument("sources", nargs="*", help="ids de sources (défaut : toutes)")
21 +
22 + p_watch = sub.add_parser("watch", help="ingestion en boucle périodique")
23 + p_watch.add_argument("--interval", type=int, default=6 * 3600,
24 + help="intervalle en secondes (défaut : 21600 = 6 h)")
25 +
26 + p_web = sub.add_parser("web", help="lancer l'API + le frontend")
27 + p_web.add_argument("--host", default="127.0.0.1")
28 + p_web.add_argument("--port", type=int, default=8120)
29 +
30 + args = parser.parse_args()
31 +
32 + if args.cmd == "sync":
33 + from sortika import ingest
34 + results = ingest.run(args.sources or None)
35 + return 1 if any("error" in r for r in results) else 0
36 +
37 + if args.cmd == "watch":
38 + from sortika import ingest
39 + ingest.watch(args.interval)
40 + return 0
41 +
42 + if args.cmd == "web":
43 + import uvicorn
44 + uvicorn.run("sortika.web:app", host=args.host, port=args.port)
45 + return 0
46 +
47 + return 2
48 +
49 +
50 +if __name__ == "__main__":
51 + sys.exit(main())
added sortika/__init__.py +9 −0
@@ -0,0 +1,9 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# __init__.py : paquet sortika — pipeline connecteurs → normalisation →
5 +# déduplication → SQLite → API FastAPI (famille ·Ka, patron Lou-Ka)
6 +# -----------------------------------------------------------------------------
7 +"""Sorti-Ka — toutes les sorties et tous les événements du Québec, un seul endroit."""
8 +
9 +__version__ = "1.0.0"
added sortika/connectors/__init__.py +30 −0
@@ -0,0 +1,30 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/__init__.py : registre AUTO-DÉCOUVRANT des connecteurs
5 +# Tout module de ce paquet contenant une sous-classe de BaseConnector avec un
6 +# source_id non vide est enregistré automatiquement — aucun fichier partagé
7 +# à modifier pour ajouter un connecteur (patron Lou-Ka).
8 +# -----------------------------------------------------------------------------
9 +from __future__ import annotations
10 +
11 +import importlib
12 +import pkgutil
13 +import sys
14 +
15 +from .base import BaseConnector
16 +
17 +CONNECTORS: dict[str, type[BaseConnector]] = {}
18 +
19 +for _mod in pkgutil.iter_modules(__path__):
20 + if _mod.name in ("base", "__init__"):
21 + continue
22 + try:
23 + module = importlib.import_module(f"{__name__}.{_mod.name}")
24 + except Exception as exc: # un connecteur cassé ne bloque pas les autres
25 + print(f"[sorti-ka] connecteur '{_mod.name}' ignoré : {exc}", file=sys.stderr)
26 + continue
27 + for obj in vars(module).values():
28 + if (isinstance(obj, type) and issubclass(obj, BaseConnector)
29 + and obj is not BaseConnector and getattr(obj, "source_id", "")):
30 + CONNECTORS[obj.source_id] = obj
added sortika/connectors/atuvu.py +127 −0
@@ -0,0 +1,127 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/atuvu.py : connecteur Atuvu.ca — calendrier culturel (Grand Montréal +)
5 +# Source : atuvu.ca (théâtre, musique, humour, danse, expositions…)
6 +# Extraction: SCRAPFLY obligatoire (Cloudflare) — sitemaps XML publics
7 +# (evenements_N_2000.xml, lastmod par fiche) puis pages fiche
8 +# (h1, blocs .event-play : date FR, salle, ville, prix).
9 +# INCRÉMENTAL avec cache disque (data/atuvu_cache.json) : seules
10 +# les fiches nouvelles ou modifiées sont re-scrapées, plafonné à
11 +# ATUVU_MAX_FETCH pages/sync pour contenir le coût en crédits.
12 +# Accès : robots.txt « User-Agent: * / Allow: / » + sitemap public.
13 +# Prix : bloc .price-reg de la fiche (prix régulier) → price_min ;
14 +# « Invitation gratuite » = offre membre, PAS gratuité → ignorée.
15 +# -----------------------------------------------------------------------------
16 +from __future__ import annotations
17 +
18 +import re
19 +import time
20 +from datetime import date, timedelta
21 +
22 +from ..normalize import clean_text, parse_date_fr
23 +from ..schema import Event
24 +from .base import BaseConnector
25 +
26 +SITEMAP_INDEX = "https://atuvu.ca/atuvu_sitemap_index.xml"
27 +
28 +ATUVU_MAX_FETCH = int(__import__("os").environ.get("ATUVU_MAX_FETCH", "120")) # fiches/sync (coût Scrapfly)
29 +LASTMOD_WINDOW_DAYS = 90 # fiches modifiées dans cette fenêtre seulement
30 +SHARDS_TO_READ = 3 # derniers shards du sitemap (ids les plus récents)
31 +
32 +_LOC_RE = re.compile(r"<loc>([^<]+)</loc>\s*(?:<lastmod>([^<]+)</lastmod>)?")
33 +_URL_BLOCK_RE = re.compile(r"<url>(.*?)</url>", re.S)
34 +_H1_RE = re.compile(r"<h1[^>]*>(.*?)</h1>", re.S)
35 +_OG_RE = {k: re.compile(rf'property="og:{k}" content="([^"]*)"')
36 + for k in ("image", "description")}
37 +_PLAY_RE = re.compile(
38 + r'class="date">\s*([^<]+?)\s*</div>.*?class="place">\s*'
39 + r'<span>([^<]*)</span>(?:<br\s*/?>\s*<span>([^<]*)</span>)?', re.S)
40 +_PRICE_RE = re.compile(r'class="price-reg[^"]*">\s*([\d.,]+)\s*\$')
41 +_CAT_RE = re.compile(r'class="main_cat[^"]*">([^<]+)<')
42 +
43 +
44 +class AtuvuConnector(BaseConnector):
45 + source_id = "atuvu"
46 + request_delay = 1.0
47 +
48 + # -- sitemap ----------------------------------------------------------
49 + def _recent_fiches(self) -> dict[str, str]:
50 + """URL de fiche → lastmod, pour les fiches modifiées récemment."""
51 + idx = self.get_rendered(SITEMAP_INDEX, render_js=False)
52 + shards = [u for u, _ in _LOC_RE.findall(idx) if "/evenements_" in u]
53 + cutoff = (date.today() - timedelta(days=LASTMOD_WINDOW_DAYS)).isoformat()
54 + fiches: dict[str, str] = {}
55 + for shard in shards[-SHARDS_TO_READ:]:
56 + xml = self.get_rendered(shard, render_js=False)
57 + for block in _URL_BLOCK_RE.findall(xml):
58 + m = _LOC_RE.search(block)
59 + if not m:
60 + continue
61 + url, lastmod = m.group(1), (m.group(2) or "")[:10]
62 + if "/billets-spectacle/" in url and lastmod >= cutoff:
63 + fiches[url] = lastmod
64 + return fiches
65 +
66 + # -- fiche ------------------------------------------------------------
67 + def _parse_fiche(self, url: str, html: str) -> list[dict]:
68 + h1 = _H1_RE.search(html)
69 + title = clean_text(h1.group(1)) if h1 else ""
70 + if not title:
71 + return []
72 + image = (_OG_RE["image"].search(html) or [None, ""])[1]
73 + cats = [clean_text(c) for c in _CAT_RE.findall(html)][:2]
74 + price_m = _PRICE_RE.search(html)
75 + price = float(price_m.group(1).replace(",", ".")) if price_m else None
76 + slug = url.rstrip("/").rsplit("/", 1)[-1]
77 + rows = []
78 + for date_txt, venue, city in _PLAY_RE.findall(html)[:10]:
79 + day = parse_date_fr(date_txt)
80 + if not day:
81 + continue
82 + rows.append({
83 + "external_id": f"{slug}-{day}",
84 + "url": url, "title": title, "image": image,
85 + "raw_categories": cats, "venue": clean_text(venue),
86 + "city": clean_text(city or ""), "start_date": day,
87 + "end_date": day, "price_min": price,
88 + "price_label": f"{price:.2f} $ (prix régulier atuvu.ca)" if price else "",
89 + })
90 + return rows
91 +
92 + # -- pipeline ---------------------------------------------------------
93 + def fetch(self) -> list[Event]:
94 + cache = self.load_cache()
95 + fiches = self._recent_fiches()
96 +
97 + # retirer du cache les fiches disparues du sitemap récent
98 + cache = {u: c for u, c in cache.items() if u in fiches}
99 +
100 + to_fetch = [u for u, mod in fiches.items()
101 + if cache.get(u, {}).get("lastmod") != mod][:ATUVU_MAX_FETCH]
102 + for url in to_fetch:
103 + try:
104 + html = self.get_rendered(url, render_js=False)
105 + cache[url] = {"lastmod": fiches[url], "fetched_at": time.time(),
106 + "rows": self._parse_fiche(url, html)}
107 + except Exception as exc: # une fiche cassée ne bloque pas la source
108 + print(f"[sorti-ka] atuvu : fiche ignorée {url} : {exc}")
109 + self.save_cache(cache)
110 +
111 + events: list[Event] = []
112 + for entry in cache.values():
113 + for row in entry.get("rows", []):
114 + events.append(Event(
115 + source=self.source_id,
116 + external_id=row["external_id"],
117 + url=row["url"], title=row["title"],
118 + raw_categories=row.get("raw_categories") or [],
119 + venue=row.get("venue", ""), city=row.get("city", ""),
120 + start_date=row.get("start_date"),
121 + end_date=row.get("end_date"),
122 + price_min=row.get("price_min"),
123 + price_label=row.get("price_label", ""),
124 + is_free=False if row.get("price_min") else None,
125 + image=row.get("image", ""),
126 + ))
127 + return events
added sortika/connectors/base.py +142 −0
@@ -0,0 +1,142 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/base.py : classe de base des connecteurs — GET/POST throttlés,
5 +# identification honnête + backend Scrapfly pour les
6 +# sources derrière anti-bot / JavaScript lourd
7 +# (patron Lou-Ka : connectors/base.py). Les deux premières
8 +# sources sont des données ouvertes → GET direct suffit.
9 +# -----------------------------------------------------------------------------
10 +from __future__ import annotations
11 +
12 +import json
13 +import os
14 +import time
15 +from pathlib import Path
16 +
17 +import requests
18 +
19 +SCRAPFLY_API = "https://api.scrapfly.io/scrape"
20 +
21 +
22 +def _load_env() -> None:
23 + """Charge .env à la racine du projet (clés Scrapfly/Firecrawl), sans dépendance."""
24 + env_path = Path(__file__).resolve().parents[2] / ".env"
25 + if not env_path.exists():
26 + return
27 + for line in env_path.read_text(encoding="utf-8").splitlines():
28 + line = line.strip()
29 + if not line or line.startswith("#") or "=" not in line:
30 + continue
31 + k, v = line.split("=", 1)
32 + os.environ.setdefault(k.strip(), v.strip())
33 +
34 +
35 +_load_env()
36 +
37 +from ..schema import Event
38 +
39 +# Identification honnête (nom + site + contact). Note : le WAF de
40 +# donnees.montreal.ca renvoie 403 à tout User-Agent contenant « bot » —
41 +# on s'identifie donc par le nom du produit, avec URL et courriel de contact.
42 +USER_AGENT = "SortiKa/1.0 (+https://www.sorti-ka.com; contact@spboucher.ai)"
43 +
44 +
45 +class BaseConnector:
46 + """Un connecteur = un adaptateur propre à une source d'événements.
47 +
48 + Sous-classes : définir `source_id` et implémenter `fetch()` qui retourne
49 + la liste complète des événements actuellement publiés par la source.
50 + Le pipeline (ingest.py) s'occupe du diff avec la base de données.
51 + """
52 +
53 + source_id: str = ""
54 + request_delay: float = 0.6 # politesse entre requêtes
55 + timeout: int = 60
56 +
57 + def __init__(self) -> None:
58 + self.session = requests.Session()
59 + self.session.headers["User-Agent"] = USER_AGENT
60 + self._last_request = 0.0
61 +
62 + def _throttle(self) -> None:
63 + wait = self.request_delay - (time.time() - self._last_request)
64 + if wait > 0:
65 + time.sleep(wait)
66 +
67 + def get(self, url: str, **kw) -> requests.Response:
68 + """GET direct avec throttling poli."""
69 + self._throttle()
70 + resp = self.session.get(url, timeout=self.timeout, **kw)
71 + self._last_request = time.time()
72 + resp.raise_for_status()
73 + return resp
74 +
75 + def get_json(self, url: str, **kw):
76 + return self.get(url, **kw).json()
77 +
78 + def get_rendered(self, url: str, render_js: bool = True) -> str:
79 + """HTML rendu via Scrapfly (anti-bot / JavaScript lourd).
80 +
81 + Nécessite SCRAPFLY_API_KEY dans l'environnement (.env). À réserver aux
82 + sources qui bloquent le GET direct — jamais pour les données ouvertes.
83 + """
84 + key = os.environ.get("SCRAPFLY_API_KEY")
85 + if not key:
86 + raise RuntimeError("SCRAPFLY_API_KEY manquant (voir .env)")
87 + self._throttle()
88 + resp = requests.get(SCRAPFLY_API, params={
89 + "key": key, "url": url, "country": "ca",
90 + "render_js": str(render_js).lower(), "asp": "true",
91 + }, timeout=120)
92 + self._last_request = time.time()
93 + resp.raise_for_status()
94 + return resp.json()["result"]["content"]
95 +
96 + # -- cache disque des fiches détail (connecteurs incrémentaux) ------------
97 + # Patron Lou-Ka (cache des pages détail) : les sources dont chaque fiche
98 + # coûte une requête (Scrapfly ou volume) ne re-scrapent que le neuf.
99 + @property
100 + def _cache_path(self) -> "Path":
101 + return Path(__file__).resolve().parents[2] / "data" / f"{self.source_id}_cache.json"
102 +
103 + def load_cache(self) -> dict:
104 + try:
105 + return json.loads(self._cache_path.read_text(encoding="utf-8"))
106 + except Exception:
107 + return {}
108 +
109 + def save_cache(self, cache: dict) -> None:
110 + self._cache_path.parent.mkdir(parents=True, exist_ok=True)
111 + self._cache_path.write_text(json.dumps(cache, ensure_ascii=False),
112 + encoding="utf-8")
113 +
114 + # -- fetch parallèle poli (enrichissements et backfills) -------------------
115 + def fetch_many(self, urls: list[str], worker, max_workers: int = 6) -> dict:
116 + """Applique `worker(url) -> valeur` sur chaque URL avec un pool de
117 + threads (chaque thread a sa propre session). Retourne {url: valeur} ;
118 + les exceptions donnent None. Concurrence modérée = politesse.
119 + """
120 + import threading
121 + from concurrent.futures import ThreadPoolExecutor
122 +
123 + local = threading.local()
124 +
125 + def _run(url):
126 + if not hasattr(local, "session"):
127 + local.session = requests.Session()
128 + local.session.headers["User-Agent"] = USER_AGENT
129 + try:
130 + return url, worker(url, local.session)
131 + except Exception:
132 + return url, None
133 +
134 + out: dict = {}
135 + with ThreadPoolExecutor(max_workers=max_workers) as pool:
136 + for url, val in pool.map(_run, urls):
137 + out[url] = val
138 + return out
139 +
140 + # -- interface ------------------------------------------------------------
141 + def fetch(self) -> list[Event]: # pragma: no cover - interface
142 + raise NotImplementedError
added sortika/connectors/evenko.py +113 −0
@@ -0,0 +1,113 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/evenko.py : connecteur evenko — grands concerts & festivals
5 +# Source : evenko.ca (promoteur : Centre Bell, Place Bell, MTELUS, Osheaga…)
6 +# Extraction: endpoint /api/search (proxy Algolia interne) — EXPLICITEMENT
7 +# permis par robots.txt (« Allow: /api/search* »). Format percé
8 +# dans leur bundle JS : GET ?body=base64(JSON{params:[query,
9 +# options], lang:"fr-CA"}). ~14 requêtes/sync (100 hits/page).
10 +# Accès : robots.txt Allow /api/search* ; GET throttlés, identification
11 +# honnête ; lien billets = URL Ticketmaster officielle du hit.
12 +# Prix : champ `free` de la source → is_free ; montants non exposés.
13 +# -----------------------------------------------------------------------------
14 +from __future__ import annotations
15 +
16 +import base64
17 +import json
18 +from datetime import datetime, timezone
19 +
20 +from ..schema import Event
21 +from .base import BaseConnector
22 +
23 +API = "https://evenko.ca/api/search"
24 +PAGE_SIZE = 100
25 +
26 +ATTRS = ["objectID", "title", "subtitle", "headliners", "venue", "show_date",
27 + "show_time", "hide_show_time", "category", "genre", "description",
28 + "free", "age", "thumbnail", "status", "additional_information",
29 + "presented_by"]
30 +
31 +# les salles evenko hors Québec sont exclues du périmètre (Sorti-Ka = QC)
32 +_EXCLUDED_REGIONS = ("ottawa", "ontario", "nouveau-brunswick", "nouvelle-écosse",
33 + "nouvelle-ecosse", "terre-neuve", "états-unis", "etats-unis",
34 + "île-du-prince-édouard", "ile-du-prince-edouard", "manitoba")
35 +
36 +
37 +def _unix_to_iso(ts) -> str | None:
38 + try:
39 + return datetime.fromtimestamp(int(ts), tz=timezone.utc).strftime("%Y-%m-%d")
40 + except (TypeError, ValueError, OSError):
41 + return None
42 +
43 +
44 +def _ticket_url(hit: dict) -> str:
45 + """URL billets officielle (fr_CA) depuis additional_information."""
46 + reg = (hit.get("additional_information") or {}).get("regular_ticket") or {}
47 + for entry in reg.get("regular_ticket_url") or []:
48 + if entry.get("locale") == "fr_CA" and entry.get("data"):
49 + return entry["data"]
50 + for entry in reg.get("regular_ticket_url") or []:
51 + if entry.get("data"):
52 + return entry["data"]
53 + return ""
54 +
55 +
56 +class EvenkoConnector(BaseConnector):
57 + source_id = "evenko"
58 +
59 + def _search_page(self, page: int) -> dict:
60 + payload = {"params": ["", {"hitsPerPage": PAGE_SIZE, "page": page,
61 + "filters": "entity_type:evenko_show",
62 + "attributesToRetrieve": ATTRS}],
63 + "lang": "fr-CA"}
64 + body = base64.b64encode(json.dumps(payload).encode()).decode()
65 + return self.get_json(API, params={"body": body})
66 +
67 + def fetch(self) -> list[Event]:
68 + events: list[Event] = []
69 + page, nb_pages = 0, 1
70 + while page < nb_pages:
71 + data = self._search_page(page)
72 + nb_pages = min(int(data.get("nbPages", 0)), 50) # garde-fou
73 + for hit in data.get("hits", []):
74 + ext_id = (hit.get("objectID") or "").removesuffix("_fr")
75 + title = hit.get("title") or ""
76 + if not ext_id or not title:
77 + continue
78 + venue = hit.get("venue") or {}
79 + region_lbl = (venue.get("region") or "").lower()
80 + if any(x in region_lbl for x in _EXCLUDED_REGIONS):
81 + continue
82 + if hit.get("subtitle"):
83 + title = f"{title} — {hit['subtitle']}"
84 + cats = [g.get("name", "") for g in hit.get("genre") or []]
85 + cat = hit.get("category")
86 + if isinstance(cat, list):
87 + cats += [str(c) for c in cat]
88 + elif cat:
89 + cats.append(str(cat))
90 + thumb = hit.get("thumbnail") or ""
91 + if thumb.startswith("//"):
92 + thumb = "https:" + thumb
93 + day = _unix_to_iso(hit.get("show_date")) or _unix_to_iso(hit.get("show_time"))
94 + events.append(Event(
95 + source=self.source_id,
96 + external_id=ext_id,
97 + url=_ticket_url(hit) or (venue.get("website") or "https://evenko.ca/fr/calendrier"),
98 + title=title,
99 + description=hit.get("description") or "",
100 + raw_categories=[c for c in cats if c] or ["Concert"],
101 + audience={"everyone": "Pour tous", "all_ages": "Pour tous",
102 + "18+": "18 ans et plus", "16+": "16 ans et plus",
103 + }.get(hit.get("age") or "", hit.get("age") or ""),
104 + venue=venue.get("name") or "",
105 + city=venue.get("city") or "",
106 + start_date=day,
107 + end_date=day,
108 + is_free=bool(hit["free"]) if hit.get("free") is not None else None,
109 + organizer="evenko",
110 + image=thumb,
111 + ))
112 + page += 1
113 + return events
added sortika/connectors/laval.py +59 −0
@@ -0,0 +1,59 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/laval.py : connecteur Ville de Laval — calendrier des activités
5 +# Source : donneesquebec.ca, jeu « Calendrier des activités » (laval.ca)
6 +# Extraction: fichier JSON officiel (données ouvertes, licence CC 4.0)
7 +# Couverture: ~300 activités municipales (bibliothèques, culture, famille,
8 +# plein air) avec dates, catégories, clientèles, lieux, images.
9 +# https://www.donneesquebec.ca/recherche/dataset/calendrier-des-activites
10 +# -----------------------------------------------------------------------------
11 +from __future__ import annotations
12 +
13 +from ..schema import Event
14 +from .base import BaseConnector
15 +
16 +FEED = ("https://www.donneesquebec.ca/recherche/dataset/"
17 + "df7add74-a741-4374-b3bd-22da8921c8b3/resource/"
18 + "b51a25de-bd06-4247-87ba-2b1ea8228005/download/calendrier-activites.json")
19 +SITE = "https://www.laval.ca"
20 +
21 +
22 +def _labels(items) -> list[str]:
23 + return [i.get("Label", "") for i in items or [] if i and i.get("Label")]
24 +
25 +
26 +class LavalConnector(BaseConnector):
27 + source_id = "laval"
28 +
29 + def fetch(self) -> list[Event]:
30 + events: list[Event] = []
31 + for rec in self.get_json(FEED):
32 + title = (rec.get("Title") or "").strip()
33 + page = (rec.get("PageUrl") or "").strip()
34 + if not title or not page:
35 + continue
36 + image = rec.get("ImageUrl") or ""
37 + # une activité récurrente = une entrée par occurrence, même PageUrl
38 + # → l'id externe inclut la date pour ne pas écraser les occurrences
39 + slug = page.rsplit("/", 1)[-1].removesuffix(".aspx")
40 + day = (rec.get("EventDate") or "")[:10]
41 + events.append(Event(
42 + source=self.source_id,
43 + external_id=f"{slug}-{day}" if day else slug,
44 + url=SITE + page,
45 + title=title,
46 + description=rec.get("Excerpt") or "",
47 + raw_categories=(_labels(rec.get("Categories"))
48 + + _labels(rec.get("Subcategories"))
49 + + _labels(rec.get("Tags"))),
50 + audience=", ".join(_labels(rec.get("Clienteles"))),
51 + venue=", ".join(_labels(rec.get("Locations"))),
52 + city="Laval",
53 + region="Laval",
54 + start_date=rec.get("EventDate"),
55 + end_date=rec.get("EndDate"),
56 + organizer="Ville de Laval",
57 + image=(SITE + image) if image.startswith("/") else image,
58 + ))
59 + return events
added sortika/connectors/lavitrine.py +162 −0
@@ -0,0 +1,162 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/lavitrine.py : connecteur La Vitrine culturelle (lavitrine.com)
5 +# Source : billetterie/calendrier culturel — spectacles, expositions,
6 +# concerts (Grand Montréal + Québec + tournées).
7 +# Extraction: sitemap.xml public (~2 000 fiches /fr/evenement/ + 185
8 +# /fr/exposition/, lastmod par URL) puis pages fiche rendues
9 +# serveur (h1, bloc .event_calendar : jour/mois/année, salle,
10 +# ville ; og:image). GET directs — pas d'anti-bot.
11 +# INCRÉMENTAL avec cache disque (data/lavitrine_cache.json) :
12 +# nouvelles fiches d'abord, re-visite roulante après 7 jours,
13 +# plafonné à LV_MAX_FETCH fiches/sync.
14 +# Accès : robots.txt « User-Agent: * / Allow: / » + Content-Signal
15 +# « search=yes, use=reference » → usage index + lien vers la
16 +# fiche originale, conforme aux signaux publiés (CLAUDE.md §15).
17 +# Prix : non exposés sur la fiche → is_free inconnu (jamais inventé).
18 +# -----------------------------------------------------------------------------
19 +from __future__ import annotations
20 +
21 +import re
22 +import time
23 +
24 +from ..normalize import clean_text
25 +from ..schema import Event
26 +from .base import BaseConnector
27 +
28 +SITEMAP = "https://www.lavitrine.com/sitemap.xml"
29 +LV_MAX_FETCH = int(__import__("os").environ.get("LV_MAX_FETCH", "400")) # fiches/sync (GET directs polis)
30 +REFRESH_AFTER = 7 * 86400 # re-visite roulante d'une fiche (secondes)
31 +
32 +_LOC_RE = re.compile(r"<loc>([^<]+)</loc>\s*(?:<lastmod>([^<]+)</lastmod>)?")
33 +_URL_BLOCK_RE = re.compile(r"<url>(.*?)</url>", re.S)
34 +_H1_RE = re.compile(r"<h1[^>]*>(.*?)</h1>", re.S)
35 +_OG_IMG_RE = re.compile(r'property="og:image" content="([^"]*)"')
36 +
37 +_MONTHS = {"jan": 1, "fév": 2, "fev": 2, "mar": 3, "avr": 4, "mai": 5,
38 + "juin": 6, "juil": 7, "aoû": 8, "aou": 8, "août": 8, "sep": 9,
39 + "oct": 10, "nov": 11, "déc": 12, "dec": 12}
40 +
41 +# une « slide » du calendrier : jour / mois / année, puis salle / ville
42 +_DATE_RE = re.compile(
43 + r'calender-date-text-large">(\d{1,2})</div>'
44 + r'<div class="event_calendar_slide-calender-date-text-small">([^<]+)</div>'
45 + r'<div class="event_calendar_slide-calender-date-text-small">(\d{4})</div>')
46 +_PLACE_RE = re.compile(r'class="text-style-2lines">([^<]*)</div><div>([^<]*)</div>')
47 +
48 +
49 +_SLIDE_A_RE = re.compile(
50 + r'<a[^>]*href="([^"]*)"[^>]*class="event_calendar_slide-content(.*?)</a>', re.S)
51 +
52 +
53 +def _slides(html: str) -> list[tuple[str, str, str, str, str, str, str]]:
54 + """Slides du calendrier : (id de fiche de la slide, url canonique, jour,
55 + mois, année, salle, ville).
56 +
57 + Le carrousel d'une fiche de tournée liste TOUTES les dates de la tournée,
58 + chacune avec l'id de SA fiche (/fr/evenement/<slug>/<ficheId>/<dateId>).
59 + On garde tout mais on identifie chaque représentation par l'id de sa
60 + slide : la même date vue depuis plusieurs fiches → même identifiant →
61 + aucun doublon à l'ingestion, aucune date perdue.
62 + """
63 + out = []
64 + for m in _SLIDE_A_RE.finditer(html):
65 + href, block = m.group(1), m.group(2)
66 + parts = href.strip("/").split("/")
67 + # …/evenement/<slug>/<ficheId>/<dateId>
68 + slide_id = parts[-2] if len(parts) >= 2 and parts[-2].isdigit() else ""
69 + canon = "https://www.lavitrine.com/" + "/".join(parts[:-1]) if slide_id else ""
70 + d = _DATE_RE.search(block)
71 + if not d:
72 + continue
73 + p = _PLACE_RE.search(block)
74 + out.append((slide_id, canon, d.group(1), d.group(2), d.group(3),
75 + p.group(1) if p else "", p.group(2) if p else ""))
76 + return out
77 +
78 +
79 +class LaVitrineConnector(BaseConnector):
80 + source_id = "lavitrine"
81 + request_delay = 0.8
82 +
83 + def _fiches(self) -> list[str]:
84 + xml = self.get(SITEMAP).text
85 + urls = []
86 + for block in _URL_BLOCK_RE.findall(xml):
87 + m = _LOC_RE.search(block)
88 + if not m:
89 + continue
90 + url = m.group(1)
91 + if "/fr/evenement/" in url or "/fr/exposition/" in url:
92 + urls.append(url)
93 + return urls
94 +
95 + def _parse_fiche(self, url: str, html: str) -> list[dict]:
96 + h1 = _H1_RE.search(html)
97 + title = clean_text(h1.group(1)) if h1 else ""
98 + if not title:
99 + return []
100 + img = _OG_IMG_RE.search(html)
101 + image = img.group(1) if img else ""
102 + ext = url.rstrip("/").rsplit("/", 1)[-1] # id numérique stable
103 + slug = url.rstrip("/").split("/")[-2]
104 + rows = []
105 + for slide_id, canon, day, month_txt, year, venue, city in _slides(html)[:60]:
106 + mo = _MONTHS.get(clean_text(month_txt).lower()[:4].rstrip(".")) \
107 + or _MONTHS.get(clean_text(month_txt).lower()[:3])
108 + if not mo:
109 + continue
110 + iso = f"{int(year):04d}-{mo:02d}-{int(day):02d}"
111 + rows.append({
112 + # id de la slide (fiche propre à cette représentation) : la
113 + # même date vue depuis plusieurs fiches → même uid, zéro doublon
114 + "external_id": f"{slide_id or ext}-{iso}",
115 + "url": canon or url, "title": title, "image": image,
116 + "raw_categories": [title, slug.replace("-", " ")],
117 + "venue": clean_text(venue or ""), "city": clean_text(city or ""),
118 + "start_date": iso, "end_date": iso,
119 + })
120 + return rows
121 +
122 + def fetch(self) -> list[Event]:
123 + cache = self.load_cache()
124 + urls = self._fiches()
125 + current = set(urls)
126 + cache = {u: c for u, c in cache.items() if u in current}
127 +
128 + now = time.time()
129 + # nouvelles fiches d'abord, puis les plus anciennes visites (roulant)
130 + candidates = sorted(
131 + (u for u in urls
132 + if u not in cache or now - cache[u].get("fetched_at", 0) > REFRESH_AFTER),
133 + key=lambda u: cache.get(u, {}).get("fetched_at", 0))[:LV_MAX_FETCH]
134 +
135 + def worker(url, session):
136 + return self._parse_fiche(url, session.get(url, timeout=20).text)
137 +
138 + for url, rows in self.fetch_many(candidates, worker, max_workers=4).items():
139 + if rows is None: # fiche cassée : on ne bloque pas la source
140 + print(f"[sorti-ka] lavitrine : fiche ignorée {url}")
141 + continue
142 + cache[url] = {"fetched_at": now, "rows": rows}
143 + self.save_cache(cache)
144 +
145 + events: list[Event] = []
146 + seen: set[str] = set()
147 + for entry in cache.values():
148 + for row in entry.get("rows", []):
149 + if row["external_id"] in seen: # même slide vue de 2 fiches
150 + continue
151 + seen.add(row["external_id"])
152 + events.append(Event(
153 + source=self.source_id,
154 + external_id=row["external_id"],
155 + url=row["url"], title=row["title"],
156 + raw_categories=row.get("raw_categories") or [],
157 + venue=row.get("venue", ""), city=row.get("city", ""),
158 + start_date=row.get("start_date"),
159 + end_date=row.get("end_date"),
160 + image=row.get("image", ""),
161 + ))
162 + return events
added sortika/connectors/lepointdevente.py +96 −0
@@ -0,0 +1,96 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/lepointdevente.py : connecteur Le point de vente (lepointdevente.com)
5 +# Source : billetterie québécoise indépendante — concerts, humour, théâtre,
6 +# festivals, partout au Québec (~1 500 événements à venir).
7 +# Extraction: pages de liste HTML rendues serveur (/billets?page=N, paginé),
8 +# parse regex des cartes (titre, date FR, salle + ville, image).
9 +# Aucun rendu JS nécessaire.
10 +# Accès : robots.txt = « User-Agent: * / Allow: * » (scraping permis) ;
11 +# GET throttlés 0,6 s, identification honnête, ~65 requêtes/sync.
12 +# Prix : non affichés sur les listes → is_free reste inconnu (on
13 +# n'invente JAMAIS un prix) ; chaque fiche pointe vers la
14 +# billetterie officielle pour le prix réel.
15 +# -----------------------------------------------------------------------------
16 +from __future__ import annotations
17 +
18 +import re
19 +
20 +from ..normalize import parse_date_fr
21 +from ..schema import Event
22 +from .base import BaseConnector
23 +
24 +BASE = "https://lepointdevente.com"
25 +
26 +_CARD_RE = re.compile(
27 + r'<a href="(/billets/[^"]+)"[^>]*class="feature-link[^"]*".*?'
28 + r'(?:<img[^>]*src="([^"]+)"[^>]*>.*?)?'
29 + r'<h3 class="feature-title">(?:<div[^>]*></div>)?([^<]+)</h3>\s*'
30 + r'<div class="feature-date">([^<]*)</div>\s*'
31 + r'<div class="feature-city">([^<]*)</div>',
32 + re.S)
33 +
34 +_PAGE_RE = re.compile(r'/billets\?page=(\d+)')
35 +
36 +
37 +class LePointDeVenteConnector(BaseConnector):
38 + source_id = "lepointdevente"
39 + request_delay = 0.8
40 +
41 + def _parse_page(self, html: str) -> list[Event]:
42 + events = []
43 + for m in _CARD_RE.finditer(html):
44 + path, image, title, date_txt, where = m.groups()
45 + # « Bar le Jockey, Montréal, QC » → salle = tout sauf ville + prov.
46 + parts = [p.strip() for p in (where or "").split(",")]
47 + city, venue = "", ""
48 + if parts:
49 + prov = parts[-1].upper()
50 + if prov in ("QC", "QUÉBEC", "QUEBEC"):
51 + parts = parts[:-1]
52 + elif prov in ("ON", "NB", "NS", "MB", "AB", "BC", "SK",
53 + "PE", "NL", "YT", "NT", "NU"):
54 + continue # hors Québec : hors périmètre Sorti-Ka
55 + if parts:
56 + city = parts[-1]
57 + venue = ", ".join(parts[:-1])
58 + events.append(Event(
59 + source=self.source_id,
60 + external_id=path.rsplit("/", 1)[-1],
61 + url=BASE + path,
62 + title=title,
63 + # la liste ne donne pas la catégorie : heuristique sur le titre
64 + # (« Festival… », « …en concert ») ; « autre » si rien ne matche
65 + raw_categories=[title],
66 + venue=venue,
67 + city=city,
68 + start_date=parse_date_fr(date_txt),
69 + image=image or "",
70 + ))
71 + return events
72 +
73 + def fetch(self) -> list[Event]:
74 + events: list[Event] = []
75 + html = self.get(f"{BASE}/billets").text
76 + events += self._parse_page(html)
77 + pages = [int(p) for p in _PAGE_RE.findall(html)]
78 + last = max(pages) if pages else 1
79 + for page in range(2, last + 1):
80 + html = self.get(f"{BASE}/billets?page={page}").text
81 + batch = self._parse_page(html)
82 + if not batch: # fin réelle de la pagination
83 + break
84 + events += batch
85 + # la pagination affichée peut grandir au fil des pages
86 + pages = [int(p) for p in _PAGE_RE.findall(html)]
87 + if pages:
88 + last = max(last, max(pages))
89 + # dédup interne (un événement vedette apparaît aussi dans la liste)
90 + seen, unique = set(), []
91 + for ev in events:
92 + if ev.external_id in seen:
93 + continue
94 + seen.add(ev.external_id)
95 + unique.append(ev)
96 + return unique
added sortika/connectors/montreal.py +114 −0
@@ -0,0 +1,114 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/montreal.py : connecteur Ville de Montréal — événements publics
5 +# Source : donnees.montreal.ca, jeu « Événements publics » (montreal.ca)
6 +# Extraction: API CKAN datastore_search (JSON, paginée) — données ouvertes,
7 +# Licence CC BY 4.0. Aucune restriction de scraping.
8 +# Couverture: ~5 600 événements municipaux (culture, conseils, famille,
9 +# sport, marchés) avec dates, coût, arrondissement, lat/lng.
10 +# Images : absentes du jeu de données → enrichissement incrémental par
11 +# og:image des fiches montreal.ca (cache disque, plafonné par
12 +# sync via MTL_IMG_MAX, défaut 300).
13 +# https://donnees.montreal.ca/dataset/evenements-publics
14 +# -----------------------------------------------------------------------------
15 +from __future__ import annotations
16 +
17 +import os
18 +import re
19 +
20 +from ..schema import Event
21 +from .base import BaseConnector
22 +
23 +API = "https://donnees.montreal.ca/api/3/action/datastore_search"
24 +RESOURCE_ID = "6decf611-6f11-4f34-bb36-324d804c9bad"
25 +PAGE = 1000
26 +IMG_MAX = int(os.environ.get("MTL_IMG_MAX", "300"))
27 +
28 +_OG_IMG_RE = re.compile(r'property="og:image" content="([^"]*)"')
29 +
30 +
31 +def _s(v) -> str:
32 + """Le datastore renvoie parfois la chaîne 'None' — la traiter comme vide."""
33 + s = str(v or "").strip()
34 + return "" if s.lower() in ("none", "null", "nan") else s
35 +
36 +
37 +class MontrealConnector(BaseConnector):
38 + source_id = "montreal"
39 + request_delay = 0.4
40 +
41 + def _enrich_images(self, events: list[Event]) -> None:
42 + """og:image des fiches montreal.ca — incrémental, cache disque,
43 + pool de threads (6). Le cache mémorise aussi les fiches SANS image
44 + ("") pour ne pas les re-visiter, et seuls les événements à venir
45 + sont visités (pas les archives).
46 + """
47 + from datetime import date
48 + today = date.today().isoformat()
49 + cache = self.load_cache()
50 + to_fetch = []
51 + for ev in events:
52 + if ev.url in cache:
53 + continue
54 + last_day = (ev.end_date or ev.start_date or "")[:10]
55 + if last_day >= today and len(to_fetch) < IMG_MAX:
56 + to_fetch.append(ev.url)
57 +
58 + def worker(url, session):
59 + m = _OG_IMG_RE.search(session.get(url, timeout=15).text)
60 + return m.group(1) if m else ""
61 +
62 + for url, img in self.fetch_many(to_fetch, worker, max_workers=6).items():
63 + cache[url] = img or ""
64 + self.save_cache(cache)
65 + for ev in events:
66 + if not ev.image:
67 + ev.image = cache.get(ev.url) or ""
68 +
69 + def fetch(self) -> list[Event]:
70 + events: list[Event] = []
71 + offset = 0
72 + while True:
73 + data = self.get_json(API, params={
74 + "resource_id": RESOURCE_ID, "limit": PAGE, "offset": offset})
75 + result = data.get("result", {})
76 + records = result.get("records", [])
77 + if not records:
78 + break
79 + for rec in records:
80 + title = _s(rec.get("titre"))
81 + url = _s(rec.get("url_fiche"))
82 + if not title or not url:
83 + continue
84 + # id stable : la fiche montreal.ca (le _id CKAN change à chaque
85 + # republication du jeu de données, l'URL de fiche non).
86 + ext_id = url.rstrip("/").rsplit("/", 1)[-1]
87 + cost = _s(rec.get("cout"))
88 + inscription = _s(rec.get("inscription"))
89 + events.append(Event(
90 + source=self.source_id,
91 + external_id=ext_id,
92 + url=url,
93 + title=title,
94 + description=_s(rec.get("description")),
95 + raw_categories=[_s(rec.get("type_evenement"))],
96 + audience=_s(rec.get("public_cible")),
97 + venue=_s(rec.get("titre_adresse")),
98 + address=_s(rec.get("adresse_principale")),
99 + city="Montréal",
100 + region="Montréal",
101 + postal_code=_s(rec.get("code_postal")),
102 + lat=_s(rec.get("lat")) or None,
103 + lng=_s(rec.get("long")) or None,
104 + start_date=_s(rec.get("date_debut")) or None,
105 + end_date=_s(rec.get("date_fin")) or None,
106 + price_label=" — ".join(p for p in (cost, inscription) if p),
107 + organizer="Ville de Montréal — "
108 + + (_s(rec.get("arrondissement")) or "ville-centre"),
109 + ))
110 + offset += PAGE
111 + if offset >= int(result.get("total", 0)):
112 + break
113 + self._enrich_images(events)
114 + return events
added sortika/connectors/sherbrooke.py +103 −0
@@ -0,0 +1,103 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/sherbrooke.py : connecteur Ville de Sherbrooke — calendrier officiel
5 +# Source : sherbrooke.ca, « Calendrier des événements en temps réel »
6 +# (référencé sur Données Québec : calendrier-des-evenements-en-temps-reel)
7 +# Extraction: la page rend TOUT le calendrier dans window['TRANSFER_CACHE']
8 +# (JSON embarqué, CMS Maruche) → 1 seule requête, ~270 événements
9 +# (nom, périodes, gratuité `payant`, catégories, clientèles,
10 +# images /Fichiers/<module>/Entities/<id>/<mainPicture>).
11 +# Accès : page municipale publique, GET direct, identification honnête.
12 +# Prix : booléen `payant` de la source → is_free (montants non exposés).
13 +# -----------------------------------------------------------------------------
14 +from __future__ import annotations
15 +
16 +import json
17 +
18 +from ..schema import Event
19 +from .base import BaseConnector
20 +
21 +PAGE = ("https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/"
22 + "calendrier-des-activites")
23 +SITE = "https://www.sherbrooke.ca"
24 +# GUID du module « fichiers d'entités » du calendrier (constaté via og:image)
25 +FILES_MODULE = "3337a882-4a53-e611-80ea-00155d09650f"
26 +
27 +
28 +def _find_events(o):
29 + """Localise la liste d'événements (dicts avec visibleId) dans le cache JSON."""
30 + if isinstance(o, list) and o and isinstance(o[0], dict) and "visibleId" in o[0]:
31 + return o
32 + if isinstance(o, dict):
33 + for v in o.values():
34 + r = _find_events(v)
35 + if r:
36 + return r
37 + if isinstance(o, list):
38 + for v in o:
39 + r = _find_events(v)
40 + if r:
41 + return r
42 + return None
43 +
44 +
45 +class SherbrookeConnector(BaseConnector):
46 + source_id = "sherbrooke"
47 +
48 + def _extract_cache(self, html: str) -> dict:
49 + i = html.find("window['TRANSFER_CACHE'] =")
50 + if i < 0:
51 + raise ValueError("TRANSFER_CACHE introuvable (page modifiée ?)")
52 + start = html.find("{", i)
53 + end = html.find("</script>", start)
54 + return json.loads(html[start:end].strip().rstrip(";"))
55 +
56 + def fetch(self) -> list[Event]:
57 + html = self.get(PAGE).text
58 + records = _find_events(self._extract_cache(html)) or []
59 + events: list[Event] = []
60 + for rec in records:
61 + title = (rec.get("name") or {}).get("fr", "")
62 + full_url = (rec.get("fullUrl") or {}).get("fr", "")
63 + periods = rec.get("periods") or []
64 + if not title or not full_url or not periods:
65 + continue
66 + start = (periods[0].get("dateDebut") or "")[:10]
67 + end = (periods[-1].get("dateFin") or "")[:10]
68 +
69 + cats, audience = [], []
70 + c = rec.get("categories") or {}
71 + cats += [(b.get("name") or {}).get("fr", "") for b in c.get("base") or []]
72 + for block in c.get("block") or []:
73 + label = (block.get("name") or {}).get("fr", "")
74 + children = [(ch.get("name") or {}).get("fr", "")
75 + for ch in block.get("children") or []]
76 + if label == "Clientèle":
77 + audience += children
78 + elif label != "Secteur":
79 + cats += children
80 +
81 + image = ""
82 + if rec.get("mainPicture") and rec.get("id"):
83 + image = (f"{SITE}/Fichiers/{FILES_MODULE}/Entities/"
84 + f"{rec['id']}/{rec['mainPicture']}")
85 +
86 + payant = rec.get("payant")
87 + events.append(Event(
88 + source=self.source_id,
89 + external_id=str(rec.get("visibleId") or rec.get("id")),
90 + url=SITE + full_url,
91 + title=title,
92 + description=(rec.get("meta") or {}).get("fr", ""),
93 + raw_categories=[x for x in cats if x],
94 + audience=", ".join(a for a in audience if a),
95 + city="Sherbrooke",
96 + region="Estrie",
97 + start_date=start or None,
98 + end_date=end or start or None,
99 + is_free=(not payant) if isinstance(payant, bool) else None,
100 + organizer="Ville de Sherbrooke",
101 + image=image,
102 + ))
103 + return events
added sortika/connectors/sitq.py +152 −0
@@ -0,0 +1,152 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# connectors/sitq.py : connecteur SIT Québec — Ministère du Tourisme
5 +# Source : Système d'information touristique Québec (Données Québec)
6 +# Extraction: API JSON officielle Tourinsoft (syndication publique)
7 +# Accès : données ouvertes — Licence CC 4.0 (Données Québec), API publiée
8 +# par le Ministère du Tourisme. Aucune restriction de scraping.
9 +# Couverture: ~1 600 événements touristiques, TOUTES les régions du Québec
10 +# (festivals, salons, manifestations sportives et culturelles),
11 +# avec région touristique, thèmes, dates, géolocalisation, site web.
12 +# https://www.donneesquebec.ca/recherche/dataset/sit-quebec-evenements
13 +# -----------------------------------------------------------------------------
14 +from __future__ import annotations
15 +
16 +import os
17 +import re
18 +
19 +from ..schema import Event
20 +from .base import BaseConnector
21 +
22 +FEED = ("https://api-v3.tourinsoft.com/api/syndications/mto.tourinsoft.com/"
23 + "c6ab1c9e-f594-463e-b036-5a029197f3a9?format=json")
24 +
25 +# le flux ne contient pas de photos → enrichissement og:image du site officiel
26 +# de chaque événement (cache disque, plafonné par sync via SITQ_IMG_MAX)
27 +IMG_MAX = int(os.environ.get("SITQ_IMG_MAX", "200"))
28 +_OG_IMG_RE = re.compile(r'property="og:image"\s+content="([^"]*)"')
29 +
30 +
31 +def _first(lst, *keys, default=None):
32 + """Descend prudemment dans les structures imbriquées Tourinsoft."""
33 + cur = lst
34 + for k in keys:
35 + if isinstance(cur, list):
36 + cur = cur[0] if cur else None
37 + if isinstance(cur, dict):
38 + cur = cur.get(k)
39 + if cur is None:
40 + return default
41 + if isinstance(cur, list):
42 + cur = cur[0] if cur else default
43 + return cur
44 +
45 +
46 +def _thes_labels(groups, inner_key) -> list[str]:
47 + """Extrait tous les ThesLibelle d'une liste de groupes Tourinsoft."""
48 + out: list[str] = []
49 + for g in groups or []:
50 + for item in g.get(inner_key) or []:
51 + lbl = (item or {}).get("ThesLibelle", "")
52 + if lbl and lbl not in out:
53 + out.append(lbl)
54 + return out
55 +
56 +
57 +class SitqConnector(BaseConnector):
58 + source_id = "sitq"
59 + request_delay = 0.3
60 + timeout = 15
61 +
62 + def _enrich_images(self, events: list[Event]) -> None:
63 + """og:image du site web officiel — incrémental, cache disque, pool de
64 + threads (8), tolérant (sites hétérogènes : échec ou absence = ""
65 + mémorisé, jamais re-visité). Événements à venir seulement."""
66 + from datetime import date
67 + today = date.today().isoformat()
68 + cache = self.load_cache()
69 + to_fetch = []
70 + for ev in events:
71 + site = ev.website
72 + if not site or site in cache or site in to_fetch:
73 + continue
74 + last_day = (ev.end_date or ev.start_date or "")[:10]
75 + if (not last_day or last_day >= today) and len(to_fetch) < IMG_MAX:
76 + to_fetch.append(site)
77 +
78 + def worker(url, session):
79 + m = _OG_IMG_RE.search(session.get(url, timeout=10).text)
80 + img = m.group(1) if m else ""
81 + if img.startswith("//"):
82 + img = "https:" + img
83 + return img if img.startswith("http") else ""
84 +
85 + for url, img in self.fetch_many(to_fetch, worker, max_workers=8).items():
86 + cache[url] = img or ""
87 + self.save_cache(cache)
88 + for ev in events:
89 + if not ev.image and ev.website:
90 + ev.image = cache.get(ev.website) or ""
91 +
92 + def fetch(self) -> list[Event]:
93 + payload = self.get_json(FEED)
94 + events: list[Event] = []
95 + for rec in payload.get("value", []):
96 + ext_id = rec.get("SyndicObjectID", "")
97 + title = rec.get("SyndicObjectName", "")
98 + if not ext_id or not title:
99 + continue
100 +
101 + addr = _first(rec.get("Adresses"), default={}) or {}
102 + geo = _first(rec.get("Geolocalisations"), default={}) or {}
103 + period = _first(rec.get("PeriodeOuvertures"), default={}) or {}
104 + struct = rec.get("Structure") or {}
105 +
106 + address = ", ".join(
107 + p for p in (addr.get("Numerovoie", ""), addr.get("Libellevoie", ""))
108 + if p) or addr.get("Adresse1", "")
109 +
110 + website = ""
111 + for site in rec.get("SiteInternets") or []:
112 + url = (site or {}).get("Coordonnees", "")
113 + if url.startswith("http"):
114 + website = url
115 + break
116 +
117 + raw_cats = _thes_labels(rec.get("Identifications") and [
118 + {"x": [i.get("Typedetablissementoudequipement") or {}
119 + for i in rec["Identifications"]]}], "x")
120 + raw_cats += _thes_labels(rec.get("Themes"), "Theme")
121 + raw_cats += _thes_labels(
122 + rec.get("ActivitesRecreativesCulturelless"),
123 + "Activitesrecreativesetculturelles")
124 +
125 + audience = ", ".join(_thes_labels(
126 + rec.get("ClientelesAdmisess"), "Clientelesadmises"))
127 +
128 + tourist_region = _first(
129 + rec.get("RegionsDesserviess"),
130 + "Regionstouristiques", "ThesLibelle", default="") or ""
131 +
132 + events.append(Event(
133 + source=self.source_id,
134 + external_id=ext_id,
135 + url=website or "https://www.quebecoriginal.com/",
136 + title=title,
137 + raw_categories=raw_cats,
138 + audience=audience,
139 + venue=addr.get("Nomdupointdeservice", "") or "",
140 + address=address,
141 + city=addr.get("Municipalite", "") or "",
142 + tourist_region=tourist_region,
143 + postal_code=addr.get("CodePostal", "") or "",
144 + lat=geo.get("Latitude"),
145 + lng=geo.get("Longitude"),
146 + start_date=period.get("Datedebut"),
147 + end_date=period.get("Datefin"),
148 + organizer=struct.get("Name", "") or "",
149 + website=website,
150 + ))
151 + self._enrich_images(events)
152 + return events
added sortika/db.py +205 −0
@@ -0,0 +1,205 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# db.py : persistance SQLite — upsert avec détection de changements,
5 +# cycle de vie avec délai de grâce (2 syncs), détection de dérive,
6 +# déduplication inter-sources par empreinte (patron Lou-Ka : louka/db.py)
7 +# -----------------------------------------------------------------------------
8 +from __future__ import annotations
9 +
10 +import json
11 +import sqlite3
12 +import statistics
13 +import time
14 +from pathlib import Path
15 +
16 +from .schema import Event
17 +
18 +DB_PATH = Path(__file__).resolve().parent.parent / "data" / "sortika.db"
19 +
20 +# Nombre d'exécutions consécutives où un événement doit être absent de la
21 +# source avant d'être désactivé (délai de grâce contre les ratés ponctuels).
22 +MISS_GRACE = 2
23 +
24 +# Dérive : si une source retourne <= DRIFT_RATIO × sa médiane historique
25 +# (médiane >= DRIFT_MIN_BASE événements), on alerte et on suspend les retraits.
26 +DRIFT_RATIO = 0.25
27 +DRIFT_MIN_BASE = 8
28 +DRIFT_HISTORY = 5
29 +
30 +_SCHEMA = """
31 +CREATE TABLE IF NOT EXISTS events (
32 + uid TEXT PRIMARY KEY,
33 + source TEXT NOT NULL,
34 + external_id TEXT NOT NULL,
35 + url TEXT,
36 + title TEXT,
37 + description TEXT,
38 + categories TEXT, -- JSON (taxonomie canonique)
39 + raw_categories TEXT, -- JSON (libellés source)
40 + audience TEXT,
41 + venue TEXT,
42 + address TEXT,
43 + city TEXT,
44 + region TEXT,
45 + tourist_region TEXT,
46 + postal_code TEXT,
47 + lat REAL,
48 + lng REAL,
49 + start_date TEXT,
50 + end_date TEXT,
51 + is_free INTEGER, -- 1 gratuit / 0 payant / NULL inconnu
52 + price_min REAL,
53 + price_label TEXT,
54 + organizer TEXT,
55 + website TEXT,
56 + image TEXT,
57 + dedup_key TEXT,
58 + content_hash TEXT,
59 + first_seen REAL,
60 + last_seen REAL,
61 + updated_at REAL,
62 + miss_count INTEGER DEFAULT 0,
63 + active INTEGER DEFAULT 1
64 +);
65 +CREATE INDEX IF NOT EXISTS idx_events_source ON events(source);
66 +CREATE INDEX IF NOT EXISTS idx_events_active ON events(active);
67 +CREATE INDEX IF NOT EXISTS idx_events_region ON events(region);
68 +CREATE INDEX IF NOT EXISTS idx_events_start ON events(start_date);
69 +CREATE INDEX IF NOT EXISTS idx_events_dedup ON events(dedup_key);
70 +
71 +CREATE TABLE IF NOT EXISTS sync_log (
72 + id INTEGER PRIMARY KEY AUTOINCREMENT,
73 + source TEXT NOT NULL,
74 + ts REAL NOT NULL,
75 + fetched INTEGER,
76 + added INTEGER,
77 + updated INTEGER,
78 + removed INTEGER,
79 + error TEXT
80 +);
81 +"""
82 +
83 +
84 +def connect(path: Path | str | None = None) -> sqlite3.Connection:
85 + p = Path(path) if path else DB_PATH
86 + p.parent.mkdir(parents=True, exist_ok=True)
87 + con = sqlite3.connect(p)
88 + con.row_factory = sqlite3.Row
89 + # plusieurs connecteurs peuvent synchroniser en parallèle (backfill) :
90 + # attendre le verrou plutôt que d'échouer, et journal WAL pour les lecteurs
91 + con.execute("PRAGMA busy_timeout=60000")
92 + try:
93 + con.execute("PRAGMA journal_mode=WAL")
94 + except sqlite3.OperationalError:
95 + pass
96 + con.executescript(_SCHEMA)
97 + return con
98 +
99 +
100 +def _event_row(ev: Event, now: float) -> dict:
101 + return {
102 + "uid": ev.uid, "source": ev.source, "external_id": ev.external_id,
103 + "url": ev.url, "title": ev.title, "description": ev.description,
104 + "categories": json.dumps(ev.categories, ensure_ascii=False),
105 + "raw_categories": json.dumps(ev.raw_categories, ensure_ascii=False),
106 + "audience": ev.audience, "venue": ev.venue, "address": ev.address,
107 + "city": ev.city, "region": ev.region, "tourist_region": ev.tourist_region,
108 + "postal_code": ev.postal_code, "lat": ev.lat, "lng": ev.lng,
109 + "start_date": ev.start_date, "end_date": ev.end_date,
110 + "is_free": (None if ev.is_free is None else int(ev.is_free)),
111 + "price_min": ev.price_min, "price_label": ev.price_label,
112 + "organizer": ev.organizer, "website": ev.website, "image": ev.image,
113 + "dedup_key": ev.dedup_key(), "content_hash": ev.content_hash(),
114 + "now": now,
115 + }
116 +
117 +
118 +def sync_source(con: sqlite3.Connection, source: str, events: list[Event]) -> dict:
119 + """Synchronise une source : ajouts / mises à jour / retraits avec grâce."""
120 + now = time.time()
121 + stats = {"source": source, "fetched": len(events),
122 + "added": 0, "updated": 0, "unchanged": 0, "removed": 0}
123 + alert = None
124 +
125 + # Dérive : volume anormalement bas → on suspend les retraits.
126 + hist = [r["fetched"] for r in con.execute(
127 + "SELECT fetched FROM sync_log WHERE source=? AND error IS NULL "
128 + "ORDER BY ts DESC LIMIT ?", (source, DRIFT_HISTORY))]
129 + if hist:
130 + med = statistics.median(hist)
131 + if med >= DRIFT_MIN_BASE and len(events) <= med * DRIFT_RATIO:
132 + alert = (f"dérive : {len(events)} événements reçus vs médiane {med:.0f} "
133 + f"— retraits suspendus (connecteur probablement cassé)")
134 +
135 + seen: set[str] = set()
136 + for ev in events:
137 + row = _event_row(ev, now)
138 + seen.add(ev.uid)
139 + cur = con.execute("SELECT content_hash FROM events WHERE uid=?", (ev.uid,))
140 + old = cur.fetchone()
141 + if old is None:
142 + con.execute(
143 + """INSERT INTO events (uid, source, external_id, url, title,
144 + description, categories, raw_categories, audience, venue,
145 + address, city, region, tourist_region, postal_code, lat, lng,
146 + start_date, end_date, is_free, price_min, price_label,
147 + organizer, website, image, dedup_key, content_hash,
148 + first_seen, last_seen, updated_at, miss_count, active)
149 + VALUES (:uid,:source,:external_id,:url,:title,:description,
150 + :categories,:raw_categories,:audience,:venue,:address,:city,
151 + :region,:tourist_region,:postal_code,:lat,:lng,:start_date,
152 + :end_date,:is_free,:price_min,:price_label,:organizer,
153 + :website,:image,:dedup_key,:content_hash,
154 + :now,:now,:now,0,1)""", row)
155 + stats["added"] += 1
156 + elif old["content_hash"] != row["content_hash"]:
157 + con.execute(
158 + """UPDATE events SET url=:url, title=:title,
159 + description=:description, categories=:categories,
160 + raw_categories=:raw_categories, audience=:audience,
161 + venue=:venue, address=:address, city=:city, region=:region,
162 + tourist_region=:tourist_region, postal_code=:postal_code,
163 + lat=:lat, lng=:lng, start_date=:start_date,
164 + end_date=:end_date, is_free=:is_free, price_min=:price_min,
165 + price_label=:price_label, organizer=:organizer,
166 + website=:website, image=:image, dedup_key=:dedup_key,
167 + content_hash=:content_hash, last_seen=:now, updated_at=:now,
168 + miss_count=0, active=1 WHERE uid=:uid""", row)
169 + stats["updated"] += 1
170 + else:
171 + con.execute("UPDATE events SET last_seen=?, miss_count=0, active=1 "
172 + "WHERE uid=?", (now, ev.uid))
173 + stats["unchanged"] += 1
174 +
175 + # Retraits (avec délai de grâce), sauf en cas de dérive détectée.
176 + if not alert:
177 + for r in con.execute(
178 + "SELECT uid, miss_count FROM events WHERE source=? AND active=1",
179 + (source,)):
180 + if r["uid"] in seen:
181 + continue
182 + miss = r["miss_count"] + 1
183 + if miss >= MISS_GRACE:
184 + con.execute("UPDATE events SET active=0, miss_count=?, "
185 + "updated_at=? WHERE uid=?", (miss, now, r["uid"]))
186 + stats["removed"] += 1
187 + else:
188 + con.execute("UPDATE events SET miss_count=? WHERE uid=?",
189 + (miss, r["uid"]))
190 +
191 + con.execute("INSERT INTO sync_log (source, ts, fetched, added, updated, "
192 + "removed, error) VALUES (?,?,?,?,?,?,NULL)",
193 + (source, now, stats["fetched"], stats["added"],
194 + stats["updated"], stats["removed"]))
195 + con.commit()
196 + if alert:
197 + stats["alert"] = alert
198 + return stats
199 +
200 +
201 +def log_failure(con: sqlite3.Connection, source: str, error: str) -> None:
202 + con.execute("INSERT INTO sync_log (source, ts, fetched, added, updated, "
203 + "removed, error) VALUES (?,?,0,0,0,0,?)",
204 + (source, time.time(), error[:500]))
205 + con.commit()
added sortika/ingest.py +67 −0
@@ -0,0 +1,67 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# ingest.py : pipeline d'ingestion — exécute les connecteurs et synchronise
5 +# la base (ajouts / mises à jour / retraits) = contenu toujours à
6 +# jour (patron Lou-Ka : louka/ingest.py)
7 +# -----------------------------------------------------------------------------
8 +from __future__ import annotations
9 +
10 +import sys
11 +import time
12 +import traceback
13 +
14 +from . import db
15 +from .connectors import CONNECTORS
16 +
17 +
18 +def run(sources: list[str] | None = None) -> list[dict]:
19 + """Exécute l'ingestion pour toutes les sources (ou celles demandées)."""
20 + con = db.connect()
21 + results = []
22 + targets = sources or list(CONNECTORS.keys())
23 + for sid in targets:
24 + cls = CONNECTORS.get(sid)
25 + if cls is None:
26 + print(f"[sorti-ka] connecteur inconnu : {sid}", file=sys.stderr)
27 + continue
28 + t0 = time.time()
29 + print(f"[sorti-ka] sync {sid} ...")
30 + try:
31 + events = cls().fetch()
32 + finalized, dropped = [], 0
33 + for ev in events:
34 + try:
35 + finalized.append(ev.finalize())
36 + except Exception: # un événement malformé ne bloque pas la source
37 + dropped += 1
38 + stats = db.sync_source(con, sid, finalized)
39 + stats["seconds"] = round(time.time() - t0, 1)
40 + if dropped:
41 + stats["dropped"] = dropped
42 + if stats.get("alert"):
43 + print(f"[sorti-ka] ⚠ ALERTE {sid} : {stats['alert']}")
44 + print(f"[sorti-ka] {stats}")
45 + results.append(stats)
46 + except Exception as exc: # robustesse : une source ne bloque pas les autres
47 + db.log_failure(con, sid, f"{exc}")
48 + traceback.print_exc()
49 + results.append({"source": sid, "error": str(exc)})
50 + try:
51 + con.execute("PRAGMA optimize")
52 + except Exception:
53 + pass
54 + con.close()
55 + return results
56 +
57 +
58 +def watch(interval_seconds: int = 6 * 3600) -> None:
59 + """Boucle de rafraîchissement périodique (pseudo-webhook par sondage).
60 +
61 + Cadence par défaut : 6 h — les calendriers d'événements bougent plus vite
62 + qu'un parc locatif, mais les sources ouvertes n'exigent pas de temps réel.
63 + """
64 + while True:
65 + run()
66 + print(f"[sorti-ka] prochaine synchro dans {interval_seconds // 60} min")
67 + time.sleep(interval_seconds)
added sortika/normalize.py +201 −0
@@ -0,0 +1,201 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# normalize.py : couche de normalisation commune — chaque connecteur retourne
5 +# les champs bruts de sa source, ce module uniformise tout :
6 +# dates ISO, prix/gratuité, catégories canoniques, villes, texte.
7 +# (patron Lou-Ka : louka/normalize.py)
8 +# -----------------------------------------------------------------------------
9 +from __future__ import annotations
10 +
11 +import re
12 +import unicodedata
13 +from html import unescape as html_unescape
14 +
15 +# ---------------------------------------------------------------------------
16 +# Utilitaires texte
17 +# ---------------------------------------------------------------------------
18 +
19 +_NBSP = (" ", " ", " ")
20 +
21 +
22 +def strip_accents(s: str) -> str:
23 + return "".join(c for c in unicodedata.normalize("NFD", s)
24 + if unicodedata.category(c) != "Mn")
25 +
26 +
27 +def clean_text(s: str) -> str:
28 + """Espaces insécables -> espaces, HTML résiduel enlevé, entités décodées."""
29 + if not s:
30 + return ""
31 + for ch in _NBSP:
32 + s = s.replace(ch, " ")
33 + s = re.sub(r"<[^>]+>", " ", s) # balises HTML résiduelles
34 + s = html_unescape(s) # &amp; &#039; &eacute; …
35 + return re.sub(r"\s+", " ", s).strip()
36 +
37 +
38 +def _key(s: str) -> str:
39 + """Texte en minuscules sans accents, pour matcher des mots-clés."""
40 + return strip_accents(clean_text(s or "")).lower()
41 +
42 +
43 +def clean_city(raw: str) -> str:
44 + """Nettoie une ville : « Montréal (Québec) » → « Montréal », casse conservée."""
45 + s = clean_text(raw)
46 + s = re.sub(r"\s*\((qu[eé]bec|qc)\)\s*$", "", s, flags=re.I)
47 + s = re.sub(r",\s*(qu[eé]bec|qc|canada)\s*$", "", s, flags=re.I)
48 + return s.strip(" ,")
49 +
50 +
51 +# ---------------------------------------------------------------------------
52 +# Dates → ISO "YYYY-MM-DD"
53 +# ---------------------------------------------------------------------------
54 +
55 +_ISO_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})")
56 +
57 +_FR_MONTHS = {
58 + "janvier": 1, "fevrier": 2, "mars": 3, "avril": 4, "mai": 5, "juin": 6,
59 + "juillet": 7, "aout": 8, "septembre": 9, "octobre": 10, "novembre": 11,
60 + "decembre": 12, "jan": 1, "fev": 2, "mar": 3, "avr": 4, "juil": 7,
61 + "sept": 9, "sep": 9, "oct": 10, "nov": 11, "dec": 12,
62 +}
63 +_FR_DATE_RE = re.compile(r"(\d{1,2})(?:er)?\s+([a-z]+)\.?\s+(\d{4})")
64 +
65 +
66 +def parse_date_iso(raw: str | None) -> str | None:
67 + """Extrait une date ISO d'un champ source ("2026-09-11T00:00:00" → "2026-09-11")."""
68 + if not raw:
69 + return None
70 + m = _ISO_RE.match(str(raw).strip())
71 + if not m:
72 + return None
73 + y, mo, d = int(m.group(1)), int(m.group(2)), int(m.group(3))
74 + if not (1990 <= y <= 2100 and 1 <= mo <= 12 and 1 <= d <= 31):
75 + return None
76 + return f"{y:04d}-{mo:02d}-{d:02d}"
77 +
78 +
79 +def parse_date_fr(raw: str | None) -> str | None:
80 + """Date française textuelle → ISO ("17 août 2026, 18h00" → "2026-08-17")."""
81 + if not raw:
82 + return None
83 + k = _key(str(raw))
84 + m = _FR_DATE_RE.search(k)
85 + if not m:
86 + return parse_date_iso(raw)
87 + d, month_word, y = int(m.group(1)), m.group(2), int(m.group(3))
88 + mo = _FR_MONTHS.get(month_word)
89 + if not mo or not (1 <= d <= 31 and 1990 <= y <= 2100):
90 + return None
91 + return f"{y:04d}-{mo:02d}-{d:02d}"
92 +
93 +
94 +# ---------------------------------------------------------------------------
95 +# Prix / gratuité — l'équivalent Sorti-Ka du price_context de Resto-Ka :
96 +# ne jamais afficher un coût sans savoir d'où il vient (price_label conservé).
97 +# ---------------------------------------------------------------------------
98 +
99 +_PRICE_RE = re.compile(r"(\d{1,4}(?:[.,]\d{2})?)\s*\$")
100 +_FREE_WORDS = ("gratuit", "entree libre", "entrée libre", "free", "sans frais")
101 +
102 +
103 +def parse_price(raw: str | None) -> tuple[bool | None, float | None, str]:
104 + """Retourne (is_free, price_min, price_label) à partir du texte source.
105 +
106 + is_free : True (gratuit), False (payant), None (inconnu).
107 + price_min : plus bas montant trouvé en $ CAD, sinon None.
108 + price_label : texte original nettoyé (traçabilité, jamais inventé).
109 + """
110 + label = clean_text(raw or "")
111 + if not label:
112 + return None, None, ""
113 + k = _key(label)
114 + amounts = [float(m.replace(",", ".")) for m in _PRICE_RE.findall(label)]
115 + if any(w in k for w in _FREE_WORDS) and not amounts:
116 + return True, None, label
117 + if amounts:
118 + return False, min(amounts), label
119 + if "payant" in k or "billet" in k or "admission" in k:
120 + return False, None, label
121 + return None, None, label
122 +
123 +
124 +# ---------------------------------------------------------------------------
125 +# Catégories canoniques (taxonomie Sorti-Ka, multi-valué)
126 +# ---------------------------------------------------------------------------
127 +
128 +CATEGORIES: list[str] = [
129 + "festival", "musique", "arts-scene", "exposition-musee", "cinema",
130 + "sport", "plein-air", "famille", "gastronomie", "marche-foire",
131 + "conference", "communautaire", "patrimoine", "autre",
132 +]
133 +
134 +# Mots-clés (sans accents, minuscules) → catégorie canonique. L'ordre compte :
135 +# le premier match l'emporte pour un même libellé source.
136 +_CATEGORY_RULES: list[tuple[str, str]] = [
137 + ("festival", "festival"),
138 + ("chanson", "musique"), ("musique", "musique"), ("concert", "musique"),
139 + ("opera", "musique"), ("orchestre", "musique"), ("rock", "musique"),
140 + ("pop", "musique"), ("metal", "musique"), ("punk", "musique"),
141 + ("hip-hop", "musique"), ("hip hop", "musique"), ("rap", "musique"),
142 + ("jazz", "musique"), ("blues", "musique"), ("electro", "musique"),
143 + ("country et folk", "musique"), ("folk", "musique"), ("dj set", "musique"),
144 + ("comedie musicale", "arts-scene"),
145 + ("theatre", "arts-scene"), ("danse", "arts-scene"), ("humour", "arts-scene"),
146 + ("cirque", "arts-scene"), ("spectacle", "arts-scene"), ("arts de la scene", "arts-scene"),
147 + ("exposition", "exposition-musee"), ("musee", "exposition-musee"),
148 + ("galerie", "exposition-musee"), ("arts visuels", "exposition-musee"),
149 + ("cinema", "cinema"), ("film", "cinema"), ("projection", "cinema"),
150 + ("manifestation sportive", "sport"), ("sport", "sport"), ("course", "sport"),
151 + ("marathon", "sport"), ("tournoi", "sport"), ("hockey", "sport"),
152 + ("plein air", "plein-air"), ("randonnee", "plein-air"), ("velo", "plein-air"),
153 + ("nature", "plein-air"), ("parc", "plein-air"),
154 + ("famille", "famille"), ("enfant", "famille"), ("jeunesse", "famille"),
155 + ("conte", "famille"),
156 + ("gastronomie", "gastronomie"), ("gourmand", "gastronomie"),
157 + ("vin", "gastronomie"), ("biere", "gastronomie"), ("bouffe", "gastronomie"),
158 + ("cabane a sucre", "gastronomie"),
159 + ("salon / foire", "marche-foire"), ("salon", "marche-foire"),
160 + ("foire", "marche-foire"), ("marche", "marche-foire"), ("braderie", "marche-foire"),
161 + ("vente-debarras", "marche-foire"),
162 + ("conference", "conference"), ("atelier", "conference"),
163 + ("formation", "conference"), ("colloque", "conference"), ("causerie", "conference"),
164 + ("conseil et comite", "communautaire"), ("assemblee", "communautaire"),
165 + ("seance", "communautaire"), ("collecte", "communautaire"),
166 + ("communautaire", "communautaire"), ("benevol", "communautaire"),
167 + ("patrimoine", "patrimoine"), ("histoire", "patrimoine"),
168 + ("historique", "patrimoine"), ("commemoration", "patrimoine"),
169 + ("celebration", "festival"), ("fete", "festival"), ("carnaval", "festival"),
170 + ("defile", "festival"), ("feux", "festival"),
171 +]
172 +
173 +
174 +def map_categories(labels: list[str] | None) -> list[str]:
175 + """Mappe des libellés source (types, thèmes) vers la taxonomie canonique."""
176 + out: list[str] = []
177 + for raw in labels or []:
178 + k = _key(raw)
179 + if not k:
180 + continue
181 + for kw, cat in _CATEGORY_RULES:
182 + if kw in k:
183 + if cat not in out:
184 + out.append(cat)
185 + break
186 + return out or ["autre"]
187 +
188 +
189 +# ---------------------------------------------------------------------------
190 +# Géolocalisation
191 +# ---------------------------------------------------------------------------
192 +
193 +def parse_latlng(lat, lng) -> tuple[float | None, float | None]:
194 + """Valide une paire lat/lng (bornes du Québec, large)."""
195 + try:
196 + la, ln = float(lat), float(lng)
197 + except (TypeError, ValueError):
198 + return None, None
199 + if not (44.0 <= la <= 63.0 and -80.0 <= ln <= -56.0):
200 + return None, None
201 + return round(la, 6), round(ln, 6)
added sortika/regions.py +173 −0
@@ -0,0 +1,173 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# regions.py : les 17 régions administratives du Québec + correspondances
5 +# région touristique → région administrative et ville → région
6 +# (patron Lou-Ka : rattacher chaque fiche à sa région via l'adresse)
7 +# -----------------------------------------------------------------------------
8 +from __future__ import annotations
9 +
10 +import re
11 +import unicodedata
12 +
13 +# Les 17 régions administratives officielles (ordre des numéros 01-17).
14 +REGIONS: list[str] = [
15 + "Bas-Saint-Laurent",
16 + "Saguenay–Lac-Saint-Jean",
17 + "Capitale-Nationale",
18 + "Mauricie",
19 + "Estrie",
20 + "Montréal",
21 + "Outaouais",
22 + "Abitibi-Témiscamingue",
23 + "Côte-Nord",
24 + "Nord-du-Québec",
25 + "Gaspésie–Îles-de-la-Madeleine",
26 + "Chaudière-Appalaches",
27 + "Laval",
28 + "Lanaudière",
29 + "Laurentides",
30 + "Montérégie",
31 + "Centre-du-Québec",
32 +]
33 +
34 +
35 +def _k(s: str) -> str:
36 + """Clé de comparaison : minuscules, sans accents, tirets/points/apostrophes unifiés."""
37 + s = unicodedata.normalize("NFD", s or "")
38 + s = "".join(c for c in s if unicodedata.category(c) != "Mn")
39 + s = s.lower().replace("’", "'")
40 + return re.sub(r"[\s\-–—_'.]+", " ", s).strip()
41 +
42 +
43 +# Région touristique (SIT Québec / ATR, 22 régions) → région administrative.
44 +TOURIST_TO_ADMIN: dict[str, str] = {
45 + _k("Abitibi-Témiscamingue"): "Abitibi-Témiscamingue",
46 + _k("Baie-James"): "Nord-du-Québec",
47 + _k("Eeyou Istchee Baie-James"): "Nord-du-Québec",
48 + _k("Nunavik"): "Nord-du-Québec",
49 + _k("Bas-Saint-Laurent"): "Bas-Saint-Laurent",
50 + _k("Cantons-de-l'Est"): "Estrie",
51 + _k("Centre-du-Québec"): "Centre-du-Québec",
52 + _k("Charlevoix"): "Capitale-Nationale",
53 + _k("Chaudière-Appalaches"): "Chaudière-Appalaches",
54 + _k("Côte-Nord"): "Côte-Nord",
55 + _k("Duplessis"): "Côte-Nord",
56 + _k("Manicouagan"): "Côte-Nord",
57 + _k("Gaspésie"): "Gaspésie–Îles-de-la-Madeleine",
58 + _k("Îles-de-la-Madeleine"): "Gaspésie–Îles-de-la-Madeleine",
59 + _k("Lanaudière"): "Lanaudière",
60 + _k("Laurentides"): "Laurentides",
61 + _k("Laval"): "Laval",
62 + _k("Mauricie"): "Mauricie",
63 + _k("Montérégie"): "Montérégie",
64 + _k("Montréal"): "Montréal",
65 + _k("Outaouais"): "Outaouais",
66 + _k("Québec"): "Capitale-Nationale",
67 + _k("Région de Québec"): "Capitale-Nationale",
68 + _k("Saguenay–Lac-Saint-Jean"): "Saguenay–Lac-Saint-Jean",
69 +}
70 +
71 +# Villes → région administrative. Table PRINCIPALE : data/villes_regions.json,
72 +# générée depuis le Répertoire des municipalités du Québec (MAMH, données
73 +# ouvertes, 1 193 municipalités sans ambiguïté). La table manuelle ci-dessous
74 +# sert de repli et couvre quelques graphies fréquentes des sources.
75 +_VILLES_JSON = None
76 +
77 +
78 +def _villes_table() -> dict[str, str]:
79 + global _VILLES_JSON
80 + if _VILLES_JSON is None:
81 + import json
82 + from pathlib import Path
83 + p = Path(__file__).resolve().parent.parent / "data" / "villes_regions.json"
84 + try:
85 + _VILLES_JSON = json.loads(p.read_text(encoding="utf-8"))["villes"]
86 + except Exception:
87 + _VILLES_JSON = {}
88 + return _VILLES_JSON
89 +
90 +
91 +CITY_TO_ADMIN: dict[str, str] = {
92 + _k(city): region
93 + for city, region in {
94 + "Montréal": "Montréal",
95 + "Québec": "Capitale-Nationale",
96 + "Lévis": "Chaudière-Appalaches",
97 + "Laval": "Laval",
98 + "Gatineau": "Outaouais",
99 + "Longueuil": "Montérégie",
100 + "Brossard": "Montérégie",
101 + "Saint-Jean-sur-Richelieu": "Montérégie",
102 + "Granby": "Montérégie",
103 + "Salaberry-de-Valleyfield": "Montérégie",
104 + "Sorel-Tracy": "Montérégie",
105 + "Vaudreuil-Dorion": "Montérégie",
106 + "Sherbrooke": "Estrie",
107 + "Magog": "Estrie",
108 + "Bromont": "Estrie",
109 + "Trois-Rivières": "Mauricie",
110 + "Shawinigan": "Mauricie",
111 + "La Tuque": "Mauricie",
112 + "Saguenay": "Saguenay–Lac-Saint-Jean",
113 + "Alma": "Saguenay–Lac-Saint-Jean",
114 + "Roberval": "Saguenay–Lac-Saint-Jean",
115 + "Dolbeau-Mistassini": "Saguenay–Lac-Saint-Jean",
116 + "Rimouski": "Bas-Saint-Laurent",
117 + "Rivière-du-Loup": "Bas-Saint-Laurent",
118 + "Matane": "Bas-Saint-Laurent",
119 + "Gaspé": "Gaspésie–Îles-de-la-Madeleine",
120 + "Percé": "Gaspésie–Îles-de-la-Madeleine",
121 + "Les Îles-de-la-Madeleine": "Gaspésie–Îles-de-la-Madeleine",
122 + "Baie-Comeau": "Côte-Nord",
123 + "Sept-Îles": "Côte-Nord",
124 + "Rouyn-Noranda": "Abitibi-Témiscamingue",
125 + "Val-d'Or": "Abitibi-Témiscamingue",
126 + "Amos": "Abitibi-Témiscamingue",
127 + "Chibougamau": "Nord-du-Québec",
128 + "Drummondville": "Centre-du-Québec",
129 + "Victoriaville": "Centre-du-Québec",
130 + "Bécancour": "Centre-du-Québec",
131 + "Joliette": "Lanaudière",
132 + "Terrebonne": "Lanaudière",
133 + "Repentigny": "Lanaudière",
134 + "Mascouche": "Lanaudière",
135 + "Saint-Jérôme": "Laurentides",
136 + "Mont-Tremblant": "Laurentides",
137 + "Blainville": "Laurentides",
138 + "Saint-Sauveur": "Laurentides",
139 + "Mirabel": "Laurentides",
140 + "Saint-Georges": "Chaudière-Appalaches",
141 + "Thetford Mines": "Chaudière-Appalaches",
142 + "Montmagny": "Chaudière-Appalaches",
143 + "Baie-Saint-Paul": "Capitale-Nationale",
144 + "La Malbaie": "Capitale-Nationale",
145 + "Wendake": "Capitale-Nationale",
146 + "L'Ancienne-Lorette": "Capitale-Nationale",
147 + }.items()
148 +}
149 +
150 +
151 +def region_from_tourist(label: str) -> str:
152 + """Région administrative à partir d'une région touristique (SITQ/ATR)."""
153 + return TOURIST_TO_ADMIN.get(_k(label), "")
154 +
155 +
156 +def region_from_city(city: str) -> str:
157 + """Région administrative à partir d'une ville (MAMH d'abord, repli manuel)."""
158 + key = _k(city)
159 + for candidate in (
160 + key,
161 + # « St-Damase » → « saint damase », « Ste-Anne » → « sainte anne »
162 + re.sub(r"\bste\b", "sainte", re.sub(r"\bst\b", "saint", key)),
163 + # « Sainte-Adèle (secteur X) » / « Québec, arr. de … »
164 + key.split("(")[0].split(",")[0].strip(),
165 + ):
166 + hit = _villes_table().get(candidate) or CITY_TO_ADMIN.get(candidate, "")
167 + if hit:
168 + return hit
169 + return ""
170 +
171 +
172 +def is_valid_region(region: str) -> bool:
173 + return region in REGIONS
added sortika/schema.py +116 −0
@@ -0,0 +1,116 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# schema.py : modèle de données standardisé (Event) + normalisation centrale
5 +# (patron Lou-Ka : louka/schema.py — Listing → ici Event)
6 +# -----------------------------------------------------------------------------
7 +"""Schéma standard d'un événement (Event) et normalisation des champs.
8 +
9 +Chaque connecteur, peu importe la source, doit produire des objets `Event`
10 +conformes à ce schéma. La méthode `finalize()` applique ensuite la couche de
11 +normalisation commune (sortika/normalize.py + sortika/regions.py) : dates ISO,
12 +catégories canoniques, gratuité/prix, région administrative — les connecteurs
13 +peuvent donc rester simples et remplir les champs bruts.
14 +"""
15 +from __future__ import annotations
16 +
17 +import hashlib
18 +import json
19 +from dataclasses import asdict, dataclass, field
20 +
21 +from .normalize import (
22 + clean_city,
23 + clean_text,
24 + map_categories,
25 + parse_date_iso,
26 + parse_latlng,
27 + parse_price,
28 +)
29 +from .regions import is_valid_region, region_from_city, region_from_tourist
30 +
31 +__all__ = ["Event"]
32 +
33 +
34 +@dataclass
35 +class Event:
36 + """Événement standardisé Sorti-Ka."""
37 +
38 + source: str # id de la source (voir data/sources.json)
39 + external_id: str # identifiant chez la source
40 + url: str # fiche de l'événement chez la source
41 + title: str = "" # ex. "Festival d'été de Québec"
42 + description: str = ""
43 + categories: list[str] = field(default_factory=list) # taxonomie canonique
44 + raw_categories: list[str] = field(default_factory=list) # libellés source
45 + audience: str = "" # ex. "Pour tous", "Adultes, Adolescents"
46 + venue: str = "" # nom du lieu (ex. "Quai Jacques-Cartier")
47 + address: str = "" # adresse civique
48 + city: str = "" # Montréal, Québec, ...
49 + region: str = "" # 1 des 17 régions administratives
50 + tourist_region: str = "" # région touristique source (SITQ/ATR)
51 + postal_code: str = ""
52 + lat: float | None = None
53 + lng: float | None = None
54 + start_date: str | None = None # ISO "2026-09-11"
55 + end_date: str | None = None # ISO "2026-09-13"
56 + is_free: bool | None = None # gratuit (None = inconnu)
57 + price_min: float | None = None # plus bas prix ($ CAD) si payant
58 + price_label: str = "" # texte original du coût (traçabilité)
59 + organizer: str = "" # organisme / structure responsable
60 + website: str = "" # site officiel de l'événement
61 + image: str = "" # URL d'une image (si la source en offre)
62 +
63 + @property
64 + def uid(self) -> str:
65 + return f"{self.source}:{self.external_id}"
66 +
67 + def content_hash(self) -> str:
68 + """Hash du contenu pour la détection de changements (pseudo-webhook)."""
69 + payload = asdict(self)
70 + blob = json.dumps(payload, sort_keys=True, ensure_ascii=False)
71 + return hashlib.sha256(blob.encode("utf-8")).hexdigest()
72 +
73 + def dedup_key(self) -> str:
74 + """Empreinte inter-sources : titre normalisé + ville + date de début.
75 +
76 + Un même événement publié par deux sources (ex. SITQ et la ville) doit
77 + produire la même clé pour être fusionné (patron Lou-Ka / section 12).
78 + """
79 + from .normalize import strip_accents
80 + t = strip_accents(self.title).lower()
81 + t = "".join(c for c in t if c.isalnum())
82 + c = strip_accents(self.city).lower().replace(" ", "")
83 + return hashlib.sha1(f"{t}|{c}|{self.start_date or ''}".encode()).hexdigest()
84 +
85 + def finalize(self) -> "Event":
86 + """Applique la normalisation commune. Appelé par le pipeline d'ingestion.
87 +
88 + Idempotent ; ne remplace jamais une valeur explicite du connecteur.
89 + """
90 + self.title = clean_text(self.title)
91 + self.description = clean_text(self.description)
92 + self.venue = clean_text(self.venue)
93 + self.address = clean_text(self.address)
94 + self.city = clean_city(self.city)
95 + self.audience = clean_text(self.audience)
96 + self.organizer = clean_text(self.organizer)
97 +
98 + self.start_date = parse_date_iso(self.start_date)
99 + self.end_date = parse_date_iso(self.end_date) or self.start_date
100 +
101 + if not self.categories:
102 + self.categories = map_categories(self.raw_categories)
103 +
104 + if self.is_free is None and self.price_min is None and self.price_label:
105 + self.is_free, self.price_min, self.price_label = parse_price(self.price_label)
106 +
107 + self.lat, self.lng = parse_latlng(self.lat, self.lng)
108 +
109 + # Région administrative : source explicite > région touristique > ville.
110 + if not is_valid_region(self.region):
111 + self.region = (region_from_tourist(self.tourist_region)
112 + or region_from_city(self.city) or "")
113 +
114 + if not self.title:
115 + raise ValueError(f"événement sans titre : {self.uid}")
116 + return self
added sortika/seo.py +200 −0
@@ -0,0 +1,200 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# seo.py : référencement — SSR léger des fiches événement, robots, sitemaps
5 +#
6 +# Principe (patron Lou-Ka seo.py) : pour chaque route publique, le serveur
7 +# renvoie le MÊME index.html que le frontend, mais avec un <head> unique
8 +# (title, description, canonical, og:, JSON-LD Event) et le contenu essentiel
9 +# en HTML dans <div id="app">. Les moteurs voient une page complète sans
10 +# exécuter JavaScript ; le frontend, en se montant, prend le relais.
11 +# -----------------------------------------------------------------------------
12 +from __future__ import annotations
13 +
14 +import html as H
15 +import json
16 +from pathlib import Path
17 +
18 +from fastapi import APIRouter, HTTPException
19 +from fastapi.responses import HTMLResponse, PlainTextResponse, Response
20 +
21 +from . import db
22 +
23 +router = APIRouter()
24 +
25 +ROOT = Path(__file__).resolve().parent.parent
26 +FRONTEND_INDEX = ROOT / "frontend" / "index.html"
27 +
28 +BASE_URL = "https://www.sorti-ka.com"
29 +SITE_NAME = "Sorti-Ka"
30 +SITEMAP_CHUNK = 10000
31 +
32 +_SRC_LABELS = {
33 + "sitq": "SIT Québec — Ministère du Tourisme", "montreal": "Ville de Montréal",
34 + "laval": "Ville de Laval", "lepointdevente": "Le point de vente",
35 + "evenko": "evenko", "atuvu": "Atuvu.ca", "lavitrine": "La Vitrine",
36 + "sherbrooke": "Ville de Sherbrooke",
37 +}
38 +
39 +
40 +def _e(s) -> str:
41 + return H.escape(str(s or ""), quote=True)
42 +
43 +
44 +def _render(head: str, ssr_body: str) -> HTMLResponse:
45 + """index.html avec <head> réécrit et contenu SSR injecté dans #ssr."""
46 + import re
47 + tpl = FRONTEND_INDEX.read_text(encoding="utf-8")
48 + # retirer title/description/canonical par défaut (remplacés par le SSR)
49 + tpl = re.sub(r"<title>.*?</title>\s*", "", tpl, count=1, flags=re.S)
50 + tpl = re.sub(r'<meta name="description"[^>]*>\s*', "", tpl, count=1)
51 + tpl = tpl.replace("<!--SSR_HEAD-->", head, 1)
52 + tpl = tpl.replace("<!--SSR_BODY-->", ssr_body, 1)
53 + return HTMLResponse(tpl)
54 +
55 +
56 +def _event_or_404(uid: str) -> dict:
57 + con = db.connect()
58 + row = con.execute("SELECT * FROM events WHERE uid=?", (uid,)).fetchone()
59 + con.close()
60 + if row is None:
61 + raise HTTPException(404, "événement introuvable")
62 + d = dict(row)
63 + d["categories"] = json.loads(d.get("categories") or "[]")
64 + return d
65 +
66 +
67 +def _jsonld(ev: dict) -> dict:
68 + ld: dict = {
69 + "@context": "https://schema.org",
70 + "@type": "Event",
71 + "name": ev["title"],
72 + "eventStatus": "https://schema.org/EventScheduled",
73 + "url": f"{BASE_URL}/evenement/{ev['uid']}",
74 + }
75 + if ev.get("start_date"):
76 + ld["startDate"] = ev["start_date"]
77 + if ev.get("end_date"):
78 + ld["endDate"] = ev["end_date"]
79 + if ev.get("description"):
80 + ld["description"] = ev["description"][:400]
81 + if ev.get("image"):
82 + ld["image"] = [ev["image"]]
83 + loc: dict = {"@type": "Place", "name": ev.get("venue") or ev.get("city") or "Québec"}
84 + addr = {"@type": "PostalAddress", "addressRegion": "QC", "addressCountry": "CA"}
85 + if ev.get("address"):
86 + addr["streetAddress"] = ev["address"]
87 + if ev.get("city"):
88 + addr["addressLocality"] = ev["city"]
89 + if ev.get("postal_code"):
90 + addr["postalCode"] = ev["postal_code"]
91 + loc["address"] = addr
92 + if ev.get("lat") and ev.get("lng"):
93 + loc["geo"] = {"@type": "GeoCoordinates",
94 + "latitude": ev["lat"], "longitude": ev["lng"]}
95 + ld["location"] = loc
96 + if ev.get("is_free"):
97 + ld["isAccessibleForFree"] = True
98 + if ev.get("price_min") is not None:
99 + ld["offers"] = {"@type": "Offer", "price": f"{ev['price_min']:.2f}",
100 + "priceCurrency": "CAD", "url": ev.get("url") or ""}
101 + if ev.get("organizer"):
102 + ld["organizer"] = {"@type": "Organization", "name": ev["organizer"]}
103 + return ld
104 +
105 +
106 +@router.get("/evenement/{uid}", include_in_schema=False)
107 +def event_page(uid: str) -> HTMLResponse:
108 + ev = _event_or_404(uid)
109 + where = " · ".join(p for p in (ev.get("venue"), ev.get("city"),
110 + ev.get("region")) if p)
111 + when = ev.get("start_date") or ""
112 + if ev.get("end_date") and ev["end_date"] != ev.get("start_date"):
113 + when += f" au {ev['end_date']}"
114 + title = f"{ev['title']} — {ev.get('city') or 'Québec'} | {SITE_NAME}"
115 + desc = (ev.get("description") or
116 + f"{ev['title']}, {where}. Dates, lieu et lien vers la billetterie "
117 + f"officielle sur {SITE_NAME}.")[:300]
118 + canonical = f"{BASE_URL}/evenement/{_e(uid)}"
119 + head = (
120 + f"<title>{_e(title)}</title>\n"
121 + f'<meta name="description" content="{_e(desc)}">\n'
122 + f'<link rel="canonical" href="{canonical}">\n'
123 + f'<meta property="og:title" content="{_e(title)}">\n'
124 + f'<meta property="og:description" content="{_e(desc)}">\n'
125 + f'<meta property="og:type" content="event">\n'
126 + f'<meta property="og:url" content="{canonical}">\n'
127 + + (f'<meta property="og:image" content="{_e(ev["image"])}">\n'
128 + if ev.get("image") else "")
129 + + '<script type="application/ld+json">'
130 + + json.dumps(_jsonld(ev), ensure_ascii=False)
131 + + "</script>"
132 + )
133 + src = _SRC_LABELS.get(ev["source"], ev["source"])
134 + ssr = (
135 + f"<article><h1>{_e(ev['title'])}</h1>"
136 + f"<p>{_e(when)} — {_e(where)}</p>"
137 + + (f"<p>{_e(ev['description'][:500])}</p>" if ev.get("description") else "")
138 + + f'<p>Source : {_e(src)} — <a href="{_e(ev["url"])}" rel="noopener">'
139 + f"fiche originale et billets</a></p>"
140 + f'<p><a href="/">Toutes les sorties et tous les événements du Québec</a></p>'
141 + f"</article>"
142 + )
143 + return _render(head, ssr)
144 +
145 +
146 +@router.get("/robots.txt", include_in_schema=False)
147 +def robots() -> PlainTextResponse:
148 + return PlainTextResponse(
149 + "User-agent: *\nAllow: /\n"
150 + "Disallow: /api/\n"
151 + f"Sitemap: {BASE_URL}/sitemap.xml\n")
152 +
153 +
154 +def _urlset(rows: list[tuple[str, str | None]]) -> Response:
155 + items = "".join(
156 + f"<url><loc>{_e(u)}</loc>" + (f"<lastmod>{m}</lastmod>" if m else "")
157 + + "</url>" for u, m in rows)
158 + return Response(
159 + '<?xml version="1.0" encoding="UTF-8"?>'
160 + '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
161 + + items + "</urlset>", media_type="application/xml")
162 +
163 +
164 +@router.get("/sitemap.xml", include_in_schema=False)
165 +def sitemap_index() -> Response:
166 + con = db.connect()
167 + n = con.execute("SELECT COUNT(*) FROM events WHERE active=1").fetchone()[0]
168 + con.close()
169 + chunks = max(1, -(-n // SITEMAP_CHUNK))
170 + items = "".join(
171 + f"<sitemap><loc>{BASE_URL}/sitemap-evenements-{i}.xml</loc></sitemap>"
172 + for i in range(chunks))
173 + return Response(
174 + '<?xml version="1.0" encoding="UTF-8"?>'
175 + '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
176 + f"<sitemap><loc>{BASE_URL}/sitemap-pages.xml</loc></sitemap>"
177 + + items + "</sitemapindex>", media_type="application/xml")
178 +
179 +
180 +@router.get("/sitemap-pages.xml", include_in_schema=False)
181 +def sitemap_pages() -> Response:
182 + return _urlset([(f"{BASE_URL}/", None)])
183 +
184 +
185 +@router.get("/sitemap-evenements-{num}.xml", include_in_schema=False)
186 +def sitemap_events(num: int) -> Response:
187 + from datetime import datetime, timezone
188 + con = db.connect()
189 + rows = con.execute(
190 + "SELECT uid, updated_at FROM events WHERE active=1 "
191 + "ORDER BY uid LIMIT ? OFFSET ?",
192 + (SITEMAP_CHUNK, num * SITEMAP_CHUNK)).fetchall()
193 + con.close()
194 + def iso(ts):
195 + try:
196 + return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d")
197 + except Exception:
198 + return None
199 + return _urlset([(f"{BASE_URL}/evenement/{r['uid']}", iso(r["updated_at"]))
200 + for r in rows])
added sortika/web.py +207 −0
@@ -0,0 +1,207 @@
1 +# -----------------------------------------------------------------------------
2 +# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)
3 +# Auteur : Simon-Pierre Boucher — contact@spboucher.ai
4 +# web.py : API FastAPI (JSON) + service du frontend statique (frontend/)
5 +# (patron Lou-Ka : louka/web.py)
6 +# -----------------------------------------------------------------------------
7 +from __future__ import annotations
8 +
9 +import json
10 +from datetime import date
11 +from pathlib import Path
12 +
13 +from fastapi import FastAPI, HTTPException, Query
14 +from fastapi.middleware.cors import CORSMiddleware
15 +from fastapi.middleware.gzip import GZipMiddleware
16 +from fastapi.responses import FileResponse
17 +from fastapi.staticfiles import StaticFiles
18 +
19 +from . import db, seo
20 +from .normalize import CATEGORIES
21 +from .regions import REGIONS
22 +
23 +ROOT = Path(__file__).resolve().parent.parent
24 +SOURCES_PATH = ROOT / "data" / "sources.json"
25 +FRONTEND_DIR = ROOT / "frontend"
26 +
27 +app = FastAPI(title="Sorti-Ka API", version="1.0",
28 + description="Agrégateur de sorties & événements — province de Québec")
29 +app.add_middleware(CORSMiddleware, allow_origins=["*"],
30 + allow_methods=["*"], allow_headers=["*"])
31 +app.add_middleware(GZipMiddleware, minimum_size=1000)
32 +
33 +
34 +def _row_to_dict(row) -> dict:
35 + d = dict(row)
36 + for k in ("categories", "raw_categories"):
37 + d[k] = json.loads(d.get(k) or "[]")
38 + d["is_free"] = None if d["is_free"] is None else bool(d["is_free"])
39 + d.pop("content_hash", None)
40 + return d
41 +
42 +
43 +@app.get("/healthz")
44 +@app.get("/api/health")
45 +def health() -> dict:
46 + con = db.connect()
47 + n = con.execute("SELECT COUNT(*) FROM events WHERE active=1").fetchone()[0]
48 + con.close()
49 + return {"status": "ok", "active_events": n}
50 +
51 +
52 +@app.get("/api/events")
53 +def list_events(
54 + q: str = Query("", description="recherche plein texte (titre, lieu, description)"),
55 + region: str = Query("", description="région administrative"),
56 + city: str = Query("", description="ville"),
57 + category: str = Query("", description="catégorie canonique"),
58 + free: bool | None = Query(None, description="gratuit seulement"),
59 + date_from: str = Query("", alias="from", description="ISO — événements se terminant à partir de cette date"),
60 + date_to: str = Query("", alias="to", description="ISO — événements commençant au plus tard à cette date"),
61 + upcoming: bool = Query(True, description="masquer les événements passés"),
62 + sort: str = Query("date", description="date | recent"),
63 + limit: int = Query(30, ge=1, le=200),
64 + offset: int = Query(0, ge=0),
65 +) -> dict:
66 + where, params = ["active=1"], []
67 + if q:
68 + where.append("(title LIKE ? OR venue LIKE ? OR description LIKE ? OR city LIKE ?)")
69 + like = f"%{q}%"
70 + params += [like, like, like, like]
71 + if region:
72 + where.append("region=?")
73 + params.append(region)
74 + if city:
75 + where.append("city=?")
76 + params.append(city)
77 + if category:
78 + where.append("categories LIKE ?")
79 + params.append(f'%"{category}"%')
80 + if free is True:
81 + where.append("is_free=1")
82 + if upcoming and not date_from:
83 + date_from = date.today().isoformat()
84 + if date_from:
85 + where.append("(end_date >= ? OR (end_date IS NULL AND start_date >= ?))")
86 + params += [date_from, date_from]
87 + if date_to:
88 + where.append("start_date <= ?")
89 + params.append(date_to)
90 +
91 + if sort == "date" and date_from:
92 + # les événements déjà commencés mais toujours en cours (expositions,
93 + # festivals longs) se classent à leur date effective, pas à leur début
94 + order = "start_date IS NULL, MAX(start_date, ?) ASC, title ASC"
95 + order_params = [date_from]
96 + elif sort == "date":
97 + order = "start_date IS NULL, start_date ASC, title ASC"
98 + order_params = []
99 + else:
100 + order, order_params = "updated_at DESC", []
101 + sql_where = " AND ".join(where)
102 +
103 + # déduplication inter-sources (CLAUDE.md §12) : un même événement publié
104 + # par plusieurs sources = une seule carte, la fiche la plus riche gagne
105 + # (prix connu > image > description), les autres restent accessibles par uid.
106 + ranked = (
107 + "SELECT *, ROW_NUMBER() OVER (PARTITION BY dedup_key ORDER BY "
108 + "(CASE WHEN price_min IS NOT NULL THEN 4 ELSE 0 END) + "
109 + "(CASE WHEN image != '' THEN 2 ELSE 0 END) + "
110 + "(CASE WHEN description != '' THEN 1 ELSE 0 END) DESC, uid) AS rn "
111 + f"FROM events WHERE {sql_where}")
112 + con = db.connect()
113 + total = con.execute(
114 + f"SELECT COUNT(*) FROM ({ranked}) WHERE rn = 1", params).fetchone()[0]
115 + rows = con.execute(
116 + f"SELECT * FROM ({ranked}) WHERE rn = 1 ORDER BY {order} "
117 + f"LIMIT ? OFFSET ?", params + order_params + [limit, offset]).fetchall()
118 + con.close()
119 + events = []
120 + for r in rows:
121 + d = _row_to_dict(dict(r) | {})
122 + d.pop("rn", None)
123 + events.append(d)
124 + return {"total": total, "limit": limit, "offset": offset, "events": events}
125 +
126 +
127 +@app.get("/api/events/{uid}")
128 +def get_event(uid: str) -> dict:
129 + con = db.connect()
130 + row = con.execute("SELECT * FROM events WHERE uid=?", (uid,)).fetchone()
131 + con.close()
132 + if row is None:
133 + raise HTTPException(404, "événement introuvable")
134 + return _row_to_dict(row)
135 +
136 +
137 +@app.get("/api/stats")
138 +def stats() -> dict:
139 + today = date.today().isoformat()
140 + con = db.connect()
141 + total = con.execute("SELECT COUNT(*) FROM events WHERE active=1").fetchone()[0]
142 + upcoming = con.execute(
143 + "SELECT COUNT(*) FROM events WHERE active=1 AND "
144 + "(end_date >= ? OR (end_date IS NULL AND start_date >= ?))",
145 + (today, today)).fetchone()[0]
146 + free = con.execute(
147 + "SELECT COUNT(*) FROM events WHERE active=1 AND is_free=1 AND "
148 + "(end_date >= ? OR (end_date IS NULL AND start_date >= ?))",
149 + (today, today)).fetchone()[0]
150 + by_region = {r["region"] or "Non rattachée": r["n"] for r in con.execute(
151 + "SELECT region, COUNT(*) AS n FROM events WHERE active=1 AND "
152 + "(end_date >= ? OR (end_date IS NULL AND start_date >= ?)) "
153 + "GROUP BY region ORDER BY n DESC", (today, today))}
154 + by_category: dict[str, int] = {}
155 + for r in con.execute(
156 + "SELECT categories FROM events WHERE active=1 AND "
157 + "(end_date >= ? OR (end_date IS NULL AND start_date >= ?))",
158 + (today, today)):
159 + for c in json.loads(r["categories"] or "[]"):
160 + by_category[c] = by_category.get(c, 0) + 1
161 + cities = con.execute(
162 + "SELECT COUNT(DISTINCT city) FROM events WHERE active=1 AND city != ''"
163 + ).fetchone()[0]
164 + con.close()
165 + return {"total_active": total, "upcoming": upcoming, "free_upcoming": free,
166 + "cities": cities, "by_region": by_region,
167 + "by_category": dict(sorted(by_category.items(),
168 + key=lambda kv: -kv[1]))}
169 +
170 +
171 +@app.get("/api/regions")
172 +def regions() -> list[str]:
173 + return REGIONS
174 +
175 +
176 +@app.get("/api/categories")
177 +def categories() -> list[str]:
178 + return CATEGORIES
179 +
180 +
181 +@app.get("/api/sources")
182 +def sources() -> dict:
183 + reg = json.loads(SOURCES_PATH.read_text(encoding="utf-8"))
184 + con = db.connect()
185 + counts = {r["source"]: r["n"] for r in con.execute(
186 + "SELECT source, COUNT(*) AS n FROM events WHERE active=1 GROUP BY source")}
187 + last = {r["source"]: r["ts"] for r in con.execute(
188 + "SELECT source, MAX(ts) AS ts FROM sync_log WHERE error IS NULL "
189 + "GROUP BY source")}
190 + con.close()
191 + for s in reg.get("sources", []):
192 + s["active_events"] = counts.get(s["id"], 0)
193 + s["last_sync"] = last.get(s["id"])
194 + return reg
195 +
196 +
197 +# --- SSR SEO : fiches /evenement/{uid}, robots.txt, sitemaps (sortika/seo.py) ---
198 +app.include_router(seo.router)
199 +
200 +
201 +# --- frontend statique (à la fin : les routes /api et SEO ont priorité) ---
202 +if FRONTEND_DIR.exists():
203 + @app.get("/")
204 + def index() -> FileResponse:
205 + return FileResponse(FRONTEND_DIR / "index.html")
206 +
207 + app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend")
added tests/fixtures/atuvu_fiche.html +1426 −0
@@ -0,0 +1,1426 @@
1 +<!DOCTYPE html>
2 +<html lang="fr" dir="ltr">
3 +
4 +<head>
5 + <title>Arkansas: tarifs réduits et billets gratuits | atuvu.ca</title>
6 +<meta name="title" content="Arkansas: tarifs réduits et billets gratuits | atuvu.ca" />
7 +<meta name="description" content="Arkansas: trouvez des billets à tarif réduit et des billets gratuits. Jusqu'à 80% de rabais. Devenez membre d'atuvu.ca et profitez des offres exclusives." />
8 +<meta property="keywords" content="atuvu.ca, Musique, Country, , " />
9 +
10 +
11 +<meta property="og:title" content="Arkansas: tarifs réduits et billets gratuits | atuvu.ca" />
12 +<meta property="og:description" content="Arkansas: trouvez des billets à tarif réduit et des billets gratuits. Jusqu'à 80% de rabais. Devenez membre d'atuvu.ca et profitez des offres exclusives." />
13 +<meta property="og:image" content="https://atuvu.ca/uploads/images/2026/08/arkansas.jpg" />
14 +<meta property="og:url" content="https://atuvu.ca/billets-spectacle/arkansas/" />
15 + <meta property="og:type" content="Article" />
16 +
17 +<meta charset="utf-8">
18 +<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1">
19 +
20 +
21 +<link rel="home" href="https://atuvu.ca">
22 +<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
23 +<link rel="icon" type="image/png"
24 + href="https://atuvu.ca/img/atuvu/favicon.png">
25 +
26 +
27 +<link rel="manifest" href="https://atuvu.ca/manifest.json">
28 +<meta name="mobile-web-app-capable" content="yes">
29 +<meta name="theme-color" content="#ffffff">
30 +
31 +
32 +<meta name="apple-mobile-web-app-capable" content="yes">
33 +<meta name="apple-mobile-web-app-status-bar-style" content="#ffffff">
34 +<meta name="apple-mobile-web-app-title" content="atuvu.ca">
35 +<link rel="apple-touch-icon" sizes="180x180" href="https://atuvu.ca/img/atuvu/icon180.png">
36 +
37 +
38 +<meta name="msapplication-TileImage" content="https://atuvu.ca/img/atuvu/icon512.png">
39 +<meta name="msapplication-TileColor" content="#ffffff">
40 +
41 +
42 +<meta property="fb:app_id" content="178489298841730" />
43 +<meta property="og:locale" content="fr_CA" />
44 +
45 +
46 +
47 +<link rel="alternate" type="application/rss+xml" title="Magazine Culturel atuvu.ca" href="https://atuvu.ca/rss.xml" />
48 +
49 +
50 +<link rel="preconnect" href="https://www.googletagmanager.com">
51 +<link rel="preconnect" href="https://connect.facebook.net">
52 +<link rel="preconnect" href="https://www.google-analytics.com">
53 +
54 +
55 +<link rel="preconnect" href="https://fonts.googleapis.com">
56 +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
57 +<link href="https://fonts.googleapis.com/css2?family=Muli:wght@400;600;700;800&display=swap" rel="stylesheet">
58 +
59 +
60 +
61 +
62 +
63 +
64 +
65 +<link rel="preload" as="style" href="https://atuvu.ca/build/assets/fr-DJAvaHnz.css" /><link rel="preload" as="style" href="https://atuvu.ca/build/assets/main-BppCHRAA.css" /><link rel="modulepreload" href="https://atuvu.ca/build/assets/main-DhUaDXjD.js" /><link rel="modulepreload" href="https://atuvu.ca/build/assets/fr-D79UFmCT.js" /><link rel="stylesheet" href="https://atuvu.ca/build/assets/fr-DJAvaHnz.css" /><link rel="stylesheet" href="https://atuvu.ca/build/assets/main-BppCHRAA.css" /><script type="module" src="https://atuvu.ca/build/assets/main-DhUaDXjD.js"></script>
66 +
67 +
68 +
69 +<script>
70 + (function(w, d, s, l, i) {
71 + w[l] = w[l] || [];
72 + w[l].push({
73 + 'gtm.start': new Date().getTime(),
74 + event: 'gtm.js'
75 + });
76 + var f = d.getElementsByTagName(s)[0],
77 + j = d.createElement(s),
78 + dl = l != 'dataLayer' ? '&l=' + l : '';
79 + j.async = true;
80 + j.src =
81 + 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
82 + f.parentNode.insertBefore(j, f);
83 + })(window, document, 'script', 'dataLayer', 'GTM-5892GNR');
84 +</script>
85 +
86 +<script src="https://storage.googleapis.com/fuel_modules/modules/FD_HB/266033/Fuel_id_266033.js"  async></script>
87 +</head>
88 +
89 +<body class="">
90 + <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5892GNR" height="0" width="0"
91 + style="display:none;visibility:hidden"></iframe></noscript>
92 +
93 +
94 +
95 +
96 + <div class="pop-up">
97 + <div class="popuptotal">
98 + <div class="pop-up--content">
99 + <!-- Bouton de fermeture -->
100 + <button class="pop-up__close button-rounded js-close-button" data-close="menu">
101 + <img src="https://atuvu.ca/img/icons/cross-nav-bar.svg" alt="Fermer">
102 + </button>
103 +
104 + <!-- Bloc gris pour le logo -->
105 + <div class="logo-box">
106 + <img class="imglogopopup" src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
107 + </div>
108 +
109 + <!-- Texte (titre + sous-titre) -->
110 + <div class="text">
111 + <h2>Inscrivez-vous</h2>
112 + <p>Des offres exclusives et événements gratuits</p>
113 + </div>
114 +
115 + <!-- Boutons (nouveau design) -->
116 + <div class="popup-btn">
117 + <a href="https://atuvu.ca/inscription" class="btn-outline">Inscription</a>
118 + <a href="https://atuvu.ca/devenir-membre" class="btn-filled">En savoir plus</a>
119 + </div>
120 + </div>
121 + </div>
122 +</div>
123 +
124 +<div id="adblock-popup" class="atuvu-popup atuvux-adblock-modal" style="display: none;">
125 + <div class="atuvu-popup__overlay"></div>
126 + <div class="atuvu-popup__content">
127 + <button class="pop-up__close button-rounded js-adblock-close" aria-label="Fermer">
128 + <img src="https://atuvu.ca/img/icons/cross-nav-bar.svg" alt="Fermer">
129 + </button>
130 +
131 + <!-- STEP 1 -->
132 + <div id="adblock-step-1">
133 + <div class="atuvu-popup__header">
134 + <img class="atuvu-popup__logo" src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
135 + </div>
136 +
137 + <h2 class="atuvu-popup__title">Bloqueur de publicité détecté !</h2>
138 +
139 + <div class="atuvu-popup__body">
140 + <p>Les publicités soutiennent notre média culturel indépendant. Elles nous aident à :</p>
141 + <ul>
142 + <li>faire rayonner la culture d’ici</li>
143 + <li>financer du contenu original de qualité</li>
144 + <li>proposer des offres exclusives à nos membres</li>
145 + </ul>
146 + <p>En désactivant votre bloqueur pour <a href="/">atuvu.ca</a>, vous contribuez à la vitalité de notre média et de notre communauté.</p>
147 + <p>Merci pour votre soutien !</p>
148 + </div>
149 +
150 + <div class="atuvu-popup__actions">
151 + <button class="btn-filled js-adblock-next" id="btn-adblock-next">Désactiver mon bloqueur pour soutenir atuvu.ca</button>
152 + </div>
153 + </div>
154 +
155 + <!-- STEP 2 -->
156 + <div id="adblock-step-2" style="display: none;">
157 + <div class="atuvu-popup__header">
158 + <img class="atuvu-popup__logo" src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
159 + </div>
160 +
161 + <h2 class="atuvu-popup__title">Comment désactiver votre bloqueur de publicité pour atuvu.ca ?</h2>
162 +
163 + <div class="atuvu-popup__body instruction-body">
164 + <p><strong>1. Repérez l'icône de votre bloqueur</strong> de publicité en haut à droite de votre navigateur.</p>
165 + <div class="adblock-icons">
166 + <div class="adblock-icon"><img src="https://atuvu.ca/img/adblock/adblock-plus.png" alt="Adblock Plus"></div>
167 + <div class="adblock-icon"><img src="https://atuvu.ca/img/adblock/adblock.png" alt="Adblock"></div>
168 + <div class="adblock-icon"><img src="https://atuvu.ca/img/adblock/ublock-origin.png" alt="uBlock Origin"></div>
169 + <div class="adblock-icon"><img src="https://atuvu.ca/img/adblock/ublock.png" alt="uBlock"></div>
170 + <div class="adblock-icon"><img src="https://atuvu.ca/img/adblock/ghostery.png" alt="Ghostery"></div>
171 + </div>
172 + <p><strong>2. Cliquez sur l'icône, puis désactivez le blocage</strong> pour le site atuvu.ca.</p>
173 + <p><strong>3. Actualisez la page.</strong></p>
174 +
175 + <h3 class="instruction-subtitle">Vous ne voyez pas votre bloqueur ?</h3>
176 + <p><strong>Ouvrez le menu de votre navigateur</strong> (souvent trois points, en haut à droite), puis allez dans Extensions ou Modules complémentaires pour gérer vos bloqueurs de publicité.</p>
177 +
178 + <p class="atuvu-popup__footer-text">Merci de contribuer à faire rayonner la culture d’ici.</p>
179 + </div>
180 +
181 + <div class="atuvu-popup__actions instruction-actions">
182 + <button class="btn-filled js-adblock-reload">Actualiser la page</button>
183 + </div>
184 + </div>
185 + </div>
186 +</div>
187 +
188 +<header class="totalheader">
189 + <div class="nav-overlay js-close-button" data-close="menu"></div>
190 + <div class="top-red-bar">Passionnés de spectacles et de culture</div>
191 + <div class="nav-sidebar">
192 + <div class="nav-sidebar__content">
193 + <!-- Bouton burger -->
194 + <button class="nav-sidebar__toggle">
195 + <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
196 + <path d="M3 6H21M3 12H21M3 18H21" stroke="white" stroke-width="2" stroke-linecap="round" />
197 + </svg>
198 + </button>
199 +
200 + <!-- Icônes de la barre rouge -->
201 + <ul class="nav-sidebar__icons">
202 + <li class="icon-home">
203 + <a href="https://atuvu.ca" class="tooltip" data-tooltip="Accueil">
204 + <img src="https://atuvu.ca/img/icons/home.svg" alt="Accueil">
205 + </a>
206 + </li>
207 + <li class="icon-articles">
208 + <a href="https://atuvu.ca/magazine-culturel" class="tooltip" data-tooltip="Magazine">
209 + <img src="https://atuvu.ca/img/icons/magazine.svg" alt="magazine">
210 + </a>
211 + </li>
212 + <li class="icon-Offres">
213 + <a href="https://atuvu.ca/programmation-culturelle" class="tooltip" data-tooltip="Offres">
214 + <img src="https://atuvu.ca/img/icons/offres.svg" alt="Offres">
215 + </a>
216 + </li>
217 + <li class="icon-calendar">
218 + <a data-close="calendar" href="#" class="tooltip" data-tooltip="Calendrier">
219 + <img src="https://atuvu.ca/img/icons/calendar-navbar.svg" alt="Calendrier">
220 + </a>
221 + </li>
222 + <li class="user-connection">
223 + <a href="https://atuvu.ca/connexion" class="user-menu">
224 + <img src="https://atuvu.ca/img/icons/connection-button-mobile.svg" alt="">
225 + </a>
226 + </li>
227 + </ul>
228 + </div>
229 +</div>
230 +
231 +<!-- Nouvelle Navbar -->
232 +<div class="nav-sidebar__menu">
233 + <!-- Bouton de fermeture -->
234 + <button class="nav-sidebar__menu__close button-rounded js-close-button" data-close="menu">
235 + <img src="https://atuvu.ca/img/icons/cross-nav-bar.svg" alt="Flèche">
236 + </button>
237 +
238 + <!-- Logo -->
239 + <a href="https://atuvu.ca" class="nav-sidebar__logo">
240 + <img src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
241 + </a>
242 +
243 + <!-- Liens du menu -->
244 + <ul class="nav-sidebar__links">
245 +
246 +
247 + <li>
248 + <a href="https://atuvu.ca">
249 + Accueil
250 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
251 + </a>
252 + </li>
253 +
254 + <li class="nav-sidebar__parent">
255 + <a href="https://atuvu.ca/magazine-culturel">
256 + Magazine
257 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
258 + </a>
259 + <ul class="nav-sidebar__sub-links">
260 + <!-- Lien vers toutes les rubriques -->
261 + <li>
262 + <a href="https://atuvu.ca/magazine-culturel">Toutes les rubriques</a>
263 + </li>
264 +
265 + <!-- Rubriques dynamiques : $rubricService->withRecentArticles() -->
266 + <li>
267 + <a href="https://atuvu.ca/magazine-culturel/critiques">
268 + Critiques
269 + </a>
270 + </li>
271 + <li>
272 + <a href="https://atuvu.ca/magazine-culturel/nouvelles-culturelles">
273 + Nouvelles
274 + </a>
275 + </li>
276 + <li>
277 + <a href="https://atuvu.ca/magazine-culturel/entrevue">
278 + Entrevue
279 + </a>
280 + </li>
281 + <li>
282 + <a href="https://atuvu.ca/magazine-culturel/suggestion-sortie">
283 + Suggestions de sorties
284 + </a>
285 + </li>
286 + <li>
287 + <a href="https://atuvu.ca/magazine-culturel/suggestions-lecture">
288 + Suggestions de lecture
289 + </a>
290 + </li>
291 + <li>
292 + <a href="https://atuvu.ca/magazine-culturel/zoom-photo">
293 + Zoom photo
294 + </a>
295 + </li>
296 + <li>
297 + <a href="https://atuvu.ca/magazine-culturel/concours-prix-a-gagner">
298 + Concours
299 + </a>
300 + </li>
301 + <li>
302 + <a href="https://atuvu.ca/magazine-culturel/actualite-musique-concert">
303 + Musique
304 + </a>
305 + </li>
306 + <li>
307 + <a href="https://atuvu.ca/magazine-culturel/actu-musicale">
308 + Actu Musicale
309 + </a>
310 + </li>
311 + <li>
312 + <a href="https://atuvu.ca/magazine-culturel/actualite-cinema">
313 + Cinéma
314 + </a>
315 + </li>
316 + </ul>
317 + </li>
318 +
319 +
320 +
321 + <li class="nav-sidebar__parent">
322 + <a href="https://atuvu.ca/programmation-culturelle">
323 + Nos offres
324 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
325 + </a>
326 + <ul class="nav-sidebar__sub-links">
327 +
328 +
329 + <li><a href="https://atuvu.ca/liste-des-evenements-en-vedette">En vedette</a></li>
330 + <li><a href="https://atuvu.ca/activite-gratuite">Gratuit</a></li>
331 + <li><a href="https://atuvu.ca/billet-de-spectacle-vip">Offres VIP</a></li>
332 + <li><a href="https://atuvu.ca/offres-privees">Offres privées: connectez-vous!</a></li>
333 + <li><a href="https://atuvu.ca/nouvelles-offres-promo">Nouvelles offres</a></li>
334 +
335 +
336 +
337 +
338 + <li>
339 + <a href="https://atuvu.ca/billets/actualite-cinema">Cinéma</a>
340 + </li>
341 + <li>
342 + <a href="https://atuvu.ca/billets/spectacle-cirque">Cirque</a>
343 + </li>
344 + <li>
345 + <a href="https://atuvu.ca/billets/spectacle-de-conte">Conte</a>
346 + </li>
347 + <li>
348 + <a href="https://atuvu.ca/billets/spectacle-de-danse">Danse</a>
349 + </li>
350 + <li>
351 + <a href="https://atuvu.ca/billets/activites-a-faire">Divers</a>
352 + </li>
353 + <li>
354 + <a href="https://atuvu.ca/billets/exposition-musee">Exposition</a>
355 + </li>
356 + <li>
357 + <a href="https://atuvu.ca/billets/spectacle-humour">Humour</a>
358 + </li>
359 + <li>
360 + <a href="https://atuvu.ca/billets/spectacle-improvisation">Improvisation</a>
361 + </li>
362 + <li>
363 + <a href="https://atuvu.ca/billets/jeux">Jeux</a>
364 + </li>
365 + <li>
366 + <a href="https://atuvu.ca/billets/concert-de-musique">Musique</a>
367 + </li>
368 + <li>
369 + <a href="https://atuvu.ca/billets/piece-de-theatre">Théâtre</a>
370 + </li>
371 +
372 +
373 + </ul>
374 + </li>
375 +
376 +
377 + <li class="nav-sidebar__parent">
378 + <li class="nav-sidebar__parent">
379 + <a href="#" onclick="event.preventDefault(); document.getElementById('calendarModal').classList.add('open');">
380 + Calendrier
381 + <span>
382 + <img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche">
383 + </span>
384 + </a>
385 +
386 + <!-- Sous-menu filtrant : Aujourd'hui, Demain, etc. -->
387 + <ul class="nav-sidebar__sub-links sub-calendar">
388 +
389 + <li>
390 + <a href="#" onclick="event.preventDefault(); document.getElementById('todayForm').submit();">
391 + Aujourd'hui
392 + </a>
393 + <form id="todayForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display:none;">
394 + <input type="hidden" name="start_date" value="1786939200">
395 + <input type="hidden" name="end_date" value="1787025600">
396 + </form>
397 + </li>
398 +
399 +
400 + <li>
401 + <a href="#" onclick="event.preventDefault(); document.getElementById('tomorrowForm').submit();">
402 + Demain
403 + </a>
404 + <form id="tomorrowForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display:none;">
405 + <input type="hidden" name="start_date" value="1787025600">
406 + <input type="hidden" name="end_date" value="1787112000">
407 + </form>
408 + </li>
409 +
410 +
411 + <li>
412 + <a href="#" onclick="event.preventDefault(); document.getElementById('weekForm').submit();">
413 + Cette semaine
414 + </a>
415 + <form id="weekForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display:none;">
416 + <input type="hidden" name="start_date" value="1786939200">
417 + <input type="hidden" name="end_date" value="1787544000">
418 + </form>
419 + </li>
420 +
421 +
422 + <li>
423 + <a href="#" onclick="event.preventDefault(); document.getElementById('weekendForm').submit();">
424 + Ce weekend
425 + </a>
426 + <form id="weekendForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display:none;">
427 + <input type="hidden" name="start_date" value="1787371200">
428 + <input type="hidden" name="end_date" value="1787544000">
429 + </form>
430 + </li>
431 +
432 +
433 + <li>
434 + <a href="#" onclick="event.preventDefault(); document.getElementById('monthForm').submit();">
435 + Ce mois-ci
436 + </a>
437 + <form id="monthForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display:none;">
438 + <input type="hidden" name="start_date" value="1786939200">
439 + <input type="hidden" name="end_date" value="1788235200">
440 + </form>
441 + </li>
442 + </ul>
443 +</li>
444 +<!-- MODAL Calendrier (en dehors du <li>) -->
445 +<div class="calendar-modal" id="calendarModal">
446 + <!-- Bouton de fermeture du calendrier -->
447 + <button class="calendar-modal__close js-close-button button-rounded" data-close="calendar">
448 + <img src="https://atuvu.ca/img/icons/cross-nav-bar.svg" alt="Fermer le calendrier">
449 + </button>
450 +
451 + <a href="https://atuvu.ca" class="nav-sidebar__logo">
452 + <img src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
453 + </a>
454 +
455 +
456 +
457 + <!-- Conteneur de navigation du mois -->
458 + <div class="calendar-modal__month-nav">
459 + <!-- Bouton flèche gauche -->
460 + <button class="calendar-modal__nav-btn flatpickr-prev-month">
461 + <img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche gauche">
462 + </button>
463 +
464 + <!-- Titre du mois -->
465 + <span id="flatpickrMonthTitle" class="calendar-modal__month-name"></span>
466 +
467 + <!-- Bouton flèche droite -->
468 + <button class="calendar-modal__nav-btn flatpickr-next-month">
469 + <img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche droite">
470 + </button>
471 + </div>
472 +
473 +
474 + <!-- Conteneur Flatpickr -->
475 + <div id="flatpickrContainer"></div>
476 +
477 + <!-- Form caché pour soumettre l’intervalle -->
478 + <form id="flatpickrRangeForm" action="https://atuvu.ca/calendrier-des-evenements" method="get" style="display: none;">
479 + <input type="hidden" name="start_date" id="rangeStart">
480 + <input type="hidden" name="end_date" id="rangeEnd">
481 + </form>
482 +
483 + <!-- Boutons de raccourci -->
484 + <div class="calendar-modal__footer-btns">
485 + <button class="calendar-modal__footer-btn" data-range="week"
486 + onclick="event.preventDefault(); document.getElementById('tomorrowForm').submit();">
487 + Cette semaine
488 + </button>
489 + <button class="calendar-modal__footer-btn" data-range="weekend"
490 + onclick="event.preventDefault(); document.getElementById('weekendForm').submit();">
491 + Ce weekend
492 + </button>
493 + <button class="calendar-modal__footer-btn" data-range="month"
494 + onclick="event.preventDefault(); document.getElementById('monthForm').submit();">
495 + Ce mois-ci
496 + </button>
497 + </div>
498 +</div>
499 + </li>
500 +
501 +
502 + <li class="nav-sidebar__parent">
503 + <a href="https://atuvu.ca/boutique">
504 + Boutique
505 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
506 + </a>
507 +
508 + <ul class="nav-sidebar__sub-links">
509 +
510 + <li><a href="https://atuvu.ca/produits-vedette">En vedette</a></li>
511 + <li><a href="https://atuvu.ca/produits-nouveaute">Nouveautés</a></li>
512 + <li><a href="https://atuvu.ca/produits-promo">Promotions</a></li>
513 + <li><a href="https://atuvu.ca/boutique">Tous les produits</a></li>
514 +
515 +
516 +
517 +
518 +
519 + <li>
520 + <a href="https://atuvu.ca/boutique/actualite-cinema">
521 + Cinéma
522 + </a>
523 + </li>
524 +
525 + <li>
526 + <a href="https://atuvu.ca/boutique/jeux">
527 + Jeux
528 + </a>
529 + </li>
530 +
531 +
532 + <li>
533 + <a href="https://atuvu.ca/boutique/piece-de-theatre">
534 + Théâtre
535 + </a>
536 + </li>
537 + </ul>
538 + </li>
539 +
540 +
541 +
542 + <li class="nav-sidebar__parent">
543 + <a href="https://atuvu.ca/villes">
544 + Villes
545 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
546 + </a>
547 + <ul class="nav-sidebar__sub-links">
548 + <li>
549 + <a href="https://atuvu.ca/ville/montreal">
550 + Montréal
551 + </a>
552 + </li>
553 + <li>
554 + <a href="https://atuvu.ca/ville/saint-jean-sur-richelieu">
555 + Saint-Jean-sur-Richelieu
556 + </a>
557 + </li>
558 + <li>
559 + <a href="https://atuvu.ca/ville/sherbrooke">
560 + Sherbrooke
561 + </a>
562 + </li>
563 + <li>
564 + <a href="https://atuvu.ca/ville/repentigny">
565 + Repentigny
566 + </a>
567 + </li>
568 + <li>
569 + <a href="https://atuvu.ca/ville/laval">
570 + Laval
571 + </a>
572 + </li>
573 + <li><a href="https://atuvu.ca/villes">Toutes les villes</a></li>
574 + </ul>
575 + </li>
576 +
577 +
578 + <li class="nav-sidebar__parent">
579 + <a href="https://atuvu.ca/festivals">
580 + Festivals
581 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
582 + </a>
583 + <ul class="nav-sidebar__sub-links">
584 + <li>
585 + <a href="https://atuvu.ca/festival/les-dimanches-du-conte">
586 + Les Dimanches du conte
587 + </a>
588 + </li>
589 + <li>
590 + <a href="https://atuvu.ca/festival/festival-martinique-gourmande">
591 + Festival Martinique Gourmande
592 + </a>
593 + </li>
594 + <li>
595 + <a href="https://atuvu.ca/festival/festival-de-montgolfi%C3%A8res-de-gatineau">
596 + Festival de montgolfières de Gatineau (FMG)
597 + </a>
598 + </li>
599 + <li>
600 + <a href="https://atuvu.ca/festival/mutek">
601 + MUTEK
602 + </a>
603 + </li>
604 + <li>
605 + <a href="https://atuvu.ca/festival/cinema-sous-les-etoiles">
606 + Cinéma sous les étoiles
607 + </a>
608 + </li>
609 + <li><a href="https://atuvu.ca/festivals">Tous les festivals</a></li>
610 + </ul>
611 + </li>
612 +
613 +
614 + <li>
615 + <a href="https://atuvu.ca/communaute-atuvu">
616 + Communauté
617 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
618 + </a>
619 + </li>
620 +
621 +
622 + <li>
623 + <a href="https://atuvu.ca/inscription">
624 + Inscription
625 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
626 + </a>
627 + </li>
628 +
629 +
630 +
631 +
632 +
633 +
634 +
635 +
636 + <li class="nav-sidebar__parent">
637 + <!-- Pour les non-connectés, on peut afficher deux items "Connexion" et "Devenir Membre" comme les autres liens. -->
638 + <li class="nav-sidebar__parent">
639 + <a href="https://atuvu.ca/connexion">
640 + Connexion
641 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
642 + </a>
643 + </li>
644 + </li>
645 +
646 +
647 + <li>
648 + <a href="https://atuvu.ca/choisir-un-abonnement">
649 + Abonnement VIP
650 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
651 + </a>
652 + </li>
653 +
654 +
655 +
656 +
657 +
658 +
659 +
660 + <li class="nav-sidebar__parent">
661 + <!-- Si le producteur n'est pas connecté, affiche le lien par défaut -->
662 + <li class="nav-sidebar__parent">
663 + <a href="https://atuvu.ca/producteur/ajouter-un-evenement">
664 + Promouvoir
665 + <span class="nav-arrow"><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
666 + </a>
667 + <ul class="nav-sidebar__sub-links">
668 + <li><a href="https://atuvu.ca/producteur/ajouter-un-evenement">Inscrire un événement</a></li>
669 + <li><a href="https://atuvu.ca/media-kit">Annoncer avec nous</a></li>
670 + </ul>
671 + </li>
672 + </li>
673 +
674 +
675 + <li>
676 + <a href="https://atuvu.ca/contact">
677 + Nous contacter
678 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
679 + </a>
680 + </li>
681 +
682 +
683 + <li>
684 + <a href="https://atuvu.ca/a-propos">
685 + À propos
686 + <span><img src="https://atuvu.ca/img/icons/arrow-nav-bar.svg" alt="Flèche"></span>
687 + </a>
688 + </li>
689 +
690 + </ul>
691 +</div>
692 +
693 + <!-- GPT AdSlot 8 for Ad unit 'cc_atuvu_970x_1' ### Size: [[970,90],[970,250],[970,500]] -->
694 +<!-- Adslot's refresh function: googletag.pubads().refresh([gptadslots[7]]) -->
695 +<div id='div-gpt-ad-1026097-8' class="ad-mast">
696 +</div>
697 +
698 +<!-- End AdSlot 8 -->
699 +
700 + <section class="main-navbar">
701 + <div class="wrapper">
702 + <div class="left">
703 + <a href="https://atuvu.ca" class="main-logo">
704 + <img src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
705 + </a>
706 + <span class="phraselogo">Passionnés de spectacles et de culture</span>
707 + </div>
708 + <div class="right">
709 + <ul>
710 + <li class="promouvoir tooltip tooltip-bottom" data-tooltip="Inscrivez votre événement">
711 + <a href="https://atuvu.ca/producteur/ajouter-un-evenement">
712 + Promouvoir
713 + </a>
714 + </li>
715 + <li class="inscription-btn tooltip tooltip-bottom" data-tooltip="Devenez membre">
716 + <a href="https://atuvu.ca/inscription" class="btn inscription">Inscription</a>
717 + </li>
718 +
719 +
720 + <li class="user-connection tooltip tooltip-bottom" data-tooltip="Connexion membre">
721 + <a href="https://atuvu.ca/connexion" class="user-menu">
722 + <img src="https://atuvu.ca/img/icons/connection-button.svg" alt="">
723 + </a>
724 + <li class="search-icon ">
725 + <button type="button" class="search-toggle button-rounded">
726 + <img src="https://atuvu.ca/img/icons/search-black.svg" alt="">
727 + </button>
728 + </li>
729 + </li>
730 + </ul>
731 + </div>
732 + </div>
733 +</section>
734 +
735 + <section class="middle">
736 + <div class="wrapper">
737 + <div class="row">
738 + <div id="logoPubContainer" class="right">
739 + <a href="https://atuvu.ca" class="main-logo-desktop">
740 + <img src="/img/atuvu/atuvu.svg" alt="atuvu.ca">
741 + </a>
742 +
743 + </div>
744 + </div>
745 + </div>
746 + </section>
747 +
748 + <div class="white-navbar-search">
749 + <form class="search-form" action="https://atuvu.ca/recherche" method="get">
750 + <input class="white-navbar-search-input" type="search" name="search" placeholder="Recherche..."
751 + autocomplete="off">
752 + <button type="submit"></button>
753 + </form>
754 +
755 +
756 +
757 +
758 +</header>
759 + <!-- GPT AdSlot 6 for Ad unit 'cc_atuvu_pixel_1' ### Size: [[1,1]] -->
760 +<!-- Adslot's refresh function: googletag.pubads().refresh([gptadslots[5]]) -->
761 +<div id='div-gpt-ad-1026097-6'></div>
762 +
763 +<!-- End AdSlot 6 -->
764 + <!-- GPT AdSlot 7 for Ad unit 'cc_atuvu_pixel_2' ### Size: [[1,1]] -->
765 +<!-- Adslot's refresh function: googletag.pubads().refresh([gptadslots[6]]) -->
766 +<div id='div-gpt-ad-1026097-7'>
767 +</div>
768 +
769 +<!-- End AdSlot 7 -->
770 + <main>
771 + <div class="wrapper">
772 + <!-- LeaderboardMobileOne for Ad unit 'cc_atuvu_mobile_320x50' ### Size: [320,100] -->
773 +<div id='div-gpt-ad-1026097-9' class="leaderboard-mobile"></div>
774 +<!-- End LeaderboardMobileOne -->
775 +
776 + <div class="uniquePageId" data-keyname = "atuvu_event" data-id="33996" data-keyname2="atuvu_category"
777 + data-id2="Musique"></div>
778 +
779 + <div class="content-sidebar">
780 +
781 + <div class="left">
782 +
783 + <div class="event">
784 +
785 + <div class="event-heading">
786 + <div class="row">
787 + <div class="left">
788 + <div class="square-slider">
789 + <img src="/uploads/images/2026/08/arkansas-cropped.jpg">
790 + </div>
791 + </div>
792 +
793 + <div class="right">
794 + <div class="content">
795 +
796 + <div class="label-share">
797 + <a class="red-label"
798 + href="https://atuvu.ca/billets/concert-de-musique">Musique</a>
799 + <div class="red-label">Country</div>
800 + </div>
801 +
802 + <h1 class="name">Arkansas</h1>
803 +
804 +
805 + </div>
806 + </div>
807 + </div>
808 + </div>
809 +
810 + <div class="event-footing">
811 + <span class="anchor" id="dates"></span>
812 + <div class="event-plays">
813 +
814 + <h3></h3>
815 + <div class="firsts">
816 + <a href="https://atuvu.ca/billets/concert-de-musique/arkansas-eglise-saint-antoine-de-lavaltrie-20-aout-2026-gratuit" class="event-play">
817 + <div class="event-play__content">
818 + <div class="event-play__date-place">
819 + <div class="date">20 août 2026 - 20h00
820 + </div>
821 + <div class="place">
822 + <span>Église Saint-Antoine-de-Lavaltrie</span><br />
823 + <span>Lavaltrie</span>
824 + </div>
825 + </div>
826 + <div class="event-play__deal">
827 + <div class="price eventprice eventprice--event-play-table">
828 + <div class='price-details'>
829 + <div class="price-reg price-reg--strike">27.00 $</div>
830 + <div class="pricetype">Invitation gratuite</div>
831 + </div>
832 + </div>
833 + </div>
834 + </div>
835 + </a>
836 + </div>
837 + </div>
838 +</div>
839 +
840 +
841 +
842 +
843 + <div class="event-content"><p><strong>HOMMAGE À CHRIS STAPLETON</strong></p>
844 +<p> </p>
845 +<p>Arkansas rend un vibrant hommage à Chris Stapleton, l'auteur-compositeur-interprète américain largement reconnu comme l'un des artistes country les plus talentueux et influents de sa génération.</p>
846 +<p>Le groupe est formé de cinq musiciens professionnels, ayant parcouru le Québec avec divers projets musicaux. Ensemble, ils offrent un spectacle captivant, interprétant les plus grands succès de l'artiste, sélectionnés parmi tous ses albums.</p>
847 +<p>Parmi ces succès incontournables, on retrouve Tennessee Whiskey, Parachute, White Horse, Broken Halos, et bien d'autres encore.</p>
848 +<p>Chaque membre d'Arkansas apporte une richesse de talent et d'authenticité à l'ensemble. Que ce soit dans les bars ou sur de plus grandes scènes, Arkansas transporte son public dans un voyage musical à travers l'univers de Chris Stapleton. Sa musique, empreinte de chaleur et de réconfort, crée une atmosphère d'intimité et d'émotion, offrant à chaque concert une expérience unique tant pour les fans de Stapleton que pour les amoureux de la musique country.</p>
849 + </div>
850 +
851 + <div class="sub-details">
852 +
853 + </div>
854 +
855 +
856 +
857 + <div class="comment-section" id="comment-section">
858 +
859 + <!-- Heading -->
860 +
861 + <div class="red-tab">
862 + <div class="tab">Aucun commentaire</div>
863 + </div>
864 +
865 +
866 + <!-- Commentaires -->
867 + <div class="rows-layout">
868 +
869 + </div>
870 +
871 + <div class="pagination-container">
872 +
873 + </div>
874 +
875 +
876 +
877 +
878 + <h2>Vous devez être connecté pour donner un avis.</h2>
879 + <a href="https://atuvu.ca/connexion" class="btn-rect">Connectez-vous ici.</a>
880 +
881 +
882 +
883 +</div>
884 +
885 +
886 + <div id="allOffers">
887 + <div class="allOffers">
888 + <div class="red-tab arrow-space withlink">
889 + <div class="tab">
890 + <a href="https://atuvu.ca/programmation-culturelle">
891 + Toutes les offres
892 + </a>
893 + </div>
894 + <a href="https://atuvu.ca/programmation-culturelle" class="arrow"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25">
895 + <path d="m17.5 5.999-.707.707 5.293 5.293H1v1h21.086l-5.294 5.295.707.707L24 12.499l-6.5-6.5z"
896 + data-name="Right" />
897 + </svg></a>
898 + </div>
899 + <div class="allOffersData">
900 + <div class="slider-layout new-carousel-slider">
901 + <ul class="slider rows-layout new-carousel-slide" data-custom="true">
902 + <li class="carousel-cell slide home-thematique-slides">
903 + <div class="slider-event slider-item">
904 + <a href="https://atuvu.ca/billets-spectacle/arkansas" class="url">
905 + <div class="img" style="background-image:url(/uploads/images/2026/08/arkansas.jpg)">
906 + </div>
907 + <div class="slider-content">
908 + <div class="cats">
909 + <div class="main_cat red-label">Musique</div>
910 + <div class="sub_cat red-label">Country</div>
911 + </div>
912 + <div class="title">Arkansas</div>
913 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Lavaltrie</div>
914 + <div class="offer">Invitations gratuites</div>
915 + </div>
916 + </a>
917 +</div>
918 + </li>
919 + <li class="carousel-cell slide home-thematique-slides">
920 + <div class="slider-event slider-item">
921 + <a href="https://atuvu.ca/billets-spectacle/flow-festival-yoga-son" class="url">
922 + <div class="img" style="background-image:url(/uploads/images/2026/07/flow-festival-yoga-son.jpg)">
923 + <div class="festival_title">Flow Festival Yoga &amp; Son</div>
924 + </div>
925 + <div class="slider-content">
926 + <div class="cats">
927 + <div class="main_cat red-label">Divers</div>
928 + <div class="sub_cat red-label">Activité physique</div>
929 + </div>
930 + <div class="title">FLOW Festival Yoga &amp; Son</div>
931 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Sainte-Adèle</div>
932 + <div class="offer">Tarif réduit -20%</div>
933 + </div>
934 + </a>
935 +</div>
936 + </li>
937 + <li class="carousel-cell slide home-thematique-slides">
938 + <div class="slider-event slider-item">
939 + <a href="https://atuvu.ca/billets-spectacle/une-femme-denfer" class="url">
940 + <div class="img" style="background-image:url(/uploads/images/2026/06/femme-denfer.jpg)">
941 + </div>
942 + <div class="slider-content">
943 + <div class="cats">
944 + <div class="main_cat red-label">Théâtre</div>
945 + <div class="sub_cat red-label">Comédie</div>
946 + <div class="sub_cat red-label">Boulevard</div>
947 + <div class="sub_cat red-label">Vaudeville</div>
948 + </div>
949 + <div class="title">Une femme d&#039;enfer</div>
950 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
951 + <div class="offer">2 pour 1</div>
952 + </div>
953 + </a>
954 +</div>
955 + </li>
956 + <li class="carousel-cell slide home-thematique-slides">
957 + <div class="slider-event slider-item">
958 + <a href="https://atuvu.ca/billets-spectacle/soiree-dhumour-comedie-club-dernest" class="url">
959 + <div class="img" style="background-image:url(/uploads/images/2026/05/soiree-dhumour-comedie-club-dernest.jpg)">
960 + </div>
961 + <div class="slider-content">
962 + <div class="cats">
963 + <div class="main_cat red-label">Humour</div>
964 + <div class="sub_cat red-label">Général</div>
965 + <div class="sub_cat red-label">Stand-up</div>
966 + <div class="sub_cat red-label">Sketch</div>
967 + </div>
968 + <div class="title">Soirée d&#039;Humour | Comédie Club d&#039;Ernest</div>
969 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Rosemont-Petite-Patrie</div>
970 + <div class="offer">Tarif réduit -40%</div>
971 + </div>
972 + </a>
973 +</div>
974 + </li>
975 + <li class="carousel-cell slide home-thematique-slides">
976 + <div class="slider-event slider-item">
977 + <a href="https://atuvu.ca/billets-spectacle/cest-la-crise" class="url">
978 + <div class="img" style="background-image:url(/uploads/images/2026/06/cest-crise.jpg)">
979 + </div>
980 + <div class="slider-content">
981 + <div class="cats">
982 + <div class="main_cat red-label">Théâtre</div>
983 + <div class="sub_cat red-label">Boulevard</div>
984 + <div class="sub_cat red-label">Vaudeville</div>
985 + <div class="sub_cat red-label">Comédie</div>
986 + </div>
987 + <div class="title">C’est la crise !</div>
988 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
989 + <div class="offer">2 pour 1</div>
990 + </div>
991 + </a>
992 +</div>
993 + </li>
994 + <li class="carousel-cell slide home-thematique-slides">
995 + <div class="slider-event slider-item">
996 + <a href="https://atuvu.ca/billets-spectacle/mon-jour-de-chance" class="url">
997 + <div class="img" style="background-image:url(/uploads/images/2026/08/mon-jour-chance-image-titre-1200x875-1785763405.jpg)">
998 + </div>
999 + <div class="slider-content">
1000 + <div class="cats">
1001 + <div class="main_cat red-label">Théâtre</div>
1002 + <div class="sub_cat red-label">Comédie</div>
1003 + </div>
1004 + <div class="title">Mon jour de chance</div>
1005 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
1006 + <div class="offer">Tarif réduit -20%</div>
1007 + </div>
1008 + </a>
1009 +</div>
1010 + </li>
1011 + <li class="carousel-cell slide home-thematique-slides">
1012 + <div class="slider-event slider-item">
1013 + <a href="https://atuvu.ca/billets-spectacle/angelo-schiraldi-lelu" class="url">
1014 + <div class="img" style="background-image:url(/uploads/images/2026/07/angelo-schiraldi-lelu.jpg)">
1015 + </div>
1016 + <div class="slider-content">
1017 + <div class="cats">
1018 + <div class="main_cat red-label">Humour</div>
1019 + <div class="sub_cat red-label">Engagé</div>
1020 + <div class="sub_cat red-label">Général</div>
1021 + <div class="sub_cat red-label">Stand-up</div>
1022 + </div>
1023 + <div class="title">Angelo Schiraldi – L’Élu</div>
1024 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
1025 + <div class="offer">2 pour 1</div>
1026 + </div>
1027 + </a>
1028 +</div>
1029 + </li>
1030 + <li class="carousel-cell slide home-thematique-slides">
1031 + <div class="slider-event slider-item">
1032 + <a href="https://atuvu.ca/billets-spectacle/plaisirs-interdits" class="url">
1033 + <div class="img" style="background-image:url(/uploads/images/2026/03/plaisirs-interdits.jpg)">
1034 + </div>
1035 + <div class="slider-content">
1036 + <div class="cats">
1037 + <div class="main_cat red-label">Théâtre</div>
1038 + <div class="sub_cat red-label">Comédie</div>
1039 + <div class="sub_cat red-label">Musical</div>
1040 + <div class="sub_cat red-label">Opéra</div>
1041 + </div>
1042 + <div class="title">Plaisirs Interdits</div>
1043 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
1044 + <div class="offer">2 pour 1</div>
1045 + </div>
1046 + </a>
1047 +</div>
1048 + </li>
1049 + <li class="carousel-cell slide home-thematique-slides">
1050 + <div class="slider-event slider-item">
1051 + <a href="https://atuvu.ca/billets-spectacle/festival-de-montgolfieres-de-gatineau" class="url">
1052 + <div class="img" style="background-image:url(/uploads/images/2026/06/festival-montgolfieres-gatineau.jpg)">
1053 + <div class="festival_title">Festival de montgolfières de Gatineau (FMG)</div>
1054 + </div>
1055 + <div class="slider-content">
1056 + <div class="cats">
1057 + <div class="main_cat red-label">Musique</div>
1058 + <div class="sub_cat red-label">Pop</div>
1059 + <div class="sub_cat red-label">Rap / Hip-Hop</div>
1060 + </div>
1061 + <div class="title">Festival de montgolfières de Gatineau</div>
1062 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Gatineau</div>
1063 + <div class="offer">74.99 $</div>
1064 + </div>
1065 + </a>
1066 +</div>
1067 + </li>
1068 + <li class="carousel-cell slide home-thematique-slides">
1069 + <div class="slider-event slider-item">
1070 + <a href="https://atuvu.ca/billets-spectacle/si-dieu-le-veut" class="url">
1071 + <div class="img" style="background-image:url(/uploads/images/2026/01/1000x1000-1768572426.jpg)">
1072 + </div>
1073 + <div class="slider-content">
1074 + <div class="cats">
1075 + <div class="main_cat red-label">Théâtre</div>
1076 + <div class="sub_cat red-label">Théâtre documentaire</div>
1077 + </div>
1078 + <div class="title">Si Dieu le veut!</div>
1079 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
1080 + <div class="offer">Invitations gratuites</div>
1081 + </div>
1082 + </a>
1083 +</div>
1084 + </li>
1085 + <li class="carousel-cell slide home-thematique-slides">
1086 + <div class="slider-event slider-item">
1087 + <a href="https://atuvu.ca/billets-spectacle/les-dimanches-du-conte-soiree-douverture-1785507851" class="url">
1088 + <div class="img" style="background-image:url(/uploads/images/2026/07/dimanches-conte-soiree-douverture-1785507851.jpg)">
1089 + <div class="festival_title">Les Dimanches du conte</div>
1090 + </div>
1091 + <div class="slider-content">
1092 + <div class="cats">
1093 + <div class="main_cat red-label">Conte</div>
1094 + <div class="sub_cat red-label">Contemporain</div>
1095 + </div>
1096 + <div class="title">Les Dimanches du conte | Soirée d&#039;ouverture</div>
1097 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Montréal</div>
1098 + <div class="offer">Tarif réduit -50%</div>
1099 + </div>
1100 + </a>
1101 +</div>
1102 + </li>
1103 + <li class="carousel-cell slide home-thematique-slides">
1104 + <div class="slider-event slider-item">
1105 + <a href="https://atuvu.ca/billets-spectacle/lhorizon-de-kheops" class="url">
1106 + <div class="img" style="background-image:url(/uploads/images/2025/12/lhorizon-kheops.jpg)">
1107 + </div>
1108 + <div class="slider-content">
1109 + <div class="cats">
1110 + <div class="main_cat red-label">Divers</div>
1111 + <div class="sub_cat red-label">Spectacle immersif</div>
1112 + </div>
1113 + <div class="title">L&#039;Horizon de Khéops</div>
1114 + <div class="city"><img src="/img/icons/map-icon.png" alt="atuvu">Dans plusieurs villes</div>
1115 + <div class="offer">Tarif réduit -15%</div>
1116 + </div>
1117 + </a>
1118 +</div>
1119 + </li>
1120 + </ul>
1121 +</div>
1122 + </div>
1123 + </div>
1124 + </div>
1125 +
1126 + </div>
1127 +
1128 + </div>
1129 +
1130 + <div class="right sidebar mobile-sidebar">
1131 + <a href="https://atuvu.ca/inscription" class="autopromo-ad autopromo-subscribe-ad">
1132 + <img src="https://atuvu.ca/img/autopromo-subscribe.jpg" alt="">
1133 +</a>
1134 +
1135 +
1136 + <div class="mobile-only-ad-zone">
1137 +
1138 + <!-- BigBoxOneMobile 'cc_atuvu_mobile_320x250_1' ### Size: [[300, 600], [300, 250]] -->
1139 +<div id='div-gpt-ad-1608743744528-0' class="padded-ad padded-ad-mobile">
1140 +
1141 +</div>
1142 +
1143 + <!-- End BigBoxOneMobile -->
1144 +
1145 + </div>
1146 +
1147 +<!-- BigBoxOneDesktop for Ad unit 'cc_atuvu_300x_1' ### Size: [300, 250],[300, 600], -->
1148 +<div id='div-gpt-ad-1026097-3' class="padded-ad">
1149 +
1150 +</div>
1151 +
1152 +<!-- End BigBoxOneDesktop -->
1153 +
1154 +
1155 +
1156 +<div class="plays-guichet-sidebar-articles">
1157 + <div>
1158 + <a class="article-link" href="https://atuvu.ca/magazine-culturel/critiques/festival-stradivaria-2026-rhapsodie-estivale-une-passionnelle-explosion-denergie"></a>
1159 + <article class="featured-article" style="background-image: url('/uploads/images/2026/08/festival-stradivaria-2026-rhapsodie-estivale-passionnelle-cropped.jpg?cropped_at=1786995763')">
1160 + <div class="content">
1161 + <a class="red-label" href="https://atuvu.ca/magazine-culturel/critiques">Critiques</a>
1162 + <h3>Festival Stradivaria 2026 | «Rhapsodie estivale», une passionnelle explosion d’énergie</h3>
1163 + <span class="info">Par Daniel Raymond |
1164 + 17 août 2026</span>
1165 + </div>
1166 + </article>
1167 + </div>
1168 +<div>
1169 + <a class="article-link" href="https://atuvu.ca/magazine-culturel/entrevue/la-fete-de-la-musique-de-tremblant-une-fin-de-semaine-de-musique-et-de-nature-au-coeur-des-laurentides"></a>
1170 + <article class="featured-article" style="background-image: url('/uploads/images/2026/08/fete-musique-tremblant-fin-semaine-musique-nature-au-coeur-1786383239-cropped.jpg?cropped_at=1786383231')">
1171 + <div class="content">
1172 + <a class="red-label" href="https://atuvu.ca/magazine-culturel/entrevue">Entrevue</a>
1173 + <h3>La Fête de la Musique de Tremblant 2026 : une fin de semaine de musique et de nature au cœur des Laurentides</h3>
1174 + <span class="info">Par Ève Christian |
1175 + 10 août 2026</span>
1176 + </div>
1177 + </article>
1178 + </div>
1179 + </div>
1180 +
1181 + <div class="mobile-only-ad-zone">
1182 +
1183 + <!-- BigBoxTwoMobile 'cc_atuvu_mobile_320x250_2' ### Size: [[300, 600], [300, 250]] -->
1184 +<div id='div-gpt-ad-1608743744528-1' class="padded-ad padded-ad-mobile">
1185 +
1186 +</div>
1187 +
1188 + <!-- End BigBoxTwoMobile -->
1189 +
1190 + </div>
1191 +<!-- BigBoxTwoDesktop for Ad unit 'cc_atuvu_300x_2' ### Size: [300, 250],[300, 600], -->
1192 +<div id='div-gpt-ad-1026097-4' class="padded-ad">
1193 +
1194 +</div>
1195 +<!-- End BigBoxTwoDesktop -->
1196 +
1197 +
1198 +
1199 +
1200 +
1201 +
1202 +<div class="red-tab social-links">
1203 + <div class="tab">
1204 + Suivez-nous
1205 + </div>
1206 + <div class="red-logos">
1207 + <a href="https://www.facebook.com/pages/atuvuca/129197624972" target="_blank">
1208 + <object class="facebook" type="image/svg+xml" data="/img/icons/facebook-icon-red.svg"></object>
1209 +
1210 + </a>
1211 + <a href="https://x.com/atuvu_ca" target="_blank">
1212 + <object class="x" type="image/svg+xml" data="/img/icons/x-icon-red.svg"></object>
1213 +
1214 + </a>
1215 + <a href="https://www.instagram.com/atuvu.ca" target="_blank">
1216 + <object class="instagram" type="image/svg+xml" data="/img/icons/instagram-icon-red.svg"></object>
1217 +
1218 + </a>
1219 + </div>
1220 +</div>
1221 +
1222 + <div class="mobile-only-ad-zone">
1223 +
1224 + <!-- BigBoxThreeMobile 'cc_atuvu_mobile_320x250_3' ### Size: [[300, 600], [300, 250]] -->
1225 +<div id='div-gpt-ad-1608743744528-2' class="padded-ad padded-ad-mobile">
1226 +
1227 +</div>
1228 +
1229 + <!-- End BigBoxThreeMobile -->
1230 +
1231 + </div>
1232 +
1233 +<!-- BigBoxThreeDesktop for Ad unit 'cc_atuvu_300x_3' ### Size: [300, 250],[300, 600], -->
1234 +<div id='div-gpt-ad-1026097-5' class="padded-ad">
1235 +
1236 +</div>
1237 +<!-- End BigBoxThreeDesktop -->
1238 + </div>
1239 +
1240 + </div>
1241 +
1242 +
1243 +
1244 + <div class="row">
1245 +
1246 + <div class="credit-full-width">
1247 + <!-- GPT AdSlot 2 for Ad unit 'cc_atuvu_728x90_2' ### Size: [[728,90],[320,50]] -->
1248 + <!-- Adslot's refresh function: googletag.pubads().refresh([gptadslots[1]]) -->
1249 + <div id='div-gpt-ad-1026097-2' class="leaderboard-two">
1250 + </div>
1251 +
1252 + <!-- End AdSlot 2 -->
1253 +
1254 + <!-- LeaderboardMobileTwo for Ad unit 'cc_atuvu_mobile_320x50_2' ### Size: [320,100] -->
1255 +<div id='div-gpt-ad-1026097-10' class="leaderboard-mobile"></div>
1256 +<!-- End LeaderboardMobileTwo -->
1257 + <a href="https://www.culturecible.ca/" target="_blank"><img class="desktop" src="/img/cultureCible.jpg"
1258 + alt="img-credit"></a>
1259 + <a href="https://www.culturecible.ca/" target="_blank"><img class="mobile" src="/img/cultureCible-petit.jpg"
1260 + alt="img-credit"></a>
1261 +
1262 + </div>
1263 +</div>
1264 + </div>
1265 + </main>
1266 +
1267 + <footer>
1268 + <section class="grey">
1269 + <div class="wrapper">
1270 + <div class = "topHidden">
1271 + <p>Suivez-nous</p>
1272 + <a href="https://www.facebook.com/pages/atuvuca/129197624972" target="_blank">
1273 + <object class="facebook" type="image/svg+xml" data="/img/icons/facebook-icon.svg"></object>
1274 + </a>
1275 + <a href="https://x.com/atuvu_ca" target="_blank">
1276 + <object class="x" type="image/svg+xml" data="/img/icons/x-icon.svg"></object>
1277 + </a>
1278 + <a href="https://www.instagram.com/atuvu.ca" target="_blank">
1279 + <object class="instagram" type="image/svg+xml" data="/img/icons/instagram-icon.svg"></object>
1280 + </a>
1281 + </div>
1282 + <div class="row middle">
1283 + <ul>
1284 + <li><a href="https://atuvu.ca/a-propos">À propos d'atuvu.ca</a></li>
1285 + <li><a href="https://atuvu.ca/producteur/ajouter-un-evenement">Inscrire un événement</a></li>
1286 + <li><a href="https://atuvu.ca/media-kit">Annoncer avec nous</a></li>
1287 + <li><a href="https://atuvu.ca/devenir-membre">Devenir membre</a></li>
1288 + <li><a href="https://atuvu.ca/charte">Charte du membre</a></li>
1289 + </ul>
1290 + <ul class="hide-mobile">
1291 + <li><a href="https://atuvu.ca/billets/concert-de-musique">Musique</a>
1292 + </li>
1293 + <li><a href="https://atuvu.ca/billets/piece-de-theatre">Théâtre</a></li>
1294 + <li><a href="https://atuvu.ca/billets/spectacle-de-danse">Danse</a></li>
1295 + <li><a href="https://atuvu.ca/billets/spectacle-humour">Humour</a></li>
1296 + <li><a href="https://atuvu.ca/billets/spectacle-cirque">Cirque</a></li>
1297 + </ul>
1298 + <ul class="hide-mobile">
1299 + <li><a href="https://atuvu.ca/billets/spectacle-pour-enfant">Conte</a>
1300 + </li>
1301 + <li><a
1302 + href="https://atuvu.ca/billets/spectacle-improvisation">Improvisation</a>
1303 + </li>
1304 + <li><a href="https://atuvu.ca/billets/spectacle-de-magie">Magie</a></li>
1305 + <li><a href="https://atuvu.ca/billets/actualite-cinema">Cinéma</a></li>
1306 + <li><a href="https://atuvu.ca/billets/activites-a-faire">Divers</a></li>
1307 + </ul>
1308 + <ul>
1309 + <li><a href="https://atuvu.ca/magazine-culturel">Magazine</a></li>
1310 + <li><a href="https://atuvu.ca/choisir-un-abonnement">Abonnement VIP</a></li>
1311 + <li><a href="https://atuvu.ca/archives">Archives</a></li>
1312 + </ul>
1313 + <ul>
1314 + <li><a href="https://atuvu.ca/conditions">Conditions d'utilisation</a></li>
1315 + <li><a href="https://atuvu.ca/privacy">Politique de confidentialité</a></li>
1316 +
1317 + <li><a href="https://atuvu.ca/contact">Nous contacter</a></li>
1318 + </ul>
1319 + <ul class="amis">
1320 + <li>Sites amis:</li>
1321 + <li><a href="https://www.baronmag.com/" target="_blank">Baron MAG</a></li>
1322 + <li><a href="https://www.labibleurbaine.com/" target="_blank">Bible Urbaine</a></li>
1323 + <li><a href="https://lecanalauditif.ca/" target="_blank">Le Canal Auditif</a></li>
1324 + <li><a href="http://www.sorstu.ca/" target="_blank">Sors-tu.ca</a></li>
1325 + </ul>
1326 + </div>
1327 +
1328 + <div class="row bottom">
1329 + <div class = "left">
1330 + <p>4521 Boul. Saint-Laurent, Montréal, QC H2T 1R2, Canada</p>
1331 +
1332 + <p>&copy; Copyright ATUVU.CA Tous droits réservés</p>
1333 + </div>
1334 + <div class = "right">
1335 + <p>Suivez-nous</p>
1336 + <a href="https://www.facebook.com/pages/atuvuca/129197624972" target="_blank">
1337 + <object class="facebook" type="image/svg+xml" data="/img/icons/facebook-icon.svg"></object>
1338 + </a>
1339 + <a href="https://x.com/atuvu_ca" target="_blank">
1340 + <object class="x" type="image/svg+xml" data="/img/icons/x-icon.svg"></object>
1341 + </a>
1342 +
1343 + <a href="https://www.instagram.com/atuvu.ca" target="_blank">
1344 + <object class="instagram" type="image/svg+xml" data="/img/icons/instagram-icon.svg"></object>
1345 + </a>
1346 + </div>
1347 + </div>
1348 + <div class="row bottom">
1349 + <p>Le nouveau site atuvu.ca a reçu le soutien du Fonds du Canada pour les périodiques</p>
1350 + <img class="finance_canada" src="/img/finance-par-canada.png" alt="">
1351 + </div>
1352 + </div>
1353 + </section>
1354 +</footer>
1355 +</div>
1356 +
1357 + <script>
1358 + console.log('%cLogged out as Member', 'font-size:14px; color:red;');
1359 +
1360 + console.log('%cLogged out as Admin', 'font-size:14px; color:red;');
1361 +
1362 + console.log('%cLogged out as Producer', 'font-size:14px; color:red;');
1363 + </script>
1364 +
1365 +
1366 + <script>
1367 + function isIE() {
1368 + ua = navigator.userAgent;
1369 + /* MSIE used to detect old browsers and Trident used to newer ones*/
1370 + var is_ie = ua.indexOf("MSIE ") > -1 || ua.indexOf("Trident/") > -1;
1371 +
1372 + return is_ie;
1373 + }
1374 + /* Create an alert to show if the browser is IE or not */
1375 + if (isIE()) {
1376 +
1377 + alert(
1378 + 'Vous utilisez un ancien navigateur. Veuillez changer de navigateur pour accéder à tout le contenu de atuvu.ca');
1379 +
1380 + var warningDiv = document.getElementById("warningBox");
1381 + warningDiv.style.display = "block";
1382 +
1383 + }
1384 +</script>
1385 +
1386 +<script type="application/ld+json">
1387 + {
1388 + "@context": "https://schema.org",
1389 + "@type": "Organization",
1390 + "name": "atuvu.ca",
1391 + "url": "https://atuvu.ca",
1392 + "sameAs":[
1393 + "https://www.facebook.com/atuvuca/",
1394 + "https://www.instagram.com/atuvu.ca/",
1395 + "https://www.linkedin.com/company/atuvu-ca/",
1396 + "https://X.com/atuvu_ca"
1397 + ]
1398 + }
1399 + </script>
1400 +
1401 +<script type="application/ld+json">
1402 + {
1403 + "@context": "https://schema.org",
1404 + "@type": "Organization",
1405 + "url": "https://atuvu.ca",
1406 + "contactPoint": [
1407 + { "@type": "ContactPoint",
1408 + "telephone": "+1-514-416-6114",
1409 + "contactType": "customer service"
1410 + }
1411 + ]
1412 + }
1413 + </script>
1414 +
1415 +<script type="application/ld+json">
1416 + {
1417 + "@context": "https://schema.org",
1418 + "@type": "Organization",
1419 + "url": "https://atuvu.ca",
1420 + "logo": "https://atuvu.ca/img/atuvu/atuvu.png"
1421 + }
1422 + </script>
1423 +<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a2cb65febd42fa82',t:'MTc4Njk5ODIyNQ=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script><script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"140fb1b6e8224ef0a24af71ba9679ef5","r":1}' crossorigin="anonymous"></script>
1424 +</body>
1425 +
1426 +</html>
added tests/fixtures/evenko_sample.json +1 −0
@@ -0,0 +1 @@
1 +{"hits": [{"entity_type": "evenko_show", "show_time": 1787006700, "show_date": 1786939200, "door_time": 1787003100, "weekend": false, "show_language": "", "event_language_code": "", "announce_date": 1774879200, "additional_information": {"representation_status": ["buy_now"], "waiting_room_min": null, "regular_ticket": {"code": "regular", "reference_entity_code": "tickets", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Régulier"}, {"locale": "en_CA", "channel": null, "data": "Regular"}]}, "created": "2023-06-14T18:02:09+00:00", "updated": "2023-09-18T19:22:46+00:00", "regular_ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/31006472BF647B96?lang=fr-ca&brand=placebell"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/31006472BF647B96?lang=en-ca&brand=placebell"}]}, "ticket_parking": {"code": "parking", "reference_entity_code": "parking", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Stationnements"}, {"locale": "en_CA", "channel": null, "data": "Parking"}], "parking_description": [{"locale": "fr_CA", "channel": null, "data": "Prévoyez à l'avance votre stationnement en réservant une place maintenant."}, {"locale": "en_CA", "channel": null, "data": "Plan ahead by reserving a parking space right now."}]}, "created": "2023-06-03T04:32:01+00:00", "updated": "2023-09-18T17:14:57+00:00", "ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/31006472BF8E7BBB?lang=fr-ca&brand=placebell"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/31006472BF8E7BBB?lang=en-ca&brand=placebell"}]}, "ticket_accommodations": [], "ticket_restaurants": [], "premium_tickets": [], "other_tickets": [], "presale_tickets": []}, "hide_show_time": false, "sale_date": 1775138400, "tour_slug": null, "presented_by_code": "", "advance_registration": false, "advanced_registration_ticket_url": "", "advanced_registration_ticket_name": "", "advanced_registration_ticket_details": "", "pre_sale_start_date": "", "pre_sale_start_time": "", "pre_sale_end_date": "", "pre_sale_end_time": "", "is_premium": true, "premium_experience": [{"name": "Expérience + | Place Bell", "slug": "experience-place-bell", "image": "//images.ctfassets.net/3yxl57nu0yl4/7m3cRcUgcF8Zg4MC9z0xOa/9459ace17bc222c73e62692cbc7928c9/premium_experience_Sans_titre-2.jpg", "description": "<p>Rapprochez-vous de vos artistes préférés avec des sièges situés dans les meilleures sections de la Place Bell.</p>\n<ul>\n<li>Sièges situés au cœur de l’action</li>\n<li>Vue imprenable sur la scène</li>\n<li>Entrée dédiée vers l’amphithéâtre</li>\n</ul>\n", "see_more_url": "", "cta_url": "https://am.ticketmaster.com/evenko/buy/quickbuy?id=MTg4Mw=="}], "venue": {"code": "place_bell", "slug": "place-bell", "name": "Place Bell", "region_code": "l_ns", "region": "Laval / Rive-Nord", "city": "Laval", "website": "https://www.placebell.ca/", "order": 60}, "status": 1, "slug": "deep-purple", "title": "Deep Purple", "description": "", "subtitle": "", "tour_name": "", "presented_by": "", "festival_slug": "", "promotional_tag": "", "promotional_tag_slug": "", "event_tag": "Métal,Rock", "free": null, "event_discount": {"percentage": 0, "amount": 0, "minimum": 0}, "age": "everyone", "category": ["Musique"], "category_code": "music", "category_slug": "musique", "genre": [{"name": "Métal/Punk", "code": "metal_punk", "slug": "metal-punk"}, {"name": "Musique", "code": "music", "slug": "musique"}, {"name": "Rock", "code": "rock", "slug": "rock"}], "headliners": ["Deep Purple"], "supports": ["Kansas", "Jefferson Starship"], "thumbnail": "//images.ctfassets.net/3yxl57nu0yl4/6mLMmhdOvL8NwVv2vVWxnH/b8e6a635c897d30a80aa391421062e22/PB-260817-DeepPurple-Web-Visuels-RS-680x473-NoText.jpg", "thumbnail_description": {"image_alt": "deep purple 2026"}, "linked_shows": {"e008035_fr": {"show_date": 1786939200, "show_time": 1787006700}, "e008036_fr": {"show_date": "", "show_time": ""}}, "objectID": "e008035_fr", "_highlightResult": {"venue": {"name": {"value": "Place Bell", "matchLevel": "none", "matchedWords": []}}, "title": {"value": "Deep Purple", "matchLevel": "none", "matchedWords": []}, "tour_name": {"value": "", "matchLevel": "none", "matchedWords": []}, "presented_by": {"value": "", "matchLevel": "none", "matchedWords": []}, "promotional_tag": {"value": "", "matchLevel": "none", "matchedWords": []}, "event_tag": {"value": "Métal,Rock", "matchLevel": "none", "matchedWords": []}, "genre": [{"name": {"value": "Métal/Punk", "matchLevel": "none", "matchedWords": []}, "code": {"value": "metal_punk", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "metal-punk", "matchLevel": "none", "matchedWords": []}}, {"name": {"value": "Musique", "matchLevel": "none", "matchedWords": []}, "code": {"value": "music", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "musique", "matchLevel": "none", "matchedWords": []}}, {"name": {"value": "Rock", "matchLevel": "none", "matchedWords": []}, "code": {"value": "rock", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "rock", "matchLevel": "none", "matchedWords": []}}], "headliners": [{"value": "Deep Purple", "matchLevel": "none", "matchedWords": []}], "supports": [{"value": "Kansas", "matchLevel": "none", "matchedWords": []}, {"value": "Jefferson Starship", "matchLevel": "none", "matchedWords": []}]}}, {"entity_type": "evenko_show", "show_time": 1787097600, "show_date": 1787025600, "door_time": 1787094000, "weekend": false, "show_language": "", "event_language_code": "", "announce_date": 1774015200, "additional_information": {"representation_status": ["buy_now"], "waiting_room_min": null, "regular_ticket": {"code": "regular", "reference_entity_code": "tickets", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Régulier"}, {"locale": "en_CA", "channel": null, "data": "Regular"}]}, "created": "2023-06-14T18:02:09+00:00", "updated": "2023-09-18T19:22:46+00:00", "regular_ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/310064719BA85821?lang=fr-ca&brand=placebell"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/310064719BA85821?lang=en-ca&brand=placebell"}]}, "ticket_parking": {"code": "parking", "reference_entity_code": "parking", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Stationnements"}, {"locale": "en_CA", "channel": null, "data": "Parking"}], "parking_description": [{"locale": "fr_CA", "channel": null, "data": "Prévoyez à l'avance votre stationnement en réservant une place maintenant."}, {"locale": "en_CA", "channel": null, "data": "Plan ahead by reserving a parking space right now."}]}, "created": "2023-06-03T04:32:01+00:00", "updated": "2023-09-18T17:14:57+00:00", "ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/310064719BAB5825?lang=fr-ca&brand=placebell"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/310064719BAB5825?lang=en-ca&brand=placebell"}]}, "ticket_accommodations": [], "ticket_restaurants": [], "premium_tickets": [], "other_tickets": [], "presale_tickets": []}, "hide_show_time": false, "sale_date": 1774620000, "tour_slug": "my-mess-my-heart-my-life-tour", "presented_by_code": "", "advance_registration": false, "advanced_registration_ticket_url": "", "advanced_registration_ticket_name": "", "advanced_registration_ticket_details": "", "pre_sale_start_date": "", "pre_sale_start_time": "", "pre_sale_end_date": "", "pre_sale_end_time": "", "is_premium": true, "premium_experience": [{"name": "Expérience + | Place Bell", "slug": "experience-place-bell", "image": "//images.ctfassets.net/3yxl57nu0yl4/7m3cRcUgcF8Zg4MC9z0xOa/9459ace17bc222c73e62692cbc7928c9/premium_experience_Sans_titre-2.jpg", "description": "<p>Rapprochez-vous de vos artistes préférés avec des sièges situés dans les meilleures sections de la Place Bell.</p>\n<ul>\n<li>Sièges situés au cœur de l’action</li>\n<li>Vue imprenable sur la scène</li>\n<li>Entrée dédiée vers l’amphithéâtre</li>\n</ul>\n", "see_more_url": "", "cta_url": "https://am.ticketmaster.com/evenko/buy/quickbuy?id=MTg4NQ== "}], "venue": {"code": "place_bell", "slug": "place-bell", "name": "Place Bell", "region_code": "l_ns", "region": "Laval / Rive-Nord", "city": "Laval", "website": "https://www.placebell.ca/", "order": 60}, "status": 1, "slug": "myles-smith", "title": "Myles Smith", "description": "Au cours des deux dernières années, Myles Smith s’est rapidement imposé comme l’une des nouvelles voix les plus captivantes du Royaume-Uni, reconnu pour ses compositions empreintes d’une grande sincérité émotionnelle, son interprétation vocale riche et sa capacité à transposer ses expériences personnelles en chansons qui trouvent un écho à l’échelle mondiale. Son single révélateur, « Stargazing », est devenu un phénomène mondial, cumulant des centaines de millions d’écoutes et faisant découvrir sa musique à un public international. Cette chanson a marqué un tournant décisif dans son ascension, imposant Myles comme un artiste capable de transformer des moments profondément personnels en chansons qui trouvent un écho à l’échelle universelle.", "subtitle": "", "tour_name": "My Mess, My Heart, My Life Tour", "presented_by": "", "festival_slug": "", "promotional_tag": "", "promotional_tag_slug": "", "event_tag": "Folk,Pop", "free": null, "event_discount": {"percentage": 0, "amount": 0, "minimum": 0}, "age": "everyone", "category": ["Musique"], "category_code": "music", "category_slug": "musique", "genre": [{"name": "Country et Folk", "code": "country_and_folk", "slug": "country-et-folk"}, {"name": "Musique", "code": "music", "slug": "musique"}, {"name": "Pop", "code": "pop", "slug": "pop"}], "headliners": ["Myles Smith"], "supports": ["Michael Marcagi"], "thumbnail": "//images.ctfassets.net/3yxl57nu0yl4/1DmNAI9gmr97K6Lpy6luPL/74b304a2c236a67125b5e846dc7bb2a9/myles_smith_2026.jpg", "thumbnail_description": {"image_alt": "myles smith 2026"}, "linked_shows": {"e007985_fr": {"show_date": 1787025600, "show_time": 1787097600}}, "objectID": "e007985_fr", "_highlightResult": {"venue": {"name": {"value": "Place Bell", "matchLevel": "none", "matchedWords": []}}, "title": {"value": "Myles Smith", "matchLevel": "none", "matchedWords": []}, "tour_name": {"value": "My Mess, My Heart, My Life Tour", "matchLevel": "none", "matchedWords": []}, "presented_by": {"value": "", "matchLevel": "none", "matchedWords": []}, "promotional_tag": {"value": "", "matchLevel": "none", "matchedWords": []}, "event_tag": {"value": "Folk,Pop", "matchLevel": "none", "matchedWords": []}, "genre": [{"name": {"value": "Country et Folk", "matchLevel": "none", "matchedWords": []}, "code": {"value": "country_and_folk", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "country-et-folk", "matchLevel": "none", "matchedWords": []}}, {"name": {"value": "Musique", "matchLevel": "none", "matchedWords": []}, "code": {"value": "music", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "musique", "matchLevel": "none", "matchedWords": []}}, {"name": {"value": "Pop", "matchLevel": "none", "matchedWords": []}, "code": {"value": "pop", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "pop", "matchLevel": "none", "matchedWords": []}}], "headliners": [{"value": "Myles Smith", "matchLevel": "none", "matchedWords": []}], "supports": [{"value": "Michael Marcagi", "matchLevel": "none", "matchedWords": []}]}}, {"entity_type": "evenko_show", "show_time": 1787182200, "show_date": 1787112000, "door_time": 1787176800, "weekend": false, "show_language": "", "event_language_code": "", "announce_date": 1779123600, "additional_information": {"representation_status": ["buy_now"], "waiting_room_min": null, "regular_ticket": {"code": "regular", "reference_entity_code": "tickets", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Régulier"}, {"locale": "en_CA", "channel": null, "data": "Regular"}]}, "created": "2023-06-14T18:02:09+00:00", "updated": "2023-09-18T19:22:46+00:00", "regular_ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/310064AEDB1FB074?lang=fr-ca&brand=mtelus"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/310064AEDB1FB074?lang=en-ca&brand=mtelus"}]}, "ticket_parking": {"code": "parking", "reference_entity_code": "parking", "values": {"label": [{"locale": "fr_CA", "channel": null, "data": "Stationnements"}, {"locale": "en_CA", "channel": null, "data": "Parking"}], "parking_description": [{"locale": "fr_CA", "channel": null, "data": "Prévoyez à l'avance votre stationnement en réservant une place maintenant."}, {"locale": "en_CA", "channel": null, "data": "Plan ahead by reserving a parking space right now."}]}, "created": "2023-06-03T04:32:01+00:00", "updated": "2023-09-18T17:14:57+00:00", "ticket_url": [{"locale": "fr_CA", "data": "https://www.ticketmaster.ca/event/310064AEDB7CB0E7?lang=fr-ca&brand=mtelus"}, {"locale": "en_CA", "data": "https://www.ticketmaster.ca/event/310064AEDB7CB0E7?lang=en-ca&brand=mtelus"}]}, "ticket_accommodations": [], "ticket_restaurants": [], "premium_tickets": [], "other_tickets": [], "presale_tickets": []}, "hide_show_time": false, "sale_date": 1779372000, "tour_slug": "the-monica-tour", "presented_by_code": "metro_metro", "advance_registration": false, "advanced_registration_ticket_url": "", "advanced_registration_ticket_name": "", "advanced_registration_ticket_details": "", "pre_sale_start_date": "", "pre_sale_start_time": "", "pre_sale_end_date": "", "pre_sale_end_time": "", "is_premium": false, "premium_experience": [], "venue": {"code": "mtelus", "slug": "mtelus", "name": "MTELUS", "region_code": "im", "region": "Île de Montréal", "city": "Montréal", "website": "https://mtelus.com/fr", "order": 70}, "status": 1, "slug": "jack-harlow", "title": "Jack Harlow", "description": "", "subtitle": "", "tour_name": "The Monica Tour ", "presented_by": "Metro Metro présente", "festival_slug": "", "promotional_tag": "", "promotional_tag_slug": "", "event_tag": "Hip-Hop,Rap", "free": null, "event_discount": {"percentage": 0, "amount": 0, "minimum": 0}, "age": "everyone", "category": ["Musique"], "category_code": "music", "category_slug": "musique", "genre": [{"name": "Musique", "code": "music", "slug": "musique"}, {"name": "Rap/Hip-Hop", "code": "rap_hip_hop", "slug": "rap-hip-hop"}], "headliners": ["Jack Harlow"], "supports": ["James Savage "], "thumbnail": "//images.ctfassets.net/3yxl57nu0yl4/1UZAnvKKy2is2bw9cK8yF6/62f64394e4d0347ed85130c19f5986c6/jack_harlow_2026.jpg", "thumbnail_description": {"image_alt": "jack harlow 2026"}, "linked_shows": {"e008414_fr": {"show_date": 1787112000, "show_time": 1787182200}}, "objectID": "e008414_fr", "_highlightResult": {"venue": {"name": {"value": "MTELUS", "matchLevel": "none", "matchedWords": []}}, "title": {"value": "Jack Harlow", "matchLevel": "none", "matchedWords": []}, "tour_name": {"value": "The Monica Tour ", "matchLevel": "none", "matchedWords": []}, "presented_by": {"value": "Metro Metro présente", "matchLevel": "none", "matchedWords": []}, "promotional_tag": {"value": "", "matchLevel": "none", "matchedWords": []}, "event_tag": {"value": "Hip-Hop,Rap", "matchLevel": "none", "matchedWords": []}, "genre": [{"name": {"value": "Musique", "matchLevel": "none", "matchedWords": []}, "code": {"value": "music", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "musique", "matchLevel": "none", "matchedWords": []}}, {"name": {"value": "Rap/Hip-Hop", "matchLevel": "none", "matchedWords": []}, "code": {"value": "rap_hip_hop", "matchLevel": "none", "matchedWords": []}, "slug": {"value": "rap-hip-hop", "matchLevel": "none", "matchedWords": []}}], "headliners": [{"value": "Jack Harlow", "matchLevel": "none", "matchedWords": []}], "supports": [{"value": "James Savage ", "matchLevel": "none", "matchedWords": []}]}}], "nbHits": 1316, "page": 0, "nbPages": 334, "hitsPerPage": 3, "exhaustiveNbHits": true, "exhaustiveTypo": true, "exhaustive": {"nbHits": true, "typo": true}, "query": "", "params": "hitsPerPage=3&page=0", "queryID": "5d35b6845d5ae7ca550a4b5185e29638", "_automaticInsights": true, "renderingContent": {}, "processingTimeMS": 1, "processingTimingsMS": {"_request": {"roundTrip": 28}, "extensions": 1, "total": 1}, "serverTimeMS": 2}
\ No newline at end of file
added tests/fixtures/laval_sample.json +449 −0
@@ -0,0 +1,449 @@
1 +[
2 + {
3 + "Title": "ANNULÉ - Rencontre avec Kevin Raphaël",
4 + "Excerpt": "<p><strong>Annulé&#160;</strong>-&#160;Viens rencontrer Kevin Raphaël, l’homme aux mille talents! L’humoriste, animateur radio et télé, podcasteur et entraîneur de football partagera avec toi quelques secrets, ses lectures coups de cœur, et peut-être même ses trucs pour jongler comme un pro avec tous ses rôles!<br></p>",
5 + "PageUrl": "/Pages/Fr/Calendrier/biblio-ados-rencontre-kevin-raphael.aspx",
6 + "ImageUrl": "/PublishingImages/Calendrier-des-activites/bibliotheque/biblio-ados-kevin-raphael-335x215.jpg",
7 + "ActivityInscriptionLabel": "Inscription en ligne",
8 + "ActivityInscriptionUrl": null,
9 + "EventDate": "2025-02-13T23:30:00",
10 + "EndDate": "2025-02-14T00:30:00",
11 + "Clienteles": [
12 + {
13 + "Id": "0272c940-f2e7-41db-9eb1-549fd4dbe222",
14 + "Label": "Adolescents",
15 + "TargetUrl": null,
16 + "CustomProperties": null
17 + }
18 + ],
19 + "Categories": [
20 + {
21 + "Id": "52ff792f-a72e-4b2b-859e-2b9faf17491a",
22 + "Label": "Bibliothèques",
23 + "TargetUrl": null,
24 + "CustomProperties": null
25 + },
26 + {
27 + "Id": "ed4057c0-6826-43fb-9211-2ab94429c593",
28 + "Label": "Activités de la ville",
29 + "TargetUrl": null,
30 + "CustomProperties": null
31 + }
32 + ],
33 + "Subcategories": [
34 + {
35 + "Id": "e6e3e361-9c36-4595-9978-0c9cf9db4440",
36 + "Label": "Conférences",
37 + "TargetUrl": null,
38 + "CustomProperties": null
39 + }
40 + ],
41 + "Locations": [
42 + {
43 + "Id": "f4084c5d-aff7-4998-9e04-16a20732ad55",
44 + "Label": "Bibliothèque Gabrielle-Roy",
45 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-gabrielle-roy.aspx",
46 + "CustomProperties": null
47 + }
48 + ],
49 + "Tags": [
50 + {
51 + "Id": "eab9ad14-6874-4c9b-8704-3fd73027b0ea",
52 + "Label": "Fabreville",
53 + "TargetUrl": null,
54 + "CustomProperties": {
55 + "TagType": "Neighborhood"
56 + }
57 + },
58 + {
59 + "Id": "94191cf9-eee0-4e11-8300-3b066afdbb14",
60 + "Label": "Découvrir",
61 + "TargetUrl": null,
62 + "CustomProperties": null
63 + },
64 + {
65 + "Id": "c125b9cb-bd62-48c0-a347-84e8c7f8dca6",
66 + "Label": "Adultes",
67 + "TargetUrl": null,
68 + "CustomProperties": null
69 + }
70 + ],
71 + "Sites": [],
72 + "ActivityInscriptionUrl.Description": null,
73 + "ActivityInscriptionUrl.Url": null
74 + },
75 + {
76 + "Title": "Club de robotique",
77 + "Excerpt": "<p>Tu veux comprendre comment programmer un robot? Lors des ateliers de ce club techno, tu découvriras différents types de robots (LEGO, Ozobot et Sphero) et tu apprendras comment ils fonctionnent. Viens t’amuser en explorant le monde de la robotique!<br></p>",
78 + "PageUrl": "/Pages/Fr/Calendrier/biblio-jeunesse-club-robotique.aspx",
79 + "ImageUrl": "/PublishingImages/Calendrier-des-activites/bibliotheque/jeunesse-club-robotique-335x215.jpg",
80 + "ActivityInscriptionLabel": "Inscription en ligne",
81 + "ActivityInscriptionUrl": null,
82 + "EventDate": "2025-02-13T23:30:00",
83 + "EndDate": "2025-02-14T01:00:00",
84 + "Clienteles": [
85 + {
86 + "Id": "019b445a-815c-49a2-8adc-2ceb9b48cd9a",
87 + "Label": "Familles",
88 + "TargetUrl": null,
89 + "CustomProperties": null
90 + }
91 + ],
92 + "Categories": [
93 + {
94 + "Id": "52ff792f-a72e-4b2b-859e-2b9faf17491a",
95 + "Label": "Bibliothèques",
96 + "TargetUrl": null,
97 + "CustomProperties": null
98 + },
99 + {
100 + "Id": "ed4057c0-6826-43fb-9211-2ab94429c593",
101 + "Label": "Activités de la ville",
102 + "TargetUrl": null,
103 + "CustomProperties": null
104 + }
105 + ],
106 + "Subcategories": [
107 + {
108 + "Id": "e6e3e361-9c36-4595-9978-0c9cf9db4440",
109 + "Label": "Conférences",
110 + "TargetUrl": null,
111 + "CustomProperties": null
112 + }
113 + ],
114 + "Locations": [
115 + {
116 + "Id": "98205b68-5745-4687-b461-2e214e81389d",
117 + "Label": "Bibliothèque Multiculturelle",
118 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-multiculturelle.aspx",
119 + "CustomProperties": null
120 + }
121 + ],
122 + "Tags": [
123 + {
124 + "Id": "bcc29691-ef6f-4449-9d03-656185b4d7f9",
125 + "Label": "Chomedey",
126 + "TargetUrl": null,
127 + "CustomProperties": {
128 + "TagType": "Neighborhood"
129 + }
130 + },
131 + {
132 + "Id": "94191cf9-eee0-4e11-8300-3b066afdbb14",
133 + "Label": "Découvrir",
134 + "TargetUrl": null,
135 + "CustomProperties": null
136 + },
137 + {
138 + "Id": "6f430cdc-7ce4-4770-aaae-9ddbfe1cd3b9",
139 + "Label": "Jeunesse",
140 + "TargetUrl": null,
141 + "CustomProperties": null
142 + },
143 + {
144 + "Id": "84db1b61-2f48-4879-9ca7-f5ffe4299e45",
145 + "Label": "Jouer",
146 + "TargetUrl": null,
147 + "CustomProperties": null
148 + },
149 + {
150 + "Id": "bde5907b-ecee-45d9-9051-bfc09e259843",
151 + "Label": "Techno",
152 + "TargetUrl": null,
153 + "CustomProperties": null
154 + }
155 + ],
156 + "Sites": [],
157 + "ActivityInscriptionUrl.Description": "Inscription en ligne",
158 + "ActivityInscriptionUrl.Url": "https://app.amilia.com/store/fr/ville-de-laval-inscriptions/api/Activity/Detail?activityId=kLG4pDO"
159 + },
160 + {
161 + "Title": "Parcours lavallois",
162 + "Excerpt": "<p>L’artiste lavalloise Jocelyne Thibault vous propose de créer votre propre capsule temporelle, dans une série d’ateliers où le dessin, l’impression, le collage et la reliure seront à l’honneur.&#160;Cette activité vous est présentée dans le cadre du 60<sup>e</sup>&#160;anniversaire de la Ville de Laval<br></p>",
163 + "PageUrl": "/Pages/Fr/Calendrier/biblio-arts-parcours-lavallois-12-ans-plus-60e.aspx",
164 + "ImageUrl": "/PublishingImages/Calendrier-des-activites/bibliotheque/histoire-arts-parcours-lavallois-335x215.jpg",
165 + "ActivityInscriptionLabel": "Inscription en ligne",
166 + "ActivityInscriptionUrl": null,
167 + "EventDate": "2025-02-13T23:30:00",
168 + "EndDate": "2025-02-14T01:45:00",
169 + "Clienteles": [
170 + {
171 + "Id": "0272c940-f2e7-41db-9eb1-549fd4dbe222",
172 + "Label": "Adolescents",
173 + "TargetUrl": null,
174 + "CustomProperties": null
175 + }
176 + ],
177 + "Categories": [
178 + {
179 + "Id": "52ff792f-a72e-4b2b-859e-2b9faf17491a",
180 + "Label": "Bibliothèques",
181 + "TargetUrl": null,
182 + "CustomProperties": null
183 + },
184 + {
185 + "Id": "ed4057c0-6826-43fb-9211-2ab94429c593",
186 + "Label": "Activités de la ville",
187 + "TargetUrl": null,
188 + "CustomProperties": null
189 + }
190 + ],
191 + "Subcategories": [
192 + {
193 + "Id": "e6e3e361-9c36-4595-9978-0c9cf9db4440",
194 + "Label": "Conférences",
195 + "TargetUrl": null,
196 + "CustomProperties": null
197 + }
198 + ],
199 + "Locations": [
200 + {
201 + "Id": "e38a50b9-3e67-4c0e-84ee-040293125a45",
202 + "Label": "Bibliothèque Germaine-Guèvremont",
203 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-germaine-guevremont.aspx",
204 + "CustomProperties": null
205 + },
206 + {
207 + "Id": "8620bd60-4ce9-48a8-b327-90a368764774",
208 + "Label": "Bibliothèque Philippe-Panneton",
209 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-philippe-panneton.aspx",
210 + "CustomProperties": null
211 + },
212 + {
213 + "Id": "4fe6717e-916d-4d5f-9c41-59f9f294b1c8",
214 + "Label": "Bibliothèque Yves-Thériault",
215 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-yves-theriault.aspx",
216 + "CustomProperties": null
217 + },
218 + {
219 + "Id": "4870618a-b491-4abd-947e-9e1f4888b897",
220 + "Label": "Bibliothèque Marius-Barbeau",
221 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-marius-barbeau.aspx",
222 + "CustomProperties": null
223 + },
224 + {
225 + "Id": "03e1dcf2-1201-4c36-8ba2-4fd7da85b80b",
226 + "Label": "Bibliothèque Laure-Conan",
227 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-laure-conan.aspx",
228 + "CustomProperties": null
229 + },
230 + {
231 + "Id": "f4084c5d-aff7-4998-9e04-16a20732ad55",
232 + "Label": "Bibliothèque Gabrielle-Roy",
233 + "TargetUrl": "/lieux/Pages/Fr/bibliotheque-gabrielle-roy.aspx",
234 + "CustomProperties": null
235 + }
236 + ],
237 + "Tags": [
238 + {
239 + "Id": "aa035589-cbcc-4e4c-a3f1-a01312b815fd",
240 + "Label": "60e Laval",
241 + "TargetUrl": null,
242 + "CustomProperties": null
243 + },
244 + {
245 + "Id": "120836d9-e77a-4c6b-a300-87071827c2a0",
246 + "Label": "Arts",
247 + "TargetUrl": null,
248 + "CustomProperties": null
249 + },
250 + {
251 + "Id": "94191cf9-eee0-4e11-8300-3b066afdbb14",
252 + "Label": "Découvrir",
253 + "TargetUrl": null,
254 + "CustomProperties": null
255 + },
256 + {
257 + "Id": "2a5da81d-9c3c-4d7f-822d-d4593f2afc05",
258 + "Label": "Duvernay",
259 + "TargetUrl": null,
260 + "CustomProperties": {
261 + "TagType": "Neighborhood"
262 + }
263 + },
264 + {
265 + "Id": "ca32aae2-90a6-4767-b4d3-26ab89c40c4d",
266 + "Label": "Laval-Ouest",
267 + "TargetUrl": null,
268 + "CustomProperties": {
269 + "TagType": "Neighborhood"
270 + }
271 + },
272 + {
273 + "Id": "e397bf4b-aeed-48cc-aa70-66c277727360",
274 + "Label": "Sainte-Dorothée",
275 + "TargetUrl": null,
276 + "CustomProperties": {
277 + "TagType": "Neighborhood"
278 + }
279 + },
280 + {
281 + "Id": "5dadd5e3-e3d3-4881-9d4c-ed6f1cb664cc",
282 + "Label": "Auteuil",
283 + "TargetUrl": null,
284 + "CustomProperties": {
285 + "TagType": "Neighborhood"
286 + }
287 + },
288 + {
289 + "Id": "eab9ad14-6874-4c9b-8704-3fd73027b0ea",
290 + "Label": "Fabreville",
291 + "TargetUrl": null,
292 + "CustomProperties": {
293 + "TagType": "Neighborhood"
294 + }
295 + },
296 + {
297 + "Id": "fe639097-75cf-4e8d-8733-ecb9b808d2eb",
298 + "Label": "Vimont",
299 + "TargetUrl": null,
300 + "CustomProperties": {
301 + "TagType": "Neighborhood"
302 + }
303 + },
304 + {
305 + "Id": "cb5f24bc-8b43-4afe-9233-33960a97a039",
306 + "Label": "Saint-François",
307 + "TargetUrl": null,
308 + "CustomProperties": {
309 + "TagType": "Neighborhood"
310 + }
311 + }
312 + ],
313 + "Sites": [],
314 + "ActivityInscriptionUrl.Description": null,
315 + "ActivityInscriptionUrl.Url": null
316 + },
317 + {
318 + "Title": "Théâtre- L'Usine de théâtre potentiel",
319 + "Excerpt": "<p>​​Théâtre d'improvisation&#160;<br>Théâtre de la LNI<br></p>",
320 + "PageUrl": "/Pages/Fr/Calendrier/mda-theatre-usine-theatre-potentiel.aspx",
321 + "ImageUrl": "/PublishingImages/Calendrier-des-activites/maison-des-arts/2024/grand-public-usine-theatre-potentiel-335x215.jpg",
322 + "ActivityInscriptionLabel": null,
323 + "ActivityInscriptionUrl": null,
324 + "EventDate": "2025-02-14T00:30:00",
325 + "EndDate": "2025-02-14T02:00:00",
326 + "Clienteles": [],
327 + "Categories": [
328 + {
329 + "Id": "ed4057c0-6826-43fb-9211-2ab94429c593",
330 + "Label": "Activités de la ville",
331 + "TargetUrl": null,
332 + "CustomProperties": null
333 + },
334 + {
335 + "Id": "03e3e7d7-ad9a-4f10-86db-dfc9d49b7abb",
336 + "Label": "Expositions et spectacles",
337 + "TargetUrl": null,
338 + "CustomProperties": null
339 + }
340 + ],
341 + "Subcategories": [
342 + {
343 + "Id": "07155b54-9250-485c-a1cb-2125f095fa1d",
344 + "Label": "Maison des arts",
345 + "TargetUrl": null,
346 + "CustomProperties": null
347 + }
348 + ],
349 + "Locations": [
350 + {
351 + "Id": "b881c7a8-00ac-40fc-81d4-296229eb64d6",
352 + "Label": "Maison des arts de Laval",
353 + "TargetUrl": "/lieux/Pages/Fr/maison-des-arts-de-laval.aspx",
354 + "CustomProperties": null
355 + }
356 + ],
357 + "Tags": [
358 + {
359 + "Id": "71245944-c666-484c-9885-16598009f4bf",
360 + "Label": "Arts de la scène",
361 + "TargetUrl": null,
362 + "CustomProperties": null
363 + },
364 + {
365 + "Id": "bc509603-3c51-46fc-a6a9-b54f2cc27617",
366 + "Label": "Laval-des-Rapides",
367 + "TargetUrl": null,
368 + "CustomProperties": {
369 + "TagType": "Neighborhood"
370 + }
371 + },
372 + {
373 + "Id": "3d20d9e1-f593-47b8-8993-c95c977416f3",
374 + "Label": "Théâtre",
375 + "TargetUrl": null,
376 + "CustomProperties": null
377 + }
378 + ],
379 + "Sites": [
380 + {
381 + "Id": "77a2c5d4-25f3-485a-9443-4c9733f424e5",
382 + "Label": "Maison des arts de Laval",
383 + "TargetUrl": null,
384 + "CustomProperties": null
385 + },
386 + {
387 + "Id": "bdbc09a4-794f-4387-9a07-e4bb2c1aeb25",
388 + "Label": "Site Internet",
389 + "TargetUrl": null,
390 + "CustomProperties": null
391 + }
392 + ],
393 + "ActivityInscriptionUrl.Description": null,
394 + "ActivityInscriptionUrl.Url": null
395 + },
396 + {
397 + "Title": "Assemblée publique de consultation – 29 février 2024",
398 + "Excerpt": "<p>La Ville de Laval a tenu une assemblée de consultation publique concernant le projet de règlement&#160;S.A.D.R.-1.6 et le projet de règlement&#160;CDU-1-8. À la suite de cette assemblée, du 1<sup>er </sup>au 15&#160;mars&#160;2024, il était&#160;possible de transmettre vos commentaires par écrit.</p>",
399 + "PageUrl": "/Pages/Fr/Calendrier/assemblee-consultation-publique-sadr-1-6-cdu-1-8.aspx",
400 + "ImageUrl": "/PublishingImages/Calendrier-des-activites/vue-aerienne-secteur-bois-equerre-314x202.jpg",
401 + "ActivityInscriptionLabel": null,
402 + "ActivityInscriptionUrl": null,
403 + "EventDate": "2024-02-29T05:00:00",
404 + "EndDate": "2025-03-01T04:59:00",
405 + "Clienteles": [],
406 + "Categories": [
407 + {
408 + "Id": "ed4057c0-6826-43fb-9211-2ab94429c593",
409 + "Label": "Activités de la ville",
410 + "TargetUrl": null,
411 + "CustomProperties": null
412 + }
413 + ],
414 + "Subcategories": [
415 + {
416 + "Id": "0337a676-5883-4e45-8fd8-c3d4b540c9d3",
417 + "Label": "Consultations publiques et assemblées",
418 + "TargetUrl": null,
419 + "CustomProperties": null
420 + }
421 + ],
422 + "Locations": [
423 + {
424 + "Id": "c62843eb-d9d1-440a-9c38-632da61f7ff3",
425 + "Label": "Hôtel de ville",
426 + "TargetUrl": "/lieux/Pages/Fr/hotel-de-ville.aspx",
427 + "CustomProperties": null
428 + }
429 + ],
430 + "Tags": [
431 + {
432 + "Id": "098da71f-1e43-4f5e-b601-9fd281a8da0a",
433 + "Label": "Archives des consultations publiques",
434 + "TargetUrl": null,
435 + "CustomProperties": null
436 + }
437 + ],
438 + "Sites": [
439 + {
440 + "Id": "bdbc09a4-794f-4387-9a07-e4bb2c1aeb25",
441 + "Label": "Site Internet",
442 + "TargetUrl": null,
443 + "CustomProperties": null
444 + }
445 + ],
446 + "ActivityInscriptionUrl.Description": null,
447 + "ActivityInscriptionUrl.Url": null
448 + }
449 +]
\ No newline at end of file
added tests/fixtures/lavitrine_fiche.html +736 −0
@@ -0,0 +1,736 @@
1 +<!DOCTYPE html><!-- This site was created in Webflow. https://webflow.com --><!-- Last Published: Mon Mar 23 2026 13:04:44 GMT+0000 (Coordinated Universal Time) --><html data-wf-domain="la-vitrine-prod-v3-7-0.webflow.io" data-wf-page="6894a11aea4bc1fdc212e030" data-wf-site="6894a11aea4bc1fdc212dfdb" data-wf-status="1" lang="fr"><head><meta charset="utf-8"><link href="https://cdn.prod.website-files.com" rel="preconnect" crossorigin="anonymous"><title>100 ans de chansons françaises avec Richard Charest | La Vitrine</title><meta content="DE CHARLES TRENET À STROMAE, UN SIÈCLE DE MÉLODIES IMMORTELLES !" name="description"><meta content="100 ans de chansons françaises avec Richard Charest | La Vitrine" property="og:title"><meta content="DE CHARLES TRENET À STROMAE, UN SIÈCLE DE MÉLODIES IMMORTELLES !" property="og:description"><meta content="Evenement | La Vitrine" name="twitter:title"><meta content="La Vitrine propose le calendrier des événements culturels le plus complet du Grand Montréal." name="twitter:description"><meta property="og:type" content="website"><meta content="summary_large_image" name="twitter:card"><meta content="width=device-width, initial-scale=1" name="viewport"><meta content="Webflow" name="generator"><link href="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/css/la-vitrine-prod-v3-7-0.webflow.shared.55c60f6ae.min.css" rel="stylesheet" type="text/css" integrity="sha384-VcYPau7+VMLkLpAO+IS23NyRq74yXG9/m/J7ECEnU/QhGqbbo1hrBNlQ/6lzj2V9" crossorigin="anonymous"><script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script><link href="https://cdn.prod.website-files.com/653818de55f553b36a003d7f/654a8bc145f4acf47b8e1d4a_favicon_32x32.png" rel="shortcut icon" type="image/x-icon"><link href="https://cdn.prod.website-files.com/653818de55f553b36a003d7f/654a8bc4c0994bdcc2977d16_favicon_mobile-1.png" rel="apple-touch-icon"><meta name="impact-site-verification" value="5de8898a-c07e-43ec-b953-8cbe07bdf1cd">
2 +<script src="https://lvc-prod.web.app/index.js"></script>
3 +
4 +<!-- GOOGLE TAG MANAGER -->
5 +<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-KXWMJC75');
6 +</script>
7 +
8 +<!-- Facebook Pixel Base Code -->
9 +<script>
10 + !function(f,b,e,v,n,t,s)
11 + {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
12 + n.callMethod.apply(n,arguments):n.queue.push(arguments)};
13 + if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
14 + n.queue=[];t=b.createElement(e);t.async=!0;
15 + t.src='https://connect.facebook.net/en_US/fbevents.js';
16 + s=b.getElementsByTagName(e)[0];
17 + s.parentNode.insertBefore(t,s)}(window, document,'script');
18 + fbq('init', '3019568851519496');
19 + fbq('track', 'PageView');
20 +</script>
21 +<noscript>
22 + </noscript><meta property="twitter:title" content="100 ans de chansons françaises avec Richard Charest | La Vitrine"><meta property="twitter:description" content="DE CHARLES TRENET À STROMAE, UN SIÈCLE DE MÉLODIES IMMORTELLES !"><meta property="og:image" content="https://lvc-medias.lavitrine.com/acf4025b-3502-5217-b26d-f3bd5a35c6dc.webp"><meta property="twitter:image" content="https://lvc-medias.lavitrine.com/acf4025b-3502-5217-b26d-f3bd5a35c6dc.webp"></head><body class="body_background-lightblue"><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=3019568851519496&amp;ev=PageView&amp;noscript=1">
23 +
24 +
25 +
26 +<!-- HIDE WEBFLOW BADGE -->
27 +<style>
28 + .w-webflow-badge {
29 + display: none !important;
30 + }
31 +</style>
32 +<style>
33 + .mobile-lvc-banner-wrapper {
34 + position: relative;
35 + transform: translateY(-100%);
36 + transition: 0.5s;
37 + }
38 + .mobile-lvc-banner-wrapper.animate {
39 + transform: translateY(0%);
40 +}
41 +</style>
42 +<style>
43 + .axeptio_mount {
44 + z-index: 9999 !important;
45 + }
46 + ._hj-YR-2H__Feedback__container {
47 + z-index: 1000 !important;
48 + }
49 +</style>
50 +<!-- PAGE INFO -->
51 +<link rel="canonical" href="https://www.lavitrine.com/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777">
52 +<link rel="alternative" hreflang="fr" href="https://www.lavitrine.com/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777">
53 +<link rel="alternative" hreflang="en" href="https://www.lavitrine.com/en/event/100-ans-de-chansons-francaises-avec-richard-charest/23777">
54 +<link rel="alternative" hreflang="x-default" href="https://www.lavitrine.com/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777">
55 +<meta>
56 +<meta>
57 +<meta>
58 +<meta property="og:url" content="/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777">
59 +<meta property="twitter:url" content="/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777">
60 +
61 +<!-- JSON-LD -->
62 +
63 +
64 +<!-- SWIPER.JS CSS-->
65 +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css">
66 +
67 +<!-- STYLES OVERWRITES -->
68 +<style>
69 +.lvc_header_content-wrapper {
70 + padding-top: 20px !important;
71 +}
72 +.lvc_header_title-wrapper {
73 + max-height: none !important;
74 + overflow: visible !important;
75 +}
76 +
77 +.lvc_header_title {
78 + font-size: 40px !important;
79 +}
80 +</style><div data-page-template="event" class="page-wrapper"><div class="background-gradient"></div><div class="global-styles w-embed"><style>
81 +/* Make text look crisper and more legible in all browsers */
82 +body {
83 + -webkit-font-smoothing: antialiased;
84 + -moz-osx-font-smoothing: grayscale;
85 + font-smoothing: antialiased;
86 + text-rendering: optimizeLegibility;
87 +}
88 +
89 +/* Focus state style for keyboard navigation for the focusable elements */
90 +*[tabindex]:focus-visible,
91 +input[type='file']:focus-visible {
92 + outline: 0.125rem solid #4d65ff;
93 + outline-offset: 0.125rem;
94 +}
95 +
96 +/* Get rid of top margin on first element in any rich text element */
97 +.w-richtext > :not(div):first-child,
98 +.w-richtext > div:first-child > :first-child {
99 + margin-top: 0 !important;
100 +}
101 +
102 +/* Get rid of bottom margin on last element in any rich text element */
103 +.w-richtext > :last-child,
104 +.w-richtext ol li:last-child,
105 +.w-richtext ul li:last-child {
106 + margin-bottom: 0 !important;
107 +}
108 +
109 +/* Prevent all click and hover interaction with an element */
110 +.pointer-events-off {
111 + pointer-events: none;
112 +}
113 +
114 +/* Enables all click and hover interaction with an element */
115 +.pointer-events-on {
116 + pointer-events: auto;
117 +}
118 +
119 +/* Create a class of .div-square which maintains a 1:1 dimension of a div */
120 +.div-square::after {
121 + content: '';
122 + display: block;
123 + padding-bottom: 100%;
124 +}
125 +
126 +/* Make sure containers never lose their center alignment */
127 +.container-medium,
128 +.container-small,
129 +.container-large {
130 + margin-right: auto !important;
131 + margin-left: auto !important;
132 +}
133 +
134 +/*
135 +Make the following elements inherit typography styles from the parent and not have hardcoded values.
136 +Important: You will not be able to style for example "All Links" in Designer with this CSS applied.
137 +Uncomment this CSS to use it in the project. Leave this message for future hand-off.
138 +*/
139 +/*
140 +a,
141 +.w-input,
142 +.w-select,
143 +.w-tab-link,
144 +.w-nav-link,
145 +.w-dropdown-btn,
146 +.w-dropdown-toggle,
147 +.w-dropdown-link {
148 + color: inherit;
149 + text-decoration: inherit;
150 + font-size: inherit;
151 +}
152 +*/
153 +
154 +/* Apply "..." after 3 lines of text */
155 +.text-style-3lines {
156 + display: -webkit-box;
157 + overflow: hidden;
158 + -webkit-line-clamp: 3;
159 + -webkit-box-orient: vertical;
160 +}
161 +
162 +/* Apply "..." after 2 lines of text */
163 +.text-style-2lines {
164 + display: -webkit-box;
165 + overflow: hidden;
166 + -webkit-line-clamp: 2;
167 + -webkit-box-orient: vertical;
168 +}
169 +.text-style-1line {
170 + display: -webkit-box;
171 + overflow: hidden;
172 + -webkit-line-clamp: 1;
173 + -webkit-box-orient: vertical;
174 +}
175 +
176 +/* Adds inline flex display */
177 +.display-inlineflex {
178 + display: inline-flex;
179 +}
180 +
181 +/* These classes are never overwritten */
182 +.hide {
183 + display: none !important;
184 +}
185 +
186 +@media screen and (max-width: 991px) {
187 + .hide,
188 + .hide-tablet {
189 + display: none !important;
190 + }
191 +}
192 +@media screen and (max-width: 767px) {
193 + .hide-mobile-landscape {
194 + display: none !important;
195 + }
196 +}
197 +@media screen and (max-width: 479px) {
198 + .hide-mobile {
199 + display: none !important;
200 + }
201 +}
202 +
203 +.margin-0 {
204 + margin: 0rem !important;
205 +}
206 +
207 +.padding-0 {
208 + padding: 0rem !important;
209 +}
210 +
211 +.spacing-clean {
212 + padding: 0rem !important;
213 + margin: 0rem !important;
214 +}
215 +
216 +.margin-top {
217 + margin-right: 0rem !important;
218 + margin-bottom: 0rem !important;
219 + margin-left: 0rem !important;
220 +}
221 +
222 +.padding-top {
223 + padding-right: 0rem !important;
224 + padding-bottom: 0rem !important;
225 + padding-left: 0rem !important;
226 +}
227 +
228 +.margin-right {
229 + margin-top: 0rem !important;
230 + margin-bottom: 0rem !important;
231 + margin-left: 0rem !important;
232 +}
233 +
234 +.padding-right {
235 + padding-top: 0rem !important;
236 + padding-bottom: 0rem !important;
237 + padding-left: 0rem !important;
238 +}
239 +
240 +.margin-bottom {
241 + margin-top: 0rem !important;
242 + margin-right: 0rem !important;
243 + margin-left: 0rem !important;
244 +}
245 +
246 +.padding-bottom {
247 + padding-top: 0rem !important;
248 + padding-right: 0rem !important;
249 + padding-left: 0rem !important;
250 +}
251 +
252 +.margin-left {
253 + margin-top: 0rem !important;
254 + margin-right: 0rem !important;
255 + margin-bottom: 0rem !important;
256 +}
257 +
258 +.padding-left {
259 + padding-top: 0rem !important;
260 + padding-right: 0rem !important;
261 + padding-bottom: 0rem !important;
262 +}
263 +
264 +.margin-horizontal {
265 + margin-top: 0rem !important;
266 + margin-bottom: 0rem !important;
267 +}
268 +
269 +.padding-horizontal {
270 + padding-top: 0rem !important;
271 + padding-bottom: 0rem !important;
272 +}
273 +
274 +.margin-vertical {
275 + margin-right: 0rem !important;
276 + margin-left: 0rem !important;
277 +}
278 +
279 +.padding-vertical {
280 + padding-right: 0rem !important;
281 + padding-left: 0rem !important;
282 +}
283 +
284 +.prevent-select {
285 + -webkit-user-select: none; /* Safari */
286 + -ms-user-select: none; /* IE 10 and IE 11 */
287 + user-select: none; /* Standard syntax */
288 +}
289 +
290 +#form-submit {
291 + pointer-events: none;
292 +}
293 +
294 +.nav_link[data-active-link='true'] {
295 + background-color: var(--active-link);
296 +}
297 +
298 +#lvc-search-trigger[data-active-link='true'] {
299 + background-color: var(--active-link);
300 + pointer-events: none;
301 +}
302 +
303 +.nav_menu_link[data-bg-color='teal'] {
304 + background-image: linear-gradient(327deg, var(--turquoise), #8da7fc 80%, var(--purple));
305 +}
306 +
307 +.nav_menu_link[data-bg-color='green'] {
308 + background-image: linear-gradient(274deg, var(--turquoise), #adeee4);
309 +}
310 +
311 +.nav_menu_link[data-bg-color='lightgreen'] {
312 + background-image: linear-gradient(225deg, #8ce0d4, #fff);
313 +}
314 +
315 +.nav_menu_link[data-bg-color='orange'] {
316 + background-image: linear-gradient(103deg, var(--light-pink), var(--peach-puff) 100%, white);
317 +}
318 +
319 +.nav_menu_link[data-bg-color='blue'] {
320 + background-image: linear-gradient(61deg, #85a1ff, var(--purple) 29%, var(--light-sky-blue));
321 +}
322 +
323 +.footer_policy-text-link:last-of-type {
324 + border: none;
325 +}
326 +
327 +.lvc-breadcrumb_component[data-is-account='true'][data-width='small'] {
328 + top: 11rem;
329 + max-width: 650px;
330 +}
331 +
332 +.lvc-breadcrumb_component[data-is-account='true'][data-width='large'] {
333 + max-width: 1142px;
334 +}
335 +
336 +.lvc-breadcrumb_item:not(:last-child)::after {
337 + content: url(https://cdn.prod.website-files.com/653818de55f553b36a003d7f/653ea55b718cbd5c5186301a_Chevron.svg);
338 + margin-left: 4px;
339 + margin-top: 3px;
340 +}
341 +
342 +.event_header_artist::after {
343 + content: ', ';
344 +}
345 +
346 +.event_header_artist:last-of-type::after {
347 + content: '';
348 +}
349 +
350 +.swiper {
351 + z-index: 0 !important;
352 +}
353 +
354 +.swiper-scrollbar-horizontal {
355 + width: 418px !important;
356 + margin: auto !important;
357 + position: static !important;
358 + height: 5px !important;
359 + background-color: #ffffff !important;
360 + bottom: 0 !important;
361 + border-radius: 99px !important;
362 +}
363 +
364 +.swiper-scrollbar-drag {
365 + background-color: #1e007d !important;
366 + height: 5px !important;
367 +}
368 +
369 +.event_calendar_swiper-scrollbar {
370 + margin-top: 3.75rem !important;
371 +}
372 +
373 +.components_content_button_button-wrapper[data-align='left'] {
374 + justify-content: start;
375 +}
376 +
377 +.components_content_button_button-wrapper[data-align='right'] {
378 + justify-content: end;
379 +}
380 +.event_info_button-layout[data-event-status='Postponed'],
381 +.event_info_button-layout[data-event-status='Cancelled'],
382 +.event_info-bar_button-wrapper[data-event-status='Postponed'],
383 +.event_info-bar_button-wrapper[data-event-status='Cancelled'],
384 +.webcomponent_lvc-share[data-event-status='Postponed'],
385 +.webcomponent_lvc-share[data-event-status='Cancelled'] {
386 + pointer-events: none;
387 + opacity: 0.5;
388 +}
389 +#eventDate[data-event-status='Postponed'],
390 +#eventDate[data-event-status='Cancelled'],
391 +#eventTime[data-event-status='Postponed'],
392 +#eventTime[data-event-status='Cancelled'],
393 +.event_info-bar_info-item-text[data-event-status='Postponed'],
394 +.event_info-bar_info-item-text[data-event-status='Cancelled'] {
395 + text-decoration: line-through;
396 +}
397 +
398 +.lvc_event_title-alert[data-event-status='Postponed'],
399 +.lvc_event_title-alert[data-event-status='Cancelled'],
400 +.lvc_event_image-alert[data-event-status='Postponed'],
401 +.lvc_event_image-alert[data-event-status='Cancelled'],
402 +.lvc_event_date-icon-alert[data-event-status='Postponed'],
403 +.lvc_event_date-icon-alert[data-event-status='Cancelled'],
404 +.event_calendar_slide-status-flag[data-event-status='Postponed'],
405 +.event_calendar_slide-status-flag[data-event-status='Cancelled'],
406 +.event_info_bar_status-warning[data-event-status='Postponed'],
407 +.event_info_bar_status-warning[data-event-status='Cancelled'] {
408 + background-color: #ff4600;
409 + color: #ffffff;
410 +}
411 +
412 +.lvc-breadcrumb_inner-wrapper > .lvc-breadcrumb_item ~ .lvc-breadcrumb_item > .lvc-breadcrumb_home-icon {
413 + display: none;
414 +}
415 +.lvc-button-large {
416 + background: linear-gradient(100deg, #FFFFFF 5%, #BFF0FF 148.15%);
417 + background-size: 250%;
418 + transition: background 0.2s ease;
419 +}
420 +.lvc-button-large.is-blue {
421 + background: #1e007d;
422 +}
423 +.lvc-button-large:hover {
424 + background-size: 100%;
425 +}
426 +.lvc-button-large.isnoclick:hover {
427 + background-size: 250%;
428 +}
429 +.lvc-button-large.is-website-link {
430 + background: transparent;
431 + transition: box-shadow 0.2s ease;
432 +}
433 +.lvc-button-large.is-website-link:hover {
434 + box-shadow: 0 4px 4px 0 #00000040,0 14px 20px 0 #1e007d2e;
435 +}
436 +
437 +@media only screen and (max-width: 991px) {
438 + .lvc-breadcrumb_component[data-is-account='true'] {
439 + padding-left: 1rem;
440 + padding-right: 1rem;
441 + top: 7rem;
442 + }
443 +
444 + .lvc-breadcrumb_component[data-is-account='true'][data-width='large'] {
445 + top: 7rem;
446 + max-width: 580px;
447 + }
448 +
449 + .lvc-breadcrumb_component[data-is-account='true'][data-width='small'] {
450 + top: 7rem;
451 + max-width: 573px;
452 + }
453 +}
454 +
455 +@media (max-width: 568px) {
456 + .swiper-scrollbar-horizontal {
457 + width: 100% !important;
458 + }
459 +
460 + .event_calendar_swiper-scrollbar {
461 + margin-top: 1.25rem !important;
462 + }
463 + .components_content_text_rich-text img{
464 + float: none!important;
465 + max-width: 100%!important;
466 + margin: 0!important;
467 + margin-bottom: 1rem!important;
468 + }
469 +}
470 +
471 +.landing-page_section_header + .components_content > .components_content_components-wrapper:first-child .components_content_text_component .padding-vertical.padding-content-components {
472 + padding-top: 0 !important;
473 +}
474 +.components_content_text_rich-text figcaption {
475 + font-size: 10px;
476 + color: #4868b3;
477 + text-align: right;
478 + font-weight: 400;
479 +}
480 +.event_info_button-layout[data-event-status='Cancelled'],
481 +.event_info-bar_button-wrapper[data-event-status='Cancelled'],
482 +.webcomponent_lvc-share[data-event-status='Cancelled'] {
483 + display: none;
484 +}
485 +</style></div><div id="mobile-banner" class="mobile-lvc-banner-wrapper"><lvc-banner></lvc-banner></div><div data-animation="default" data-collapse="all" data-duration="400" data-easing="ease" data-easing2="ease" role="banner" class="nav_component w-nav"><div class="padding-horizontal padding-medium"><div class="nav_layout"><div class="nav_left-content"><a href="/" class="nav_logo-wrapper w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212dfff_logo.svg" loading="lazy" alt="" class="nav_logo"></a><lvc-search-menu-button></lvc-search-menu-button></div><div class="nav_right-content"><lvc-auth-wrapper class="nav_account-link_component"><div data-hover="false" data-delay="0" slot="unauth" class="nav_account-link_dropdown w-dropdown"><div class="nav_account-link_dropdown-toggle w-dropdown-toggle"><div data-gtm-trigger-location="menu_principal" data-gtm-trigger-id-0="compte" class="nav_link"><img loading="lazy" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e053_lvc-account-icon.svg" alt="" class="nav_link-icon is-account-menu"><div class="nav_account-link_dropdown-text">Compte</div></div></div><nav class="nav_account-link_menu_component w-dropdown-list"><a href="/fr/connexion" class="nav_account-link_menu_link w-dropdown-link">Connexion</a><a href="/fr/s-inscrire" class="nav_account-link_menu_link w-dropdown-link">Créer un compte</a></nav></div><div data-hover="false" data-delay="0" slot="auth" class="nav_account-link_dropdown w-dropdown"><div class="nav_account-link_dropdown-toggle w-dropdown-toggle"><div class="nav_account_link"><div data-active-link="false" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_principal" class="nav_link"><img loading="lazy" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e067_lvc-account-connected-icon.svg" alt="" class="nav_link-icon is-account-menu"><div class="nav_account-link_dropdown-text">Compte</div></div></div></div><nav class="nav_account-link_menu_component w-dropdown-list"><a href="/fr/compte" class="nav_account-link_menu_name-text w-inline-block"><lvc-auth-templated-text>{{{firstName}}} {{{lastName}}}</lvc-auth-templated-text></a><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_mon_profil" href="/fr/compte/profil-culturel" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e10d_Ticket.svg" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Profil culturel</div></a><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/compte/favoris" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e256_Heart.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Favoris</div></a><lvc-auth-wrapper><a slot="partner" href="https://pro.lavitrine.com" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e184_lvc-espace-pro.svg" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Compte professionnel</div></a></lvc-auth-wrapper><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_notifications" href="/fr/compte/notifications" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e1fa_Notification.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Notifications</div></a><a href="/fr/compte/info-personnelles" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e26e_Profil.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Infos personnelles</div></a><a href="/fr/compte/connexion-et-securite" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e250_Security.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Connexion et sécurité</div></a><div class="nav_account-link_menu_divider"></div><div id="account-menu-logout" data-auth-disconnect="true" class="nav_account-link_menu_disconnect">Déconnexion</div></nav></div></lvc-auth-wrapper><div class="nav_menu-button w-nav-button"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f5_lvc-burger-menu.svg" loading="lazy" alt="" class="nav_menu-button-icon"></div></div><nav role="navigation" class="nav_menu_component w-nav-menu"><div class="nav_menu_layout"><div class="nav_menu_header"><a href="/" class="nav_menu_logo-wrapper w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0c5_logo%20(1).svg" loading="lazy" alt="" class="nav_menu_logo"></a><a href="#" class="nav_menu_close-button w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03c_lvc-nav-menu-close.svg" loading="lazy" alt="" class="nav_menu_close-icon"><div class="nav_menu_close-text">Menu</div></a></div><div class="nav_menu_link-layout"><div class="nav_menu_link-wrapper"><a class="nav_menu_link w-inline-block" href="/fr/recherche" data-gtm-trigger-id-0="rechercher" data-gtm-trigger-location="menu_hamburger" data-bg-color="teal"><img class="nav_menu_link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/384b0e5c-ce44-43cd-b74b-6c3189ac2ad1/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/384b0e5c-ce44-43cd-b74b-6c3189ac2ad1/?null"><div class="nav_menu_link-text">Rechercher</div></a><a class="nav_menu_link w-inline-block" href="/fr/blogue" data-gtm-trigger-id-0="blogue" data-gtm-trigger-location="menu_hamburger" data-bg-color="orange"><div class="nav_menu_link-text">Blogue</div></a></div><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div slot="auth" class="nav_menu_auth-content"><a data-gtm-trigger-location="menu_hamburger" data-bg-color="green" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/compte/favoris" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0df_lvc-heart.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mes favoris</div></a></div><div slot="unauth" class="nav_menu_unauth-content"><a data-gtm-trigger-location="menu_hamburger" data-bg-color="green" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/connexion" class="nav_menu_link green w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0df_lvc-heart.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mes favoris</div></a></div></lvc-auth-wrapper><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div slot="auth" class="nav_menu_auth-content"><a data-bg-color="lightgreen" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_hamburger" href="/fr/compte" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e067_lvc-account-connected-icon.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mon compte</div></a></div><div slot="unauth" class="nav_menu_unauth-content"><a data-bg-color="lightgreen" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_hamburger" href="/fr/s-inscrire" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e117_Group%2040000.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Créer un compte</div></a></div></lvc-auth-wrapper><div class="spacer-small"></div><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div data-auth-disconnect="true" slot="auth" class="nav_menu_disconnect-button">Déconnexion</div><a slot="unauth" href="/fr/connexion" class="nav_menu_disconnect-button">Connexion</a></lvc-auth-wrapper></div><div class="nav_menu_footer"><div class="nav_menu_footer_column_wrapper"><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">L'organisation</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="nav_menu_footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="nav_menu_footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="nav_menu_footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="nav_menu_footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Médias</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Aide</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="nav_menu_footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Idées de sorties</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Espace Pro</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="nav_menu_footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="nav_menu_footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="nav_menu_footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div><div class="nav_menu_footer_right"><div class="nav_menu_footer_language-button-wrapper"><a href="#" class="nav_menu_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f3_lvc-nav-menu-lang-fr.svg" loading="lazy" alt="" class="nav_menu_language-switch-icon"></a></div><div class="nav_menu_footer_social-link-wrapper"><a href="https://www.facebook.com/vitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null"></a><a href="https://twitter.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null"></a><a href="https://www.instagram.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null"></a><a href="https://www.youtube.com/user/Lavitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null"></a></div></div></div><div class="nav_menu_footer-mobile"><div class="nav_menu_footer_column_wrapper"><div class="fs_accordion-2_embed w-embed w-script"><!-- [Finsweet Attributes] Accordion -->
486 +<script>(()=>{var t="https://cdn.jsdelivr.net/npm/@finsweet/attributes-accordion@1/accordion.js",e=document.querySelector(`script[src="${t}"]`);e||(e=document.createElement("script"),e.async=!0,e.src=t,document.head.append(e));})();</script></div><div fs-accordion-initial="none" fs-accordion-element="group" class="fs_accordion-2_component"><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">L'organisation</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="nav_menu_footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="nav_menu_footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="nav_menu_footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="nav_menu_footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Médias</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Aide</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="nav_menu_footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Idées de sorties</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Espace Pro</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="nav_menu_footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="nav_menu_footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="nav_menu_footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div></div></div></div><div class="nav_menu_footer_right"><div class="nav_menu_footer_language-button-wrapper"><a href="#" class="nav_menu_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f3_lvc-nav-menu-lang-fr.svg" loading="lazy" alt="" class="nav_menu_language-switch-icon"></a></div><div class="nav_menu_footer_social-link-wrapper"><a href="https://www.facebook.com/vitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null"></a><a href="https://twitter.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null"></a><a href="https://www.instagram.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null"></a><a href="https://www.youtube.com/user/Lavitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null"></a></div></div></div></div></nav></div></div><lvc-banner class="lvc-banner-desktop"></lvc-banner><lvc-search-menu class="lvc-search-menu"></lvc-search-menu></div><div data-animation="default" data-collapse="medium" data-duration="400" data-easing="ease" data-easing2="ease" role="banner" class="nav_mobile_component w-nav"><lvc-search-menu></lvc-search-menu><div class="nav_layout"><nav role="navigation" class="nav_menu_component w-nav-menu"><div class="nav_menu_layout"><div class="nav_menu_header"><a href="/" class="nav_menu_logo-wrapper w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0c5_logo%20(1).svg" loading="lazy" alt="" class="nav_menu_logo"></a><a href="#" class="nav_menu_close-button w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03c_lvc-nav-menu-close.svg" loading="lazy" alt="" class="nav_menu_close-icon"><div class="nav_menu_close-text">Menu</div></a></div><div class="nav_menu_link-layout"><div class="nav_menu_link-wrapper"><a class="nav_menu_link w-inline-block" href="/fr/recherche" data-gtm-trigger-id-0="rechercher" data-gtm-trigger-location="menu_hamburger" data-bg-color="teal"><img class="nav_menu_link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/384b0e5c-ce44-43cd-b74b-6c3189ac2ad1/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/384b0e5c-ce44-43cd-b74b-6c3189ac2ad1/?null"><div class="nav_menu_link-text">Rechercher</div></a><a class="nav_menu_link w-inline-block" href="/fr/blogue" data-gtm-trigger-id-0="blogue" data-gtm-trigger-location="menu_hamburger" data-bg-color="orange"><div class="nav_menu_link-text">Blogue</div></a></div><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div slot="auth" class="nav_menu_auth-content"><a data-gtm-trigger-location="menu_hamburger" data-bg-color="green" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/compte/favoris" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0df_lvc-heart.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mes favoris</div></a></div><div slot="unauth" class="nav_menu_unauth-content"><a data-gtm-trigger-location="menu_hamburger" data-bg-color="green" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/connexion" class="nav_menu_link green w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0df_lvc-heart.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mes favoris</div></a></div></lvc-auth-wrapper><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div slot="auth" class="nav_menu_auth-content"><a data-bg-color="lightgreen" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_hamburger" href="/fr/compte" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e067_lvc-account-connected-icon.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Mon compte</div></a></div><div slot="unauth" class="nav_menu_unauth-content"><a data-bg-color="lightgreen" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_hamburger" href="/fr/s-inscrire" class="nav_menu_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e117_Group%2040000.svg" loading="lazy" alt="" class="nav_menu_link-icon"><div class="nav_menu_link-text">Créer un compte</div></a></div></lvc-auth-wrapper><div class="spacer-small"></div><lvc-auth-wrapper class="nav_menu_auth-wrapper"><div data-auth-disconnect="true" slot="auth" class="nav_menu_disconnect-button">Déconnexion</div><a slot="unauth" href="/fr/connexion" class="nav_menu_disconnect-button">Connexion</a></lvc-auth-wrapper></div><div class="nav_menu_footer"><div class="nav_menu_footer_column_wrapper"><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">L'organisation</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="nav_menu_footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="nav_menu_footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="nav_menu_footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="nav_menu_footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Médias</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Aide</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="nav_menu_footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Idées de sorties</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div><div class="nav_menu_footer_column"><div class="nav_menu_footer_column-title">Espace Pro</div><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="nav_menu_footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="nav_menu_footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="nav_menu_footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div><div class="nav_menu_footer_right"><div class="nav_menu_footer_language-button-wrapper"><a href="#" class="nav_menu_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f3_lvc-nav-menu-lang-fr.svg" loading="lazy" alt="" class="nav_menu_language-switch-icon"></a></div><div class="nav_menu_footer_social-link-wrapper"><a href="https://www.facebook.com/vitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null"></a><a href="https://twitter.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null"></a><a href="https://www.instagram.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null"></a><a href="https://www.youtube.com/user/Lavitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null"></a></div></div></div><div class="nav_menu_footer-mobile"><div class="nav_menu_footer_column_wrapper"><div class="fs_accordion-2_embed w-embed w-script"><!-- [Finsweet Attributes] Accordion -->
487 +<script>(()=>{var t="https://cdn.jsdelivr.net/npm/@finsweet/attributes-accordion@1/accordion.js",e=document.querySelector(`script[src="${t}"]`);e||(e=document.createElement("script"),e.async=!0,e.src=t,document.head.append(e));})();</script></div><div fs-accordion-initial="none" fs-accordion-element="group" class="fs_accordion-2_component"><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">L'organisation</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="nav_menu_footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="nav_menu_footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="nav_menu_footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="nav_menu_footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Médias</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Aide</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="nav_menu_footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Idées de sorties</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="nav_menu_footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div></div></div><div class="nav_menu_footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header-2"><div class="nav_menu_footer_column-title">Espace Pro</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e03e_lvc-mobile-nav-dropdown.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="nav_menu_footer_text_link_wrapper"><a class="nav_menu_footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="nav_menu_footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="nav_menu_footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="nav_menu_footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div></div></div></div><div class="nav_menu_footer_right"><div class="nav_menu_footer_language-button-wrapper"><a href="#" class="nav_menu_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f3_lvc-nav-menu-lang-fr.svg" loading="lazy" alt="" class="nav_menu_language-switch-icon"></a></div><div class="nav_menu_footer_social-link-wrapper"><a href="https://www.facebook.com/vitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/12415aa5-d5e7-497a-8882-7a3c3dab5e63/?null"></a><a href="https://twitter.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/54ae3616-543a-4406-ba34-1ea4887096c2/?null"></a><a href="https://www.instagram.com/la_vitrine" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/8da2c084-421c-477c-95df-ecd8f08e1fe2/?null"></a><a href="https://www.youtube.com/user/Lavitrineculturelle" target="_blank" class="nav_menu_footer_social-link w-inline-block"><img class="nav_menu_footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null" alt="" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/f4264729-b4f1-481b-a9e2-531931d50c0f/?null"></a></div></div></div></div></nav><a href="/" class="nav_logo-wrapper w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212dfff_logo.svg" loading="lazy" width="86" height="55" alt="" class="nav_logo"></a><lvc-search-menu-button></lvc-search-menu-button><lvc-auth-wrapper class="nav_account-link_component"><div data-hover="false" data-delay="0" slot="unauth" class="nav_account-link_dropdown w-dropdown"><div class="nav_account-link_dropdown-toggle w-dropdown-toggle"><div data-gtm-trigger-location="menu_principal" data-gtm-trigger-id-0="compte" class="nav_link"><img loading="lazy" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e053_lvc-account-icon.svg" alt="" class="nav_link-icon is-account-menu"><div class="nav_account-link_dropdown-text">Compte</div></div></div><nav class="nav_account-link_menu_component w-dropdown-list"><a href="/fr/connexion" class="nav_account-link_menu_link w-dropdown-link">Connexion</a><a href="/fr/s-inscrire" class="nav_account-link_menu_link w-dropdown-link">Créer un compte</a></nav></div><div data-hover="false" data-delay="0" slot="auth" class="nav_account-link_dropdown w-dropdown"><div class="nav_account-link_dropdown-toggle w-dropdown-toggle"><div class="nav_account_link"><div data-active-link="false" data-gtm-trigger-id-0="compte" data-gtm-trigger-location="menu_principal" class="nav_link"><img loading="lazy" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e067_lvc-account-connected-icon.svg" alt="" class="nav_link-icon is-account-menu"><div class="nav_account-link_dropdown-text">Compte</div></div></div></div><nav class="nav_account-link_menu_component w-dropdown-list"><a href="/fr/compte" class="nav_account-link_menu_name-text w-inline-block"><lvc-auth-templated-text>{{{firstName}}} {{{lastName}}}</lvc-auth-templated-text></a><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_mon_profil" href="/fr/compte/profil-culturel" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e10d_Ticket.svg" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Profil culturel</div></a><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_mes_favoris" href="/fr/compte/favoris" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e256_Heart.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Favoris</div></a><lvc-auth-wrapper><a slot="partner" href="https://pro.lavitrine.com" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e184_lvc-espace-pro.svg" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Compte professionnel</div></a></lvc-auth-wrapper><a data-gtm-trigger-location="menu_principal" data-gtm-trigger-global-id="cta_notifications" href="/fr/compte/notifications" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e1fa_Notification.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Notifications</div></a><a href="/fr/compte/info-personnelles" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e26e_Profil.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Infos personnelles</div></a><a href="/fr/compte/connexion-et-securite" class="nav_account-link_menu_icon-link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e250_Security.png" loading="lazy" width="20" height="20" alt="" class="nav_account-link_menu_icon"><div class="nav_account-link_menu_link-text">Connexion et sécurité</div></a><div class="nav_account-link_menu_divider"></div><div id="account-menu-logout" data-auth-disconnect="true" class="nav_account-link_menu_disconnect">Déconnexion</div></nav></div></lvc-auth-wrapper><div class="nav_menu-button w-nav-button"><div class="nav_menu-button-inner-wrapper"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f5_lvc-burger-menu.svg" loading="lazy" width="Auto" alt="" class="nav_menu-button-icon"><div class="nav_mobile_link-text">Menu</div></div></div></div></div><main class="main-wrapper"><div id="event-info-bar" class="event_info-bar_component"><div class="event_info-bar_content"><div class="event_info-bar_content-wrapper"><div class="event_info-bar_text-wrapper"><div class="event_info-bar_title text-style-1line">100 ans de chansons françaises avec Richard Charest</div><div class="event_info-bar_info-wrapper"></div></div><div class="event_info-bar_button-wrapper" data-event-status="Scheduled"><lvc-share class="webcomponent_lvc-share" data-button-variant="round" data-event-status="Scheduled" data-event-name="100 ans de chansons françaises avec Richard Charest"></lvc-share></div></div><div class="event_info-bar_content-wrapper is-mobile"><div class="event_info-bar_text-wrapper"><div class="event_info-bar_title text-style-1line">100 ans de chansons françaises avec Richard Charest</div><div class="event_info-bar_info-wrapper"><div class="event_info-bar_info-item-divider"></div></div></div><div class="event_info-bar_button-wrapper"><lvc-share data-button-color="white" data-button-size="large" class="webcomponent_lvc-share"></lvc-share></div></div></div></div><header class="section_event_header"><div class="lvc-mobile-header-logo_component"><div class="padding-global"><div class="container-large"><a href="/" class="lvc-mobile-header-logo_link w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e166_lvc-mobile-logo.svg" loading="lazy" alt=""></a></div></div></div><div class="lvc-breadcrumb_padding"><div class="lvc-breadcrumb_component"><div class="lvc-breadcrumb_left-arrow"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e08b_arrow-prev.svg" loading="lazy" alt="" class="lvc-breadcrumb_left-arrow-icon"></div><div class="lvc-breadcrumb_right-arrow"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e027_arrow-next.svg" loading="lazy" alt="" class="lvc-breadcrumb_right-arrow-icon"></div><div class="lvc-breadcrumb_inner-wrapper"><div class="lvc-breadcrumb_item"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e26a_lvc-home-icon%402x.png" loading="lazy" width="28" height="28" alt="" class="lvc-breadcrumb_home-icon"><a href="https://www.lavitrine.com" class="lvc-breadcrumb_link">Accueil</a></div><div class="lvc-breadcrumb_item"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e26a_lvc-home-icon%402x.png" loading="lazy" width="28" height="28" alt="" class="lvc-breadcrumb_home-icon"><a href="/fr/recherche?query=%7B%22modal%22%3A%22events_search%22%7D" class="lvc-breadcrumb_link">Événements</a></div><div class="lvc-breadcrumb_item"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e26a_lvc-home-icon%402x.png" loading="lazy" width="28" height="28" alt="" class="lvc-breadcrumb_home-icon"><div class="lvc-breadcrumb_text is-active">100 ans de chansons françaises avec Richard Charest</div></div></div></div></div><div class="padding-global"><div class="container-medium"><div class="padding-section-small"><div class="padding-top padding-medium"><div class="lvc_header_component"><div class="lvc_header_content-wrapper"><div class="lvc_header_title-wrapper"><h1 id="eventName" class="lvc_header_title">100 ans de chansons françaises avec Richard Charest</h1></div><div class="event_header_artist-wrapper"><a class="event_header_artist" href="/fr/artiste/richard-charest/28032" id="eventArtist">Richard Charest</a></div><div class="event_info_details-wrapper"></div><div class="event_info_button-layout" data-event-status="Scheduled"></div></div><div class="lvc_header_image-wrapper"><div class="lvc-image_wrapper"><img class="lvc-image_image" src="https://lvc-medias.lavitrine.com/acf4025b-3502-5217-b26d-f3bd5a35c6dc.webp?null" alt="" loading="eager" srcset="https://lvc-medias.lavitrine.com/acf4025b-3502-5217-b26d-f3bd5a35c6dc.webp?null" data-placeholder-image="https://cloud.squidex.io/api/assets/lvc-dev/d6da24cd-2549-481b-98e5-fa3548c469a0/placeholder-evenement.webp?version=0" onerror="$(this).attr('src', $(this).attr('data-placeholder-image'));$(this).attr('srcset', $(this).attr('data-placeholder-image'));"></div></div></div></div></div></div></div></header><div class="section_event_calendar"><div class="event_calendar_content"><div class="padding-global"><div class="padding-bottom padding-medium"><div class="event_calendar_component"><div id="calendar" class="event_calendar_anchor-link"></div><div class="container-medium"><div><div class="lvc_event_offer-list-title-wrapper"><div>Dates</div><div>(<span>1</span>)</div></div><div class="event_calendar_swiper"><div class="swiper-wrapper"><div class="swiper-slide is-event-dates"><a data-gtm-trigger-location="event_offers" data-gtm-trigger-global-id="clic_date_event_offer" href="/fr/evenement/100-ans-de-chansons-francaises-avec-richard-charest/23777/1673187" class="event_calendar_slide-content w-inline-block"><div class="event_calendar_slide-calender-icon-wrapper"><div class="event_calendar_slide-calender-date-text-large">22</div><div class="event_calendar_slide-calender-date-text-small">Nov</div><div class="event_calendar_slide-calender-date-text-small">2026</div></div><div class="event_calendar_slide-calender-text-wrapper"><div class="event_calendar_slide-calender-item"><img width="24" height="24" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e233_lvc-clock.svg" loading="lazy" alt="" class="event_calendar_slide-calendar-small-icon"><div class="text-size-small text-height-1-2">15:00 HNE</div></div><div class="event_calendar_slide-calender-item is-place"><img class="event_calendar_slide-calendar-small-icon" src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e1fc_lvc-map-pin.svg" width="24" height="24" alt="" loading="lazy"><div class="lvc_event_calendar-place-text-wrapper"><div class="text-style-2lines">Théâtre Petit Champlain</div><div>Québec</div></div></div></div></a></div></div><div class="event_calendar_swiper-scrollbar"></div></div></div></div></div></div></div></div></div><section class="section_event_tabs"><div class="padding-global"><div class="container-medium"><div class="padding-section-small is-joining-section"><div data-current="a-propos" data-easing="ease-in-out" data-duration-in="300" data-duration-out="100" class="tabs_tab-component w-tabs"><div class="tabs_tab-menu w-tab-menu"><a data-w-tab="a-propos" data-gtm-trigger-id="tab_apropos" class="tabs_tab-link w-inline-block w-tab-link w--current"><div>À propos</div></a><a data-w-tab="evenement-similaires" data-gtm-trigger-id="tab_similar_events" class="tabs_tab-link w-inline-block w-tab-link"><div>Événements similaires</div></a></div><div class="tabs_tab-layout w-tab-content"><div data-w-tab="a-propos" class="w-tab-pane w--tab-active"><div class="event_tabs_layout"><div class="event_tabs_content-wrapper"><div class="event_tabs_description-flag-wrapper"></div><div id="about-first-sentence" class="event_tabs_description-short">DE CHARLES TRENET À STROMAE, UN SIÈCLE DE MÉLODIES IMMORTELLES !</div><div id="about-description">Après avoir sillonné les plus belles scènes de la planète au sein, entre autres, de la comédie musicale Notre Dame de Paris, du Big Bazar et plus récemment du spectacle Pub Royal des Cowboys Fringants, Richard Charest met ses 25 années d’expérience et sa sensibilité d’interprète au service d’un tout nouveau spectacle célébrant 100 ans de chansons françaises.<br><br>Accompagné à la guitare et au piano par deux talentueux musiciens, Richard vous invite à survoler un siècle de mélodies immortelles popularisées par les plus grands artistes de la scène musicale française. Laissez-vous emporter par les chansons inoubliables de Charles Trenet, Edith Piaf, Gilbert Bécaud, Jacques Brel, Charles Aznavour, Serge Lama, Michel Fugain, Joe Dassin, Francis Cabrel, Luc Plamondon, Patrick Bruel, Céline Dion, Zaz, Stromae et bien plus encore.<br><br>Présentée par le GROUPE RC2 Inc., cette formule trio intimiste vous promet un grand voyage mélodique empreint de douceur et de nostalgie ponctué d’anecdotes savoureuses sur ces sublimes chansons et leurs interprètes de légende.</div><div class="event_tabs_info-component is-mobile"><div class="event_tabs_info-button-wrapper"><lvc-share class="webcomponent_lvc-share" data-event-status="Scheduled" data-event-name="100 ans de chansons françaises avec Richard Charest"></lvc-share></div><div class="event_tabs_info-details-layout"></div></div><div class="event_tabs_artists-component"><div class="event_tabs_artists-wrapper"><div id="artistes" class="event_tabs_contributors-title-wrapper"><div class="text-weight-bold">Artistes</div></div><lvc-search-result-block class="lvc_event_artist-list" results="%5B%7B%22id%22%3A28032%2C%22contributorIdVitrine%22%3A%22LVC-contributor-5bb90f1%22%2C%22contributorType%22%3A%22Person%22%2C%22nameFr%22%3A%22Richard%20Charest%22%2C%22nameEn%22%3A%22Richard%20Charest%22%2C%22alternateName%22%3Anull%2C%22descriptionFr%22%3Anull%2C%22descriptionEn%22%3Anull%2C%22mainEntityOfPageFr%22%3Anull%2C%22mainEntityOfPageEn%22%3Anull%2C%22slugComponent%22%3A%22richard-charest%22%2C%22genderType%22%3Anull%2C%22birthplace%22%3Anull%2C%22nationality%22%3Anull%2C%22sameAs%22%3Anull%2C%22urlConstruct%22%3A%7B%22en%22%3A%22%2Fen%2Fartist%2Frichard-charest%2F28032%22%2C%22fr%22%3A%22%2Ffr%2Fartiste%2Frichard-charest%2F28032%22%7D%2C%22address%22%3Anull%2C%22postalCode%22%3Anull%2C%22city%22%3Anull%2C%22province%22%3Anull%2C%22provinceCode%22%3Anull%2C%22_geoloc%22%3Anull%2C%22touristRegion%22%3Anull%2C%22additionalTypeTaxo%22%3Anull%2C%22uiAdditionalTypesCode%22%3Anull%2C%22uiAdditionalTypes%22%3Anull%2C%22medias%22%3Anull%2C%22total_event_count%22%3A1%2C%22publishedEventsCount%22%3A1%2C%22hasPublishedEvents%22%3Atrue%2C%22agg_affs%22%3Anull%2C%22clientId%22%3Anull%2C%22offersCount%22%3A0%2C%22table%22%3A%22contributors%22%7D%5D"></lvc-search-result-block></div></div><div class="event_tabs_tags-wrapper is-mobile"><div class="event_tabs_info-tag-wrapper"><lvc-tags class="webcomponent_lvc-tags" data-discipline-tags="%5B%7B%22id%22%3A306%2C%22code%22%3A%22UI-G-Music-Chanson_Pop%22%2C%22sameAs%22%3A%5B%5D%2C%22labelEn%22%3A%22Music%20%3E%20Song%20and%20Pop%22%2C%22labelFr%22%3A%22Musique%20%3E%20Chanson%20et%20Pop%22%2C%22archived%22%3Afalse%2C%22mergedId%22%3Anull%2C%22created_at%22%3A%222023-09-08T05%3A00%3A57.604186%22%2C%22sourceInfo%22%3A%7B%22conso%22%3A%7B%22additionalTypes%22%3A306%7D%7D%2C%22updated_at%22%3A%222024-05-11T07%3A51%3A08.075816%22%2C%22validationInfo%22%3A%7B%7D%2C%22vocabularyName%22%3A%22V1UITaxoDVitrine%22%2C%22displaySettings%22%3A%5B%5D%2C%22updated_manually%22%3Atrue%2C%22additionalTypes_id%22%3A304%2C%22additionalTypeIdVitrine%22%3A%22V1UITaxoDVitrine_UI-G-Music-Chanson_Pop%22%7D%2C%7B%22id%22%3A304%2C%22code%22%3A%22UI-D-Music%22%2C%22sameAs%22%3A%5B%5D%2C%22labelEn%22%3A%22Music%22%2C%22labelFr%22%3A%22Musique%22%2C%22archived%22%3Afalse%2C%22mergedId%22%3Anull%2C%22created_at%22%3A%222023-09-08T05%3A00%3A57.533377%22%2C%22sourceInfo%22%3A%7B%22conso%22%3A%7B%22additionalTypes%22%3A304%7D%7D%2C%22updated_at%22%3A%222024-05-11T07%3A51%3A08.075816%22%2C%22validationInfo%22%3A%7B%7D%2C%22vocabularyName%22%3A%22V1UITaxoDVitrine%22%2C%22displaySettings%22%3A%5B%5D%2C%22updated_manually%22%3Atrue%2C%22additionalTypes_id%22%3Anull%2C%22additionalTypeIdVitrine%22%3A%22V1UITaxoDVitrine_UI-D-Music%22%7D%5D" data-audience-tags="%22General%22" data-layout="flex"></lvc-tags></div><div class="event_tabs_info-report-problem"><lvc-report-problem-button></lvc-report-problem-button></div></div></div><div class="event_tabs_info-component"><div class="event_tabs_info-button-wrapper"><lvc-share class="webcomponent_lvc-share" data-event-status="Scheduled" data-event-name="100 ans de chansons françaises avec Richard Charest"></lvc-share></div><div class="event_tabs_info-details-layout"></div><div class="event_tabs_info-tag-wrapper"><lvc-tags class="webcomponent_lvc-tags" data-discipline-tags="%5B%7B%22id%22%3A306%2C%22code%22%3A%22UI-G-Music-Chanson_Pop%22%2C%22sameAs%22%3A%5B%5D%2C%22labelEn%22%3A%22Music%20%3E%20Song%20and%20Pop%22%2C%22labelFr%22%3A%22Musique%20%3E%20Chanson%20et%20Pop%22%2C%22archived%22%3Afalse%2C%22mergedId%22%3Anull%2C%22created_at%22%3A%222023-09-08T05%3A00%3A57.604186%22%2C%22sourceInfo%22%3A%7B%22conso%22%3A%7B%22additionalTypes%22%3A306%7D%7D%2C%22updated_at%22%3A%222024-05-11T07%3A51%3A08.075816%22%2C%22validationInfo%22%3A%7B%7D%2C%22vocabularyName%22%3A%22V1UITaxoDVitrine%22%2C%22displaySettings%22%3A%5B%5D%2C%22updated_manually%22%3Atrue%2C%22additionalTypes_id%22%3A304%2C%22additionalTypeIdVitrine%22%3A%22V1UITaxoDVitrine_UI-G-Music-Chanson_Pop%22%7D%2C%7B%22id%22%3A304%2C%22code%22%3A%22UI-D-Music%22%2C%22sameAs%22%3A%5B%5D%2C%22labelEn%22%3A%22Music%22%2C%22labelFr%22%3A%22Musique%22%2C%22archived%22%3Afalse%2C%22mergedId%22%3Anull%2C%22created_at%22%3A%222023-09-08T05%3A00%3A57.533377%22%2C%22sourceInfo%22%3A%7B%22conso%22%3A%7B%22additionalTypes%22%3A304%7D%7D%2C%22updated_at%22%3A%222024-05-11T07%3A51%3A08.075816%22%2C%22validationInfo%22%3A%7B%7D%2C%22vocabularyName%22%3A%22V1UITaxoDVitrine%22%2C%22displaySettings%22%3A%5B%5D%2C%22updated_manually%22%3Atrue%2C%22additionalTypes_id%22%3Anull%2C%22additionalTypeIdVitrine%22%3A%22V1UITaxoDVitrine_UI-D-Music%22%7D%5D" data-audience-tags="%22General%22"></lvc-tags></div><div class="event_tabs_info-report-problem"><lvc-report-problem-button></lvc-report-problem-button></div></div></div></div><div data-w-tab="evenement-similaires" class="w-tab-pane"><lvc-similar-events class="webcomponent_lvc-similar-events" disciplines="UI-G-Music-Chanson_Pop, UI-D-Music" data-schema-type="eventOffers"></lvc-similar-events></div></div></div></div></div></div></section></main><footer class="footer_component"><div class="padding-global"><div class="container-medium"><div class="footer_wrapper"><lvc-newsletter id="contact" class="footer_cta-wrapper"></lvc-newsletter><div class="footer_top-content-wrapper"><div class="footer_columns-wrapper"><div class="footer_column"><div class="footer_column-header">L'organisation</div><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div><div class="footer_column"><div class="footer_column-header">Médias</div><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div><div class="footer_column"><div class="footer_column-header">Aide</div><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div><div class="footer_column"><div class="footer_column-header">Idées de sorties</div><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div><div class="footer_column"><div class="footer_column-header">Espace Pro</div><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div><div class="footer_columns-wrapper-mobile"><div class="fs_accordion-2_embed w-embed w-script"><!-- [Finsweet Attributes] Accordion -->
488 +<script>(()=>{var t="https://cdn.jsdelivr.net/npm/@finsweet/attributes-accordion@1/accordion.js",e=document.querySelector(`script[src="${t}"]`);e||(e=document.createElement("script"),e.async=!0,e.src=t,document.head.append(e));})();</script></div><div fs-accordion-initial="none" fs-accordion-element="group" class="fs_accordion-2_component"><div class="footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header"><div class="footer_column-header">L'organisation</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f4_lvc-dropdown-arrow.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/a-propos" target="_self">À propos</a><a class="footer_text-link" href="/fr/equipe" target="_self">Équipe</a><a class="footer_text-link" href="/fr/gouvernance" target="_self">Gouvernance</a><a class="footer_text-link" href="/fr/partenaires" target="_self">Partenaires</a><a class="footer_text-link" href="/fr/carrieres" target="_self">Carrières</a></div></div></div></div><div class="footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header"><div class="footer_column-header">Médias</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f4_lvc-dropdown-arrow.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/medias" target="_self">Communiqués de presse</a></div></div></div></div><div class="footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header"><div class="footer_column-header">Aide</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f4_lvc-dropdown-arrow.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/faq" target="_self">Foire aux questions</a><a class="footer_text-link" href="/fr/contact" target="_self">Contactez-nous</a></div></div></div></div><div class="footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header"><div class="footer_column-header">Idées de sorties</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f4_lvc-dropdown-arrow.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/blogue" target="_self">Blogue</a><a class="footer_text-link" href="/fr/destination/quoi-faire-gatineau" target="_self">Quoi faire à Gatineau</a><a class="footer_text-link" href="/fr/destination/quoi-faire-montreal" target="_self">Quoi faire à Montréal</a><a class="footer_text-link" href="/fr/destination/quoi-faire-quebec" target="_self">Quoi faire à Québec</a><a class="footer_text-link" href="/fr/destination/quoi-faire-saguenay" target="_self">Quoi faire à Saguenay</a><a class="footer_text-link" href="/fr/destination/quoi-faire-sherbrooke" target="_self">Quoi faire à Sherbrooke</a><a class="footer_text-link" href="/fr/destination/quoi-faire-trois-rivieres" target="_self">Quoi faire à Trois-Rivières</a></div></div></div></div><div class="footer_column"><div fs-accordion-element="accordion" class="fs_accordion-2_item"><div fs-accordion-element="trigger" class="fs_accordion-2_footer_header"><div class="footer_column-header">Espace Pro</div><div fs-accordion-element="arrow" class="fs_accordion-2_arrow-wrapper is-active-accordion"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e0f4_lvc-dropdown-arrow.svg" loading="lazy" alt="" class="fs_accordion-2_footer_icon"></div></div><div fs-accordion-element="content" class="fs_accordion-2_content"><div class="footer_column-links-wrapper"><a class="footer_text-link" href="/fr/espace-pro" target="_self">Compte professionnel</a><a class="footer_text-link" href="/fr/centre-de-ressources" target="_self">Centre de ressources</a><a class="footer_text-link" href="/fr/faq-pro" target="_self">Foire aux Questions Pro</a><a class="footer_text-link" href="/fr/espace-pro#contact" target="_self">S'abonner à l'infolettre Pro</a></div></div></div></div></div></div><div class="footer_social-language-wrapper"><div id="w-node-_3d6f7482-e98e-693b-58e0-899d14c1b504-30917d4a" class="footer_social-icons-wrapper"><a href="https://www.facebook.com/vitrineculturelle" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/8154deed-a074-4df8-a8a9-1665cc3c9bc0/?null" alt="Facebook" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/8154deed-a074-4df8-a8a9-1665cc3c9bc0/?null"><div class="footer_social-link-text">Facebook</div></a><a href="https://www.instagram.com/la_vitrine" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/da9a6d3b-cf85-4efb-96c8-ad7c18fd987c/?null" alt="Instagram" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/da9a6d3b-cf85-4efb-96c8-ad7c18fd987c/?null"><div class="footer_social-link-text">Instagram</div></a><a href="https://twitter.com/la_vitrine" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/5d1cf7da-9fef-4f6d-b1b6-5925b827a25f/?null" alt="X" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/5d1cf7da-9fef-4f6d-b1b6-5925b827a25f/?null"><div class="footer_social-link-text">X</div></a><a href="https://www.youtube.com/user/Lavitrineculturelle" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/77065cd4-4e53-43c2-9940-3c022856e3c4/?null" alt="YouTube" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/77065cd4-4e53-43c2-9940-3c022856e3c4/?null"><div class="footer_social-link-text">YouTube</div></a><a href="https://www.linkedin.com/company/590570/admin/feed/posts/" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/9f44d342-09c5-4097-9432-e79b3a5ffea6/?null" alt="LinkedIn" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/9f44d342-09c5-4097-9432-e79b3a5ffea6/?null"><div class="footer_social-link-text">LinkedIn</div></a><a href="https://www.tiktok.com/@lavitrine.com" target="_blank" class="footer_social-link w-inline-block"><img class="footer_social-link-icon" src="https://cloud.squidex.io/api/assets/lvc-dev/7dab3ddd-b57c-4d6e-a15e-06ad9ba489e0/?null" alt="TikTok" loading="lazy" srcset="https://cloud.squidex.io/api/assets/lvc-dev/7dab3ddd-b57c-4d6e-a15e-06ad9ba489e0/?null"><div class="footer_social-link-text">TikTok</div></a></div><div class="footer_language-switch-wrapper hide-tablet"><a href="/en/event/100-ans-de-chansons-francaises-avec-richard-charest/23777" class="footer_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e113_lvc-lang-switch-fr.svg" loading="lazy" width="88" height="48" alt="" class="footer_language-switch-icon"></a></div></div></div><div class="footer_bottom-content-wrapper"><div class="footer_copyright-row"><div class="footer_copyright-text">© 2026 La Vitrine culturelle</div><div class="footer_policy-links"><a class="footer_policy-text-link" href="/fr/politique-de-confidentialite" target="_self">Politique de confidentialité</a><a class="footer_policy-text-link" href="/fr/politique-de-partage-des-donnees" target="_self">Politique de partage des données</a><a class="footer_policy-text-link" href="javascript:openAxeptioCookies()" target="_self">Gérer mes cookies</a><a class="footer_policy-text-link" href="/fr/conditions-d-utilisation" target="_self">Conditions d’utilisation</a></div></div></div><div class="footer_bottom-content-wrapper-mobile"><div class="footer_copyright-row"><div class="footer_copyright-text">© 2026 La Vitrine culturelle</div><div class="footer_language-switch-wrapper"><a href="/en/event/100-ans-de-chansons-francaises-avec-richard-charest/23777" class="footer_language-switch w-inline-block"><img src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/6894a11aea4bc1fdc212e113_lvc-lang-switch-fr.svg" loading="lazy" width="88" height="48" alt="" class="footer_language-switch-icon"></a></div></div><div class="footer_middle-row-mobile"><div class="footer_policy-links"><a class="footer_policy-text-link" href="/fr/politique-de-confidentialite" target="_self">Politique de confidentialité</a><a class="footer_policy-text-link" href="/fr/politique-de-partage-des-donnees" target="_self">Politique de partage des données</a><a class="footer_policy-text-link" href="javascript:openAxeptioCookies()" target="_self">Gérer mes cookies</a><a class="footer_policy-text-link" href="/fr/conditions-d-utilisation" target="_self">Conditions d’utilisation</a></div></div><div class="footer_logos-layout"><div class="footer_partner-logo-wrapper"></div></div></div></div></div></div></footer></div><script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=6894a11aea4bc1fdc212dfdb" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script><script src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/js/webflow.schunk.36b8fb49256177c8.js" type="text/javascript" integrity="sha384-4abIlA5/v7XaW1HMXKBgnUuhnjBYJ/Z9C1OSg4OhmVw9O3QeHJ/qJqFBERCDPv7G" crossorigin="anonymous"></script><script src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/js/webflow.schunk.f0b7d6d0ebeedbb3.js" type="text/javascript" integrity="sha384-Ey/zePk+QfNaTT7BFsNVFYOMRQuEEovsyuWIaaWarY8f6v12DDWdv6tK5U9wCOBK" crossorigin="anonymous"></script><script src="https://cdn.prod.website-files.com/6894a11aea4bc1fdc212dfdb/js/webflow.30ab0cf7.44d37b9db6899b2b.js" type="text/javascript" integrity="sha384-k3e+tZWYa605tqMEz8KIhqV2Qh/a0DE6O3CaSS+ABXLT9zU5bdmTlnJB0/+r0tkj" crossorigin="anonymous"></script><script>
489 +const banner = $('.mobile-lvc-banner-wrapper')
490 + document.addEventListener('DOMContentLoaded', function() {
491 + banner.addClass('animate')
492 + })
493 +</script>
494 +
495 +<!-- GTM EVENT TRIGGERS -->
496 +<script>
497 + $("[data-gtm-trigger-global-id]").on('click', function () {
498 + window.dataLayer.push({
499 + event: $(this).attr('data-gtm-trigger-global-id'),
500 + emplacement: $(this).attr('data-gtm-trigger-location')
501 + })
502 + })
503 +
504 + $("[data-gtm-trigger-id-0='decouvrir']").on('click', function () {
505 + window.dataLayer.push({
506 + event: 'cta_decouvrir',
507 + emplacement: $(this).attr('data-gtm-trigger-location')
508 + })
509 + })
510 +
511 + $("[data-gtm-trigger-id-0='compte']").on('click', function () {
512 + window.dataLayer.push({
513 + event: 'cta_compte',
514 + emplacement: $(this).attr('data-gtm-trigger-location')
515 + })
516 + })
517 +
518 + $("[data-gtm-trigger-id-0='rechercher']").on('click', function () {
519 + window.dataLayer.push({
520 + event: 'cta_rechercher',
521 + emplacement: $(this).attr('data-gtm-trigger-location')
522 + })
523 + })
524 +</script>
525 +
526 +<!-- EVENT INFO BAR DISPLAY LOGIC -->
527 +<script>
528 + const eventInfoBar = document.getElementById('event-info-bar')
529 + let showPosition = 800
530 + if (window.innerWidth <= 991) {
531 + showPosition = 350
532 + }
533 + if (eventInfoBar) {
534 + $(document).ready(() => {
535 + if (window.pageYOffset >= showPosition) {
536 + $(eventInfoBar).show()
537 + } else {
538 + $(eventInfoBar).hide()
539 + }
540 + $(document).on('scroll', () => {
541 + if (window.pageYOffset >= showPosition) {
542 + $(eventInfoBar).fadeIn(200)
543 + } else {
544 + $(eventInfoBar).fadeOut(200)
545 + }
546 + })
547 + })
548 + }
549 +</script>
550 +
551 +<!-- NAV MENU LANGUAGE BUTTON -->
552 +<script>
553 + const footerLangButton = $('.footer_language-switch')
554 + const navMenuLangButton = $('.nav_menu_language-switch')
555 + if (footerLangButton) {
556 + const langLink = $(footerLangButton).attr('href')
557 + $(navMenuLangButton).attr('href', langLink)
558 + }
559 +</script>
560 +
561 +<!-- ACCOUNT DISCONNECT BUTTON -->
562 +<script>
563 + $("[data-auth-disconnect='true']").on('click', () => {
564 + window.LVC_MAIN_MODULE.getAuth().logout()
565 + })
566 +</script>
567 +
568 +<script>
569 +$('.nav_mobile_component .nav_account-link_dropdown-toggle, .nav_mobile_component .nav_menu-button').on('click',()=>{
570 + window.dispatchEvent(new CustomEvent('lvc:search-menu-close-button-clicked'))
571 +})
572 +</script>
573 +<!-- SWIPER.JS -->
574 +<script src="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js"></script>
575 +
576 +<!-- GTM CUSTOM CODE -->
577 +<script id="pageGTM" data-id="23777" data-schema="events" data-ui-additional-type="Musique > Chanson et Pop">
578 + // Page Load GTM
579 + const $pageGTM = $('#pageGTM');
580 + const $eventPlace = $('#eventPlace');
581 + const $eventLocation = $('#eventLocation');
582 + const $ticketText = $('#ticketText');
583 +
584 + const ticketText = $ticketText.text().toLowerCase();
585 + const eventPlace = $eventPlace.text();
586 +
587 + // Helper function to determine ticket category
588 + const getTicketCategory = (text) => {
589 + if (['billets', 'tickets'].includes(text)) return 'billet';
590 + if (['free', 'gratuit'].includes(text)) return 'gratuit';
591 + return $ticketText.text(); // Return original case if no match
592 + };
593 +
594 + const gtmData = {
595 + item_id: $pageGTM.attr('data-id'),
596 + item_name: $('#eventName').text(),
597 + item_brand: $('#eventArtist').text(),
598 + item_category: $pageGTM.attr('data-ui-additional-type'),
599 + item_category2: eventPlace ? `${eventPlace}, ${$eventLocation.text()}` : '',
600 + item_category3: `${$('#eventDate').text()} - ${$('#eventTime').text()}`,
601 + item_category4: getTicketCategory(ticketText),
602 + emplacement: 'page_evenement'
603 + };
604 +
605 + window.dataLayer.push({
606 + event: 'view_item',
607 + ecommerce: {
608 + items: [gtmData],
609 + },
610 + })
611 +
612 + // About Tab GTM
613 + $("[data-gtm-trigger-id='tab_apropos']").on('click', () => {
614 + window.dataLayer.push({
615 + event: 'onglet_apropos',
616 + emplacement: gtmData.emplacement,
617 + })
618 + })
619 +
620 + // Similar Events Tab GTM
621 + $("[data-gtm-trigger-id='tab_similar_events']").on('click', () => {
622 + window.dataLayer.push({
623 + event: 'onglet_evenements_similaires',
624 + emplacement: gtmData.emplacement,
625 + })
626 + })
627 +
628 + // Ticket Button GTM & Facebook Pixel
629 + $("[data-gtm-trigger-id='ticketButton']").on('click', function () {
630 + // Check if the event is free/gratuit
631 + if (gtmData.item_category4 === 'gratuit') {
632 + // Send the special GTM event for free events
633 + window.dataLayer.push({
634 + event: 'cta_gratuit',
635 + id_evenement: gtmData['item_id'],
636 + nom: gtmData['item_name'],
637 + artiste: gtmData['item_brand'],
638 + type: gtmData['item_category'],
639 + event_location: gtmData['item_category2'],
640 + date: gtmData['item_category3'],
641 + emplacement: gtmData['emplacement'] || '',
642 + })
643 + } else {
644 + // Send the regular add_to_cart event for paid events
645 + window.dataLayer.push({
646 + event: 'add_to_cart',
647 + ecommerce: {
648 + items: [gtmData],
649 + },
650 + })
651 + }
652 +
653 + // Facebook Pixel
654 + fbq('track', 'AddToCart', {
655 + ticket_url: this.href || '',
656 + state: getTicketCategory($ticketText.text().toLowerCase()),
657 + emplacement: gtmData.emplacement,
658 + })
659 + })
660 +
661 + // Manipulating Favorite Button for GTM
662 + $('.webcomponent_lvc-favorite-button').attr({
663 + 'data-gtm-id': gtmData['item_id'],
664 + 'data-gtm-name': gtmData['item_name'],
665 + 'data-gtm-brand': gtmData['item_brand'],
666 + 'data-gtm-category': gtmData['item_category'],
667 + 'data-gtm-category2': gtmData['item_category2'],
668 + 'data-gtm-category3': gtmData['item_category3'],
669 + 'data-gtm-category4': gtmData['item_category4'],
670 + })
671 +</script>
672 +
673 +<!-- CALENDAR SLIDER -->
674 +<script>
675 + const calendarSwiper = new Swiper('.event_calendar_swiper', {
676 + speed: 400,
677 + spaceBetween: 10,
678 + centeredSlides: false,
679 + slidesPerView: 'auto',
680 + updateOnWindowResize: true,
681 + scrollbar: {
682 + el: '.event_calendar_swiper-scrollbar',
683 + draggable: true,
684 + },
685 + })
686 +</script>
687 +
688 +<!-- CUSTOM BREADCRUMBS FOR MOBILE -->
689 +<script>
690 + $(document).ready(function () {
691 + const wrapper = document.querySelector('.lvc-breadcrumb_inner-wrapper')
692 + const leftArrow = $('.lvc-breadcrumb_left-arrow')
693 + const rightArrow = $('.lvc-breadcrumb_right-arrow')
694 + const clientWidth = wrapper.clientWidth
695 + const scrollWidth = wrapper.scrollWidth
696 + if (scrollWidth > clientWidth) {
697 + leftArrow.hide()
698 + rightArrow.show()
699 + } else {
700 + leftArrow.hide()
701 + rightArrow.hide()
702 + }
703 + $('.lvc-breadcrumb_inner-wrapper').on('scroll', function () {
704 + const scrollPosLeft = Math.round(this.scrollLeft)
705 + const scrollPosRight = scrollWidth - scrollPosLeft
706 + const isStart = scrollPosLeft <= 5
707 + const isEnd = scrollPosRight - 5 <= clientWidth
708 + const showLeftArrow = !isStart
709 + const showRightArrow = !isEnd
710 + showLeftArrow ? leftArrow.show() : leftArrow.hide()
711 + showRightArrow ? rightArrow.show() : rightArrow.hide()
712 + })
713 + })
714 +</script>
715 +<script>
716 + function adjustFontSize() {
717 + const heading = document.querySelector('.lvc_header_title')
718 + const container = heading.parentElement
719 + let fontSize = parseFloat(window.getComputedStyle(heading).fontSize)
720 + while (heading.clientHeight < container.clientHeight && fontSize <= 40) {
721 + fontSize += 1
722 + heading.style.setProperty('font-size', `${fontSize}px`)
723 + }
724 + while (heading.scrollWidth > container.clientWidth && fontSize > 0) {
725 + fontSize -= 1
726 + heading.style.setProperty('font-size', `${fontSize}px`)
727 + }
728 + while (heading.clientHeight > container.clientHeight && fontSize > 0) {
729 + fontSize -= 1
730 + heading.style.setProperty('font-size', `${fontSize}px`)
731 + }
732 + }
733 + window.addEventListener('resize', adjustFontSize)
734 + window.addEventListener('DOMContentLoaded', adjustFontSize)
735 +</script>
736 +<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a2cb693f29ba8f77',t:'MTc4Njk5ODM1OA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>
\ No newline at end of file
added tests/fixtures/lepointdevente_page1.html +2284 −0
@@ -0,0 +1,2284 @@
1 +<!DOCTYPE html>
2 +<html lang="fr" data-release="6a760b15">
3 +<head>
4 + <title>Lepointdevente.com</title>
5 + <meta charset="utf-8">
6 + <meta name="description" content="Découvrez une multitude d&#039;événements dans votre région et faites plaisir à vos proches. Achetez des billets en quelques clics.">
7 + <meta property="og:title" content="Lepointdevente.com">
8 + <meta property="og:type" content="website">
9 + <meta property="og:image" content="https://cdn.lepointdevente.com/themes/tpos/ogp-img-fr-5f32b556.gif">
10 + <meta property="og:url" content="https://lepointdevente.com/billets?locality=11&amp;radius=100">
11 + <meta property="og:description" content="Découvrez une multitude d&#039;événements dans votre région et faites plaisir à vos proches. Achetez des billets en quelques clics.">
12 + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
13 + <meta name="theme-color" content="#3a3a3a">
14 + <meta name="turbolinks-cache-control" content="no-cache">
15 + <!--[if lte IE 8]><script src="//cdn.lepointdevente.com/releases/6a760b15/js/html5shiv.min.js"></script><![endif]-->
16 + <link rel="shortcut icon" href="//cdn.lepointdevente.com/themes/tpos/favicon-en-5f32b555.png">
17 + <link rel="canonical" href="https://lepointdevente.com/billets?locality=11&amp;radius=100">
18 + <link href="//cdn.lepointdevente.com/releases/6a760b15/b2c/icons/apple-touch-icon.png" rel="apple-touch-icon">
19 + <link href="//cdn.lepointdevente.com/releases/6a760b15/b2c/icons/icon-192.png" rel="icon" sizes="192x192">
20 + <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700,800">
21 + <link rel="stylesheet" href="//cdn.lepointdevente.com/releases/6a760b15/b2c/css/main.css" type="text/css" data-turbolinks-track="reload">
22 + <script type="text/javascript" src="https://pg.feroot.com/v1/bundle/6fd73e95-a2f0-43bb-8d57-2048cbe4e4ff" async></script>
23 + <script>var cookiedomain='lepointdevente.com',embedded=!1;try{embedded=window.self!==window.top}catch(e){embedded=!0}if(embedded){document.documentElement.className+=' embed'}</script>
24 + <script src="//cdn.lepointdevente.com/releases/6a760b15/js/modernizr.min.js"></script>
25 + <script src="//cdn.lepointdevente.com/releases/6a760b15/js/detectizr.min.js"></script>
26 + <script src="//cdn.lepointdevente.com/releases/6a760b15/b2c/js/vendors.js" data-turbolinks-track="reload"></script>
27 + <script src="https://cdnjs.cloudflare.com/ajax/libs/email-autocomplete/0.1.3/jquery.email-autocomplete.min.js"></script>
28 + <script src="//cdn.lepointdevente.com/releases/6a760b15/b2c/js/locale/fr.js" data-turbolinks-track="reload" defer></script>
29 + <script src="//cdn.lepointdevente.com/releases/6a760b15/b2c/locale/fr.js" data-turbolinks-track="reload" defer></script>
30 + <script src="//cdn.lepointdevente.com/releases/6a760b15/b2c/js/main.js" data-turbolinks-track="reload" defer></script>
31 + <script src="/plugins/embed.js?footer=false&amp;ping=false" defer></script>
32 + <script src="https://cdn.seatsio.net/chart.js" defer></script>
33 + <script src="//maps.googleapis.com/maps/api/js?key=AIzaSyBO9ufDr3qKNIY16M0IhgdHrsUA3rteH_A&loading=async" defer></script>
34 + <script src="https://www.google.com/recaptcha/api.js"></script>
35 +</head>
36 +<body data-canonical="/billets?locality=11&amp;radius=100" data-model="events">
37 + <a href="#skip-content-anchor" class="skip-content-link" data-turbolinks="false">Passer au contenu</a>
38 + <header id="header" class="l-header">
39 + <div class="l-page-wrapper">
40 + <div class="l-header-content">
41 + <a class="logo" href="/">
42 + <img src="https://cdn.lepointdevente.com/themes/tpos/logo-on-dark-fr-5f32b556.svg" alt="Lepointdevente.com">
43 + </a>
44 + <button id="embed-back" type="button" class="embed-back" onclick="embedBack();"></button>
45 + <nav class="nav-meta">
46 + <ul>
47 + <li class="nav-meta-item"><a href="http://blog.lepointdevente.com/" target="_blank"><span class="nav-meta-text">Blogue</span></a></li>
48 + <li class="nav-meta-item"><a href="/contact/"><span class="nav-meta-text">Contact</span></a></li>
49 + <li class="nav-meta-item"><a href="https://lepointdevente.com/organisateurs/a-propos/emplois/" target="_blank"><span class="nav-meta-text">Emplois</span></a></li>
50 + </ul>
51 + </nav>
52 + <div id="help" class="folder-help js-folder">
53 + <button class="btn help js-folder-trigger">
54 + <span class="text-wrapper">
55 + <span class="text" data-turbolinks-permanent>Aide</span>
56 + </span>
57 + </button>
58 + <nav id="help-menu" class="folder-content js-folder-content">
59 + <ul>
60 + <li><a href="http://aide.lepointdevente.com/" target="blank">Centre d'aide</a></li>
61 + <li><a href="https://www.youtube.com/@Lepointdeventecom-vu9lt/videos" target="blank">Tutoriels vidéos</a></li>
62 + <li><a href="https://aide.lepointdevente.com/fr/collections/572670-f-a-q-lepointdevente-com" target="blank">F.A.Q.</a></li>
63 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/etudes-de-cas" target="blank">Études de cas</a></li>
64 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/bonnes-pratiques" target="blank">Bonnes pratiques</a></li>
65 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/formations" target="blank">Formations</a></li>
66 + </ul>
67 + </nav>
68 + </div>
69 + <div class="langselector embed-hidden">
70 + <a href="/?lang=en" lang="en" class="langselector-trigger">
71 + <span class="langselector-icon" aria-hidden="true"></span>
72 + <span class="langselector-trigger-text">English</span>
73 + </a>
74 + </div>
75 + <a id="sign-in" href="/connexion/" class="btn login"><span class="text">Connexion</span></a>
76 + <div id="user" class="folder-login js-folder">
77 + <button class="btn login js-folder-trigger">
78 + <span class="text-wrapper">
79 + <span id="user-name" class="text" data-turbolinks-permanent></span>
80 + </span>
81 + </button>
82 + <nav id="user-menu" class="folder-content js-folder-content">
83 + <ul>
84 + <li><a href="/mon-compte/">Mon compte</a></li>
85 + <li id="user-menu-management" data-turbolinks-permanent><a href="/gestion/" data-turbolinks="false">Gestion des événements</a></li>
86 + <li><a href="/panier">Panier d'achat</a></li>
87 + <li><a href="/deconnexion/" data-turbolinks="false">Déconnexion</a></li>
88 + </ul>
89 + </nav>
90 + </div>
91 + <a id="organizers" href="/organisateurs" class="btn create is-red" data-turbolinks="false" data-turbolinks-permanent><span class="text">Créer un événement</span></a>
92 + <a id="cart-mobile" href="/panier" class="header-cart" data-turbolinks-permanent></a>
93 + <button class="offcanvas-trigger js-offcanvas-trigger">
94 + <span class="offcanvas-trigger-strokes">
95 + <span class="offcanvas-trigger-stroke"></span>
96 + <span class="offcanvas-trigger-stroke"></span>
97 + <span class="offcanvas-trigger-stroke"></span>
98 + </span>
99 + </button>
100 + <div class="langs">
101 + <select id="langs">
102 + <option lang="en" value="en">English</option>
103 + <option lang="fr" value="fr" selected>Français</option>
104 + </select>
105 + </div>
106 + <button class="btn-close js-embed-modal-close"></button>
107 + </div>
108 + </div>
109 + </header>
110 + <div id="mobile-menu" class="l-offcanvas js-offcanvas">
111 + <button class="visuallyhidden btn-guard top" aria-hidden="true"></button>
112 + <h1 class="visuallyhidden">Menu principal</h1>
113 + <nav class="nav-meta">
114 + <ul>
115 + <li class="nav-meta-item"><a href="http://blog.lepointdevente.com/" target="blank"><span class="nav-meta-text">Blogue</span></a></li>
116 + <li class="nav-meta-item"><a href="/contact/"><span class="nav-meta-text">Contact</span></a></li>
117 + <li class="nav-meta-item"><a href="https://lepointdevente.com/organisateurs/a-propos/emplois/" target="_blank"><span class="nav-meta-text">Emplois</span></a></li>
118 + </ul>
119 + </nav>
120 + <div id="help-mobile" class="folder-help js-folder">
121 + <button class="btn help js-folder-trigger">
122 + <span class="text-wrapper">
123 + <span class="text" data-turbolinks-permanent>Aide</span>
124 + </span>
125 + </button>
126 + <nav id="help-menu-mobile" class="folder-content js-folder-content" data-turbolinks-permanent>
127 + <ul>
128 + <li><a href="http://aide.lepointdevente.com/" target="blank">Centre d'aide</a></li>
129 + <li><a href="https://www.youtube.com/@Lepointdeventecom-vu9lt/videos" target="blank">Tutoriels vidéos</a></li>
130 + <li><a href="https://aide.lepointdevente.com/fr/collections/572670-f-a-q-lepointdevente-com" target="blank">F.A.Q.</a></li>
131 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/etudes-de-cas" target="blank">Études de cas</a></li>
132 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/bonnes-pratiques" target="blank">Bonnes pratiques</a></li>
133 + <li><a href="https://lepointdevente.com/organisateurs/landing-page/formations" target="blank">Formations</a></li>
134 + </ul>
135 + </nav>
136 + </div>
137 + <div class="langselector embed-hidden">
138 + <a href="/?lang=en" lang="en" class="langselector-trigger">
139 + <span class="langselector-icon" aria-hidden="true"></span>
140 + <span class="langselector-trigger-text">English</span>
141 + </a>
142 + </div>
143 + <a id="sign-in-mobile" href="/connexion/" class="btn login"><span class="text">Connexion</span></a>
144 + <div id="user-mobile" class="folder-login js-folder">
145 + <button class="btn login js-folder-trigger">
146 + <span class="text-wrapper">
147 + <span id="user-name-mobile" class="text" data-turbolinks-permanent></span>
148 + </span>
149 + </button>
150 + <nav id="user-menu-mobile" class="folder-content js-folder-content" data-turbolinks-permanent>
151 + <ul>
152 + <li><a href="/mon-compte/">Mon compte</a></li>
153 + <li id="user-menu-management-mobile" data-turbolinks-permanent><a href="/gestion/" data-turbolinks="false">Gestion des événements</a></li>
154 + <li><a href="/panier">Panier d'achat</a></li>
155 + <li><a href="/deconnexion/" data-turbolinks="false">Déconnexion</a></li>
156 + </ul>
157 + </nav>
158 + </div>
159 + <a id="organizers-mobile" href="/organisateurs" class="btn create is-red" data-turbolinks="false" data-turbolinks-permanent><span class="text">Créer un événement</span></a>
160 + <nav class="nav-prefooter">
161 + <div>
162 + <h2><a href="/">Acheter des billets</a></h2>
163 + <ul>
164 + <li class="nav-prefooter-item"><a id="modal-giftcard-link-trigger-mobile" tabindex="0">Offrir une carte-cadeau</a></li>
165 + <li class="nav-prefooter-item"><a href="/conditions/#achat">Politique d’achat</a></li>
166 + <li class="nav-prefooter-item"><a href="http://aide.lepointdevente.com/fr/collections/572670-f-a-q-achat-de-billets-en-ligne" target="blank">Aide</a></li>
167 + </ul>
168 + </div>
169 + <div>
170 + <h2><a href="/organisateurs" data-turbolinks="false">Organisateurs</a></h2>
171 + <ul>
172 + <li class="nav-prefooter-item"><a href="/organisateurs" data-turbolinks="false">Créer un événement</a></li>
173 + </ul>
174 + </div>
175 + <div>
176 + <h2><a href="/">Événements</a></h2>
177 + <ul>
178 + <li class="nav-prefooter-item"><a href="/?type=virtual&amp;locality=">Événements virtuels</a></li>
179 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=11&amp;radius=0">Événements à Montréal</a></li>
180 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=1&amp;radius=0">Événements à Québec</a></li>
181 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=2&amp;radius=0">Événements à Longueuil</a></li>
182 + </ul>
183 + </div>
184 + </nav>
185 + <nav class="nav-medias">
186 + <ul>
187 + <li class="nav-medias-item">
188 + <a href="https://facebook.com/lepointdevente" class="facebook" target="_blank"><span class="visuallyhidden" lang="en">Facebook</span></a>
189 + </li>
190 + <li class="nav-medias-item">
191 + <a href="https://twitter.com/lepointdevente" class="twitter" target="_blank"><span class="visuallyhidden" lang="en">Twitter</span></a>
192 + </li>
193 + <li class="nav-medias-item">
194 + <a href="https://instagram.com/lepointdevente" class="instagram" target="_blank"><span class="visuallyhidden" lang="en">Instagram</span></a>
195 + </li>
196 + <li class="nav-medias-item">
197 + <a href="https://www.linkedin.com/company/lepointdevente-com" class="linkedin" target="_blank"><span class="visuallyhidden" lang="en">LinkedIn</span></a>
198 + </li>
199 + <li class="nav-medias-item">
200 + <a href="https://www.tiktok.com/@lepointdevente.com" class="tiktok" target="_blank"><span class="visuallyhidden" lang="en">Tiktok</span></a>
201 + </li>
202 + </ul>
203 + </nav>
204 + <a href="/organisateurs/a-propos#compliance-security" class="b2c-mobile-pci-link" target="_blank">
205 + <img src="https://tpos-cdn.s3.ca-central-1.amazonaws.com/b2b/about/logo-pci-white.png" alt="PCI Logo">
206 + </a>
207 + <nav class="nav-footer">
208 + <ul>
209 + <li class="nav-footer-item">
210 + <a href="http://blog.lepointdevente.com/" target="blank"><span class="nav-footer-text">Blogue</span></a>
211 + </li>
212 + <li class="nav-footer-item">
213 + <a href="/contact/"><span class="nav-footer-text">Contact</span></a>
214 + </li>
215 + <li class="nav-footer-item">
216 + <a href="https://lepointdevente.com/organisateurs/a-propos/emplois/" target="_blank"><span class="nav-footer-text">Emplois</span></a>
217 + </li>
218 + <li class="nav-footer-item">
219 + <a href="/conditions/"><span class="nav-footer-text">Conditions d'utilisation</span></a>
220 + </li>
221 + <li class="nav-footer-item">
222 + <a href="/conditions/#confidentialite"><span class="nav-footer-text">Politique de confidentialité</span></a>
223 + </li>
224 + <li class="nav-footer-item">
225 + <a role="button" tabindex="0" onclick="openCookiePreferences()" style="cursor:pointer"><span class="nav-footer-text">Préférences de confidentialité</span></a>
226 + </li>
227 + </ul>
228 + </nav>
229 + <button class="visuallyhidden btn-guard bottom" aria-hidden="true"></button>
230 + </div>
231 + <div id="search" class="l-banner">
232 + <div class="l-page-wrapper">
233 + <div class="banner-content">
234 + <form id="search-quick" action="/" method="get" class="form-search">
235 + <label for="search-quick-keywords" class="visuallyhidden">Spectacle, artiste, lieu...</label>
236 + <input id="search-quick-keywords" type="search" name="q" placeholder="Spectacle, artiste, lieu..." autocomplete="off">
237 + <input id="search-quick-type" type="hidden" name="type" disabled>
238 + <input id="search-quick-wfr" type="hidden" name="wfr" disabled>
239 + <input id="search-quick-locality" type="hidden" name="locality" value="11">
240 + <input id="search-quick-radius" type="hidden" name="radius" disabled>
241 + <input id="search-quick-dates" type="hidden" name="dates" disabled>
242 + <input id="search-quick-start" type="hidden" name="start" disabled>
243 + <input id="search-quick-end" type="hidden" name="end" disabled>
244 + <input id="search-quick-categories" type="hidden" name="categories" disabled>
245 + <button type="submit"><span class="visuallyhidden">Rechercher</span></button>
246 + </form>
247 + <div id="search-summary" class="form-search-data-wrapper">
248 + <div class="form-search-data">
249 + <span id="search-summary-links">
250 + <span data-radius="0">À <button type="button" class="search-locality-name link-alike"><span data-placeholder="locality"></span></button></span>
251 + <span data-radius="1">Dans un rayon de <button type="button" class="link-alike"><span data-placeholder="radius"></span> km</button> de <button type="button" class="search-locality-name link-alike"><span data-placeholder="locality"></span></button></span>
252 + </span>
253 + <button type="button" id="search-everywhere" class="link-alike form-search-all">Rechercher partout</button>
254 + </div>
255 + </div>
256 + <div id="search-options" class="form-search-options-folder js-folder">
257 + <button id="search-advanced-trigger" class="form-search-options-trigger js-folder-trigger">
258 + <span class="parentheses">(</span>Plus d'options<span class="parentheses">)</span>
259 + </button>
260 + <div class="form-search-options-content js-folder-content">
261 + <div class="l-page-wrapper">
262 + <form id="search-advanced" action="/" class="form-search-options">
263 + <fieldset>
264 + <legend>Recherche avancée</legend>
265 + <input id="search-advanced-keywords" type="search" name="q" placeholder="Spectacle, artiste, lieu..." autocomplete="off">
266 + </fieldset>
267 + <fieldset id="search-advanced-types">
268 + <legend>Type d'événement</legend>
269 + <div class="checkbox-wrapper">
270 + <input id="search-advanced-type-physical" type="checkbox" name="type" value="physical">
271 + <label for="search-advanced-type-physical">Événements en personne</label>
272 + </div>
273 + <div class="checkbox-wrapper">
274 + <input id="search-advanced-type-virtual" type="checkbox" name="type" value="virtual">
275 + <label for="search-advanced-type-virtual">Événements virtuels</label>
276 + </div>
277 + </fieldset>
278 + <fieldset id="search-advanced-locality">
279 + <legend>Lieu</legend>
280 + <label for="search-advanced-locality-select"><span class="visuallyhidden">Sélectionnez une ville</span></label>
281 + <div class="select-wrapper">
282 + <select id="search-advanced-locality-select" name="locality">
283 + <option value="" data-lat="45.5313015" data-lng="-73.6078282">Rechercher partout</option>
284 + <option value="62849" data-lat="48.6302715" data-lng="-78.3270664">Abitibi</option>
285 + <option value="79244" data-lat="48.7895720" data-lng="-79.3287814">Abitibi-Ouest</option>
286 + <option value="7245" data-lat="45.3111798" data-lng="-74.6367158">Alexandria</option>
287 + <option value="1273" data-lat="48.5591159" data-lng="-71.6510638">Alma</option>
288 + <option value="1275" data-lat="48.5725527" data-lng="-78.1160282">Amos</option>
289 + <option value="1276" data-lat="48.4666914" data-lng="-67.4305586">Amqui</option>
290 + <option value="63054" data-lat="45.6711166" data-lng="-74.4550935">Argenteuil</option>
291 + <option value="315" data-lat="47.9893086" data-lng="-66.7170809">Atholville</option>
292 + <option value="1291" data-lat="49.2042249" data-lng="-68.2276400">Baie-Comeau</option>
293 + <option value="1295" data-lat="47.4439499" data-lng="-70.5100985">Baie-Saint-Paul</option>
294 + <option value="1306" data-lat="46.2131919" data-lng="-70.7766548">Beauceville</option>
295 + <option value="1307" data-lat="45.3108945" data-lng="-73.8797592">Beauharnois</option>
296 + <option value="1311" data-lat="47.0517304" data-lng="-70.8915390">Beaupré</option>
297 + <option value="1313" data-lat="44.9924029" data-lng="-69.8780277">Bedford</option>
298 + <option value="1315" data-lat="45.5711704" data-lng="-73.2108459">Beloeil</option>
299 + <option value="1317" data-lat="46.9236654" data-lng="-70.7213465">Berthier-sur-Mer</option>
300 + <option value="1322" data-lat="45.5836870" data-lng="-71.5755900">Bishopton</option>
301 + <option value="1324" data-lat="45.6721723" data-lng="-73.8665611">Blainville</option>
302 + <option value="1326" data-lat="45.6686844" data-lng="-73.7598298">Bois-des-Filion</option>
303 + <option value="1328" data-lat="45.6177450" data-lng="-73.8404074">Boisbriand</option>
304 + <option value="1332" data-lat="48.0745267" data-lng="-65.5014194">Bonaventure</option>
305 + <option value="1334" data-lat="45.5994306" data-lng="-73.4420266">Boucherville</option>
306 + <option value="677" data-lat="43.1518078" data-lng="-80.2644979">Brantford</option>
307 + <option value="108539" data-lat="44.4293703" data-lng="-79.1534702">Brock</option>
308 + <option value="1342" data-lat="45.3004283" data-lng="-72.6709372">Bromont</option>
309 + <option value="1343" data-lat="45.4570288" data-lng="-73.4653331">Brossard</option>
310 + <option value="1312" data-lat="46.3248142" data-lng="-72.4295329">Bécancour</option>
311 + <option value="1348" data-lat="47.9275319" data-lng="-69.4839843">Cacouna</option>
312 + <option value="126" data-lat="51.0362372" data-lng="-114.0955924">Calgary</option>
313 + <option value="4963" data-lat="45.7483616" data-lng="-73.2688781">Calixa-Lavallée</option>
314 + <option value="5786" data-lat="47.9996202" data-lng="-66.6698369">Campbellton</option>
315 + <option value="1355" data-lat="49.0705323" data-lng="-66.6730648">Cap-Chat</option>
316 + <option value="1356" data-lat="49.1060320" data-lng="-66.6578650">Cap-Chat-Est</option>
317 + <option value="1358" data-lat="47.0242017" data-lng="-70.4441050">Cap-Saint-Ignace</option>
318 + <option value="1359" data-lat="46.6822695" data-lng="-71.7892637">Cap-Santé</option>
319 + <option value="1354" data-lat="47.3758775" data-lng="-61.8721020">Cap-aux-Meules</option>
320 + <option value="1357" data-lat="48.4284610" data-lng="-64.3281930">Cap-d&#039;Espoir</option>
321 + <option value="12445" data-lat="48.1261209" data-lng="-66.1663817">Carleton-sur-Mer</option>
322 + <option value="1366" data-lat="45.4418608" data-lng="-73.2946868">Chambly</option>
323 + <option value="1372" data-lat="45.7263484" data-lng="-73.4900207">Charlemagne</option>
324 + <option value="49215" data-lat="47.6651148" data-lng="-70.3807635">Charlevoix-Est</option>
325 + <option value="1376" data-lat="45.5287490" data-lng="-75.8285582">Chelsea</option>
326 + <option value="1380" data-lat="49.9132592" data-lng="-74.3702869">Chibougamau</option>
327 + <option value="1382" data-lat="49.1260694" data-lng="-68.3954559">Chute-aux-Outardes</option>
328 + <option value="4949" data-lat="45.5448548" data-lng="-75.2840230">Clarence-Rockland</option>
329 + <option value="1384" data-lat="47.6950028" data-lng="-70.2264740">Clermont</option>
330 + <option value="1386" data-lat="45.6666669" data-lng="-72.0833330">Cleveland</option>
331 + <option value="1392" data-lat="45.2365698" data-lng="-71.8081088">Compton</option>
332 + <option value="744" data-lat="45.0288392" data-lng="-74.7304464">Cornwall</option>
333 + <option value="1396" data-lat="45.2950949" data-lng="-74.1771485">Coteau-du-Lac</option>
334 + <option value="1398" data-lat="45.8732821" data-lng="-70.9847943">Courcelles</option>
335 + <option value="1399" data-lat="45.2063399" data-lng="-72.7480539">Cowansville</option>
336 + <option value="1400" data-lat="45.9643860" data-lng="-73.4740712">Crabtree</option>
337 + <option value="1402" data-lat="45.7755529" data-lng="-72.0246082">Danville</option>
338 + <option value="1405" data-lat="45.3790530" data-lng="-73.5443140">Delson</option>
339 + <option value="1409" data-lat="46.6598039" data-lng="-71.9525687">Deschambault</option>
340 + <option value="1412" data-lat="45.9092283" data-lng="-71.3637610">Disraeli</option>
341 + <option value="1414" data-lat="48.8785312" data-lng="-72.2197227">Dolbeau-Mistassini</option>
342 + <option value="1417" data-lat="46.6759299" data-lng="-71.7260441">Donnacona</option>
343 + <option value="1418" data-lat="45.4477237" data-lng="-73.7537183">Dorval</option>
344 + <option value="1421" data-lat="45.8718764" data-lng="-72.4819139">Drummondville</option>
345 + <option value="143" data-lat="53.5185059" data-lng="-113.5111493">Edmonton</option>
346 + <option value="352" data-lat="47.3844551" data-lng="-68.3289773">Edmundston</option>
347 + <option value="1438" data-lat="45.2827326" data-lng="-72.9760484">Farnham</option>
348 + <option value="1441" data-lat="47.4020519" data-lng="-61.8959135">Fatima</option>
349 + <option value="1448" data-lat="46.8846217" data-lng="-71.6143865">Fossambault-sur-le-Lac</option>
350 + <option value="1449" data-lat="45.2865070" data-lng="-72.4919060">Foster</option>
351 + <option value="1457" data-lat="48.8712414" data-lng="-64.4530614">Gaspé</option>
352 + <option value="1458" data-lat="45.4669767" data-lng="-75.7025605">Gatineau</option>
353 + <option value="1465" data-lat="45.7666670" data-lng="-74.2500000">Gore</option>
354 + <option value="1467" data-lat="45.4035823" data-lng="-72.7329422">Granby</option>
355 + <option value="1473" data-lat="48.4981255" data-lng="-64.5469633">Grande-Rivière</option>
356 + <option value="1476" data-lat="48.2434792" data-lng="-69.5451847">Grandes-Bergeronnes</option>
357 + <option value="41014" data-lat="46.5083962" data-lng="-80.9801422">Greater Sudbury</option>
358 + <option value="46595" data-lat="45.7607750" data-lng="-71.6025190">Ham-Sud</option>
359 + <option value="837" data-lat="43.2389146" data-lng="-79.8625362">Hamilton</option>
360 + <option value="1491" data-lat="47.4107810" data-lng="-61.7911045">Havre-aux-Maisons</option>
361 + <option value="848" data-lat="45.6032175" data-lng="-74.6067814">Hawkesbury</option>
362 + <option value="850" data-lat="49.6880184" data-lng="-83.6669552">Hearst</option>
363 + <option value="1496" data-lat="45.1406573" data-lng="-73.2046755">Henryville</option>
364 + <option value="1501" data-lat="45.1866946" data-lng="-73.8489239">Howick</option>
365 + <option value="1493" data-lat="48.3862070" data-lng="-71.6787100">Hébertville</option>
366 + <option value="1497" data-lat="46.6659150" data-lng="-72.6243220">Hérouxville</option>
367 + <option value="1504" data-lat="46.2680883" data-lng="-71.5357843">Inverness</option>
368 + <option value="1505" data-lat="46.0718713" data-lng="-71.4852918">Irlande</option>
369 + <option value="1507" data-lat="46.0280925" data-lng="-73.4376712">Joliette</option>
370 + <option value="11033" data-lat="45.3843419" data-lng="-73.6619489">Kahnawake</option>
371 + <option value="1509" data-lat="47.5841717" data-lng="-69.8443166">Kamouraska</option>
372 + <option value="894" data-lat="44.2434424" data-lng="-76.5226631">Kingston</option>
373 + <option value="4606" data-lat="46.2164212" data-lng="-71.3772792">Kinnear&#039;s Mills</option>
374 + <option value="1517" data-lat="45.2193223" data-lng="-72.5119025">Knowlton</option>
375 + <option value="1523" data-lat="48.1970350" data-lng="-70.2456914">L&#039;Anse-Saint-Jean</option>
376 + <option value="1527" data-lat="45.8459979" data-lng="-73.4264119">L&#039;Assomption</option>
377 + <option value="1532" data-lat="45.3911091" data-lng="-73.9544411">L&#039;Île-Perrot</option>
378 + <option value="1538" data-lat="47.4023980" data-lng="-70.3511180">La Baleine</option>
379 + <option value="1544" data-lat="45.8364561" data-lng="-70.8252463">La Guadeloupe</option>
380 + <option value="1547" data-lat="47.6653145" data-lng="-70.1454915">La Malbaie</option>
381 + <option value="1550" data-lat="45.4018576" data-lng="-71.2496433">La Patrie</option>
382 + <option value="1552" data-lat="45.4110393" data-lng="-73.4846150">La Prairie</option>
383 + <option value="62890" data-lat="46.3733905" data-lng="-75.9791843">La Vallée-de-la-Gatineau</option>
384 + <option value="1575" data-lat="46.4091200" data-lng="-70.5184101">Lac-Etchemin</option>
385 + <option value="1586" data-lat="45.6524545" data-lng="-74.3421741">Lachute</option>
386 + <option value="1587" data-lat="45.0819794" data-lng="-73.3728638">Lacolle</option>
387 + <option value="1591" data-lat="45.8406595" data-lng="-71.0890802">Lambton</option>
388 + <option value="1597" data-lat="46.5405777" data-lng="-71.6292103">Laurier-Station</option>
389 + <option value="1599" data-lat="45.5822347" data-lng="-73.7424071">Laval</option>
390 + <option value="1600" data-lat="45.8932543" data-lng="-73.2981718">Lavaltrie</option>
391 + <option value="1612" data-lat="45.2699234" data-lng="-74.2223732">Les Coteaux</option>
392 + <option value="1614" data-lat="48.3477660" data-lng="-69.4057350">Les Escoumins</option>
393 + <option value="46586" data-lat="46.1237226" data-lng="-74.4281294">Les Laurentides</option>
394 + <option value="924" data-lat="42.9852367" data-lng="-81.2601680">London</option>
395 + <option value="7402" data-lat="42.9876872" data-lng="-81.2582658">London</option>
396 + <option value="2" data-lat="45.5138211" data-lng="-73.4605916">Longueuil</option>
397 + <option value="1621" data-lat="45.6698195" data-lng="-73.7837257">Lorraine</option>
398 + <option value="1623" data-lat="46.5820511" data-lng="-71.8140340">Lotbinière</option>
399 + <option value="1625" data-lat="46.2553526" data-lng="-72.9456981">Louiseville</option>
400 + <option value="1618" data-lat="46.7560941" data-lng="-71.2114097">Lévis</option>
401 + <option value="1634" data-lat="45.2700521" data-lng="-72.1471475">Magog</option>
402 + <option value="1635" data-lat="48.1342248" data-lng="-78.1121550">Malartic</option>
403 + <option value="1637" data-lat="46.3784488" data-lng="-75.9764101">Maniwaki</option>
404 + <option value="1648" data-lat="45.7449195" data-lng="-73.6278075">Mascouche</option>
405 + <option value="1650" data-lat="46.3389419" data-lng="-72.9756818">Maskinongé</option>
406 + <option value="1651" data-lat="45.9117740" data-lng="-72.9282527">Massueville</option>
407 + <option value="1653" data-lat="48.8420210" data-lng="-67.5178219">Matane</option>
408 + <option value="948" data-lat="45.2886749" data-lng="-74.8545436">Maxville</option>
409 + <option value="1656" data-lat="45.3163313" data-lng="-73.7414192">Mercier</option>
410 + <option value="1662" data-lat="45.6724876" data-lng="-74.0078731">Mirabel</option>
411 + <option value="66656" data-lat="47.0084346" data-lng="-65.5547822">Miramichi</option>
412 + <option value="964" data-lat="43.5776709" data-lng="-79.6659499">Mississauga</option>
413 + <option value="396" data-lat="46.1072541" data-lng="-64.8037015">Moncton</option>
414 + <option value="1670" data-lat="45.3339528" data-lng="-73.1533651">Mont-Saint-Grégoire</option>
415 + <option value="1674" data-lat="46.1411998" data-lng="-74.5965680">Mont-Tremblant</option>
416 + <option value="1676" data-lat="45.6519493" data-lng="-74.9408715">Montebello</option>
417 + <option value="1677" data-lat="46.9793422" data-lng="-70.5558763">Montmagny</option>
418 + <option value="11" selected data-lat="45.5318396" data-lng="-73.6114472">Montréal</option>
419 + <option value="71021" data-lat="45.5043932" data-lng="-73.3820059">Montérégie</option>
420 + <option value="70040" data-lat="49.3436997" data-lng="-82.1603359">Moonbeam</option>
421 + <option value="1682" data-lat="45.9043849" data-lng="-74.2433976">Morin-Heights</option>
422 + <option value="1685" data-lat="45.1880677" data-lng="-73.4059914">Napierville</option>
423 + <option value="1687" data-lat="46.7004923" data-lng="-71.5903420">Neuville</option>
424 + <option value="55406" data-lat="48.0064225" data-lng="-69.3776599">Notre-Dame-des-Sept-Douleurs</option>
425 + <option value="1710" data-lat="46.0032410" data-lng="-72.3405090">Notre-Dame-du-Bon-Conseil</option>
426 + <option value="1712" data-lat="46.4879993" data-lng="-72.6565468">Notre-Dame-du-Mont-Carmel</option>
427 + <option value="1714" data-lat="47.7630291" data-lng="-69.6095775">Notre-Dame-du-Portage</option>
428 + <option value="1715" data-lat="48.1368641" data-lng="-66.3120732">Nouvelle</option>
429 + <option value="11083" data-lat="45.5396811" data-lng="-73.9586658">Oka</option>
430 + <option value="51190" data-lat="43.9228965" data-lng="-78.8662300">Oshawa</option>
431 + <option value="1032" data-lat="45.3834795" data-lng="-75.6884291">Ottawa</option>
432 + <option value="4349" data-lat="45.3962279" data-lng="-75.6777978">Ottawa</option>
433 + <option value="1720" data-lat="45.5405866" data-lng="-73.2058369">Otterburn Park</option>
434 + <option value="79995" data-lat="45.5193725" data-lng="-73.6146967">Outremont</option>
435 + <option value="1726" data-lat="45.6192843" data-lng="-75.0190977">Papineauville</option>
436 + <option value="1728" data-lat="48.5195565" data-lng="-64.2348171">Percé</option>
437 + <option value="410" data-lat="47.7882334" data-lng="-65.7243180">Petit-Rocher</option>
438 + <option value="7241" data-lat="48.0560961" data-lng="-70.0400300">Petit-Saguenay</option>
439 + <option value="1733" data-lat="45.8902442" data-lng="-74.1333124">Piedmont</option>
440 + <option value="1060" data-lat="45.5351150" data-lng="-74.9988470">Plantagenet</option>
441 + <option value="1739" data-lat="46.2198485" data-lng="-71.7732534">Plessisville</option>
442 + <option value="1745" data-lat="45.4526171" data-lng="-73.8108181">Pointe-Claire</option>
443 + <option value="1746" data-lat="45.3306092" data-lng="-73.9695432">Pointe-des-Cascades</option>
444 + <option value="1749" data-lat="46.7605444" data-lng="-71.6870776">Pont-Rouge</option>
445 + <option value="1758" data-lat="48.6022432" data-lng="-68.1251489">Price</option>
446 + <option value="1759" data-lat="46.1704574" data-lng="-71.8805424">Princeville</option>
447 + <option value="1757" data-lat="45.8686238" data-lng="-74.0742034">Prévost</option>
448 + <option value="1729" data-lat="48.7665360" data-lng="-72.0533700">Péribonka</option>
449 + <option value="1" data-lat="46.8330697" data-lng="-71.2725725">Québec</option>
450 + <option value="1767" data-lat="46.0517475" data-lng="-73.7184317">Rawdon</option>
451 + <option value="65869" data-lat="44.0767258" data-lng="-79.5152216">Regional Municipality of York</option>
452 + <option value="985" data-lat="45.7537013" data-lng="-73.4517998">Repentigny</option>
453 + <option value="1771" data-lat="48.4393192" data-lng="-68.5208882">Rimouski</option>
454 + <option value="1772" data-lat="45.7858639" data-lng="-75.0988670">Ripon</option>
455 + <option value="1782" data-lat="47.4328950" data-lng="-70.0174250">Rivière-Ouelle</option>
456 + <option value="1784" data-lat="46.4619455" data-lng="-74.9306486">Rivière-Rouge</option>
457 + <option value="1785" data-lat="48.0886193" data-lng="-69.1945082">Rivière-Trois-Pistoles</option>
458 + <option value="1778" data-lat="47.8300622" data-lng="-69.5313232">Rivière-du-Loup</option>
459 + <option value="1786" data-lat="48.5137600" data-lng="-72.2279629">Roberval</option>
460 + <option value="1789" data-lat="45.6384019" data-lng="-73.7959847">Rosemère</option>
461 + <option value="1790" data-lat="45.4332666" data-lng="-73.0503665">Rougemont</option>
462 + <option value="1793" data-lat="48.2327645" data-lng="-79.0210492">Rouyn-Noranda</option>
463 + <option value="1798" data-lat="48.2519501" data-lng="-69.8654972">Sacré-Coeur</option>
464 + <option value="1799" data-lat="48.4093491" data-lng="-71.1157639">Saguenay</option>
465 + <option value="80663" data-lat="48.3873164" data-lng="-71.0751233">Saguenay–Lac-Saint-Jean</option>
466 + <option value="1806" data-lat="46.5592181" data-lng="-71.4353770">Saint-Agapit</option>
467 + <option value="174" data-lat="53.6427562" data-lng="-113.6245381">Saint-Albert</option>
468 + <option value="1811" data-lat="47.6833056" data-lng="-69.6249779">Saint-Alexandre-de-Kamouraska</option>
469 + <option value="1818" data-lat="46.1871961" data-lng="-73.6861803">Saint-Alphonse-Rodriguez</option>
470 + <option value="1822" data-lat="48.4775660" data-lng="-68.4229830">Saint-Anaclet</option>
471 + <option value="7243" data-lat="45.7271348" data-lng="-75.0531834">Saint-André-Avellin</option>
472 + <option value="74362" data-lat="47.6759480" data-lng="-69.7248480">Saint-André-de-Kamouraska</option>
473 + <option value="1828" data-lat="46.6303479" data-lng="-70.9818967">Saint-Anselme</option>
474 + <option value="1831" data-lat="45.8004713" data-lng="-73.1988211">Saint-Antoine-sur-Richelieu</option>
475 + <option value="1848" data-lat="47.4202260" data-lng="-70.3826640">Saint-Bernard-sur-Mer</option>
476 + <option value="1849" data-lat="45.2112670" data-lng="-73.2873490">Saint-Blaise-sur-Richelieu</option>
477 + <option value="1855" data-lat="45.5279410" data-lng="-73.3431905">Saint-Bruno-de-Montarville</option>
478 + <option value="46579" data-lat="45.9497907" data-lng="-73.8495832">Saint-Calixte-de-Kilkenny</option>
479 + <option value="1857" data-lat="45.6768667" data-lng="-71.7079408">Saint-Camille</option>
480 + <option value="1859" data-lat="46.6559201" data-lng="-72.1406839">Saint-Casimir</option>
481 + <option value="1862" data-lat="46.0465570" data-lng="-73.4627906">Saint-Charles-Borromée</option>
482 + <option value="1869" data-lat="45.1024875" data-lng="-73.7606125">Saint-Chrysostome</option>
483 + <option value="1872" data-lat="46.2280500" data-lng="-73.4079630">Saint-Cléophas-de-Brandon</option>
484 + <option value="1877" data-lat="45.3752507" data-lng="-73.5783276">Saint-Constant</option>
485 + <option value="1881" data-lat="45.9217003" data-lng="-72.4332066">Saint-Cyrille-de-Wendover</option>
486 + <option value="1861" data-lat="45.4086703" data-lng="-72.9993415">Saint-Césaire</option>
487 + <option value="1882" data-lat="46.3053507" data-lng="-71.7118898">Saint-Damase</option>
488 + <option value="1885" data-lat="46.6356948" data-lng="-70.6839702">Saint-Damien-de-Buckland</option>
489 + <option value="1887" data-lat="48.6208650" data-lng="-71.1111360">Saint-David-de-Falardeau</option>
490 + <option value="1895" data-lat="46.3352794" data-lng="-74.2433144">Saint-Donat-de-Montcalm</option>
491 + <option value="1903" data-lat="46.3996233" data-lng="-71.0748590">Saint-Elzéar</option>
492 + <option value="46584" data-lat="45.4996514" data-lng="-74.4667798">Saint-Eugène</option>
493 + <option value="1916" data-lat="45.5702989" data-lng="-73.8950295">Saint-Eustache</option>
494 + <option value="1918" data-lat="48.2983236" data-lng="-68.8534651">Saint-Fabien</option>
495 + <option value="1928" data-lat="46.5156507" data-lng="-71.5594976">Saint-Flavien</option>
496 + <option value="1921" data-lat="48.6576401" data-lng="-72.4428984">Saint-Félicien</option>
497 + <option value="1922" data-lat="48.3045868" data-lng="-70.6248283">Saint-Félix-d&#039;Otis</option>
498 + <option value="1924" data-lat="45.7943290" data-lng="-72.1903880">Saint-Félix-de-Kingsey</option>
499 + <option value="1925" data-lat="46.1684180" data-lng="-73.4290510">Saint-Félix-de-Valois</option>
500 + <option value="1939" data-lat="46.2666670" data-lng="-73.3833329">Saint-Gabriel-de-Brandon</option>
501 + <option value="1944" data-lat="46.1167543" data-lng="-70.6713486">Saint-Georges</option>
502 + <option value="1942" data-lat="48.4951560" data-lng="-71.7759180">Saint-Gédéon</option>
503 + <option value="1957" data-lat="48.5339089" data-lng="-71.0861345">Saint-Honoré</option>
504 + <option value="1958" data-lat="45.9507691" data-lng="-70.8026365">Saint-Honoré-de-Shenley</option>
505 + <option value="1962" data-lat="45.6256303" data-lng="-72.9508295">Saint-Hyacinthe</option>
506 + <option value="1969" data-lat="45.9494099" data-lng="-73.5712065">Saint-Jacques</option>
507 + <option value="44017" data-lat="46.2858006" data-lng="-71.3471963">Saint-Jacques-de-Leeds</option>
508 + <option value="1981" data-lat="45.3142262" data-lng="-73.2645077">Saint-Jean-sur-Richelieu</option>
509 + <option value="1986" data-lat="46.3093057" data-lng="-70.8767576">Saint-Joseph-de-Beauce</option>
510 + <option value="1982" data-lat="45.7837935" data-lng="-74.0094466">Saint-Jérôme</option>
511 + <option value="1997" data-lat="45.4965507" data-lng="-73.5043861">Saint-Lambert</option>
512 + <option value="1998" data-lat="46.5886146" data-lng="-71.2198682">Saint-Lambert-de-Lauzon</option>
513 + <option value="1999" data-lat="46.8672154" data-lng="-71.0095410">Saint-Laurent-de-l&#039;Île-d&#039;Orléans</option>
514 + <option value="2001" data-lat="46.6554924" data-lng="-70.7941627">Saint-Lazare-de-Bellechasse</option>
515 + <option value="2010" data-lat="45.8539097" data-lng="-73.7639545">Saint-Lin - Laurentides</option>
516 + <option value="2034" data-lat="48.1830809" data-lng="-68.9727623">Saint-Mathieu-de-Rioux</option>
517 + <option value="2039" data-lat="46.6751790" data-lng="-73.9167790">Saint-Michel-des-Saints</option>
518 + <option value="2040" data-lat="47.8282122" data-lng="-69.3843690">Saint-Modeste</option>
519 + <option value="2045" data-lat="48.5846649" data-lng="-71.5494932">Saint-Nazaire</option>
520 + <option value="2059" data-lat="46.9313503" data-lng="-69.8081138">Saint-Pamphile</option>
521 + <option value="2060" data-lat="47.5254998" data-lng="-69.7900562">Saint-Pascal</option>
522 + <option value="2062" data-lat="46.0048557" data-lng="-73.4556748">Saint-Paul</option>
523 + <option value="2063" data-lat="45.4287138" data-lng="-72.8778540">Saint-Paul-d&#039;Abbotsford</option>
524 + <option value="450" data-lat="47.5172484" data-lng="-67.3920575">Saint-Quentin</option>
525 + <option value="2083" data-lat="46.7834235" data-lng="-70.7262015">Saint-Raphaël</option>
526 + <option value="2084" data-lat="46.8873693" data-lng="-71.8128803">Saint-Raymond</option>
527 + <option value="2089" data-lat="45.9723414" data-lng="-72.9999972">Saint-Robert</option>
528 + <option value="50856" data-lat="45.8520570" data-lng="-73.6772780">Saint-Roch-Ouest</option>
529 + <option value="2103" data-lat="46.6109075" data-lng="-72.3845431">Saint-Stanislas-de-Champlain</option>
530 + <option value="2111" data-lat="46.0197439" data-lng="-73.3433661">Saint-Thomas</option>
531 + <option value="2113" data-lat="46.7080910" data-lng="-72.1632520">Saint-Thuribe</option>
532 + <option value="2114" data-lat="46.7349340" data-lng="-72.5673243">Saint-Tite</option>
533 + <option value="2125" data-lat="46.1489118" data-lng="-70.9037158">Saint-Victor</option>
534 + <option value="17983" data-lat="48.0366202" data-lng="-69.2070555">Saint-Éloi</option>
535 + <option value="1910" data-lat="46.4337949" data-lng="-72.7824203">Saint-Étienne-des-Grès</option>
536 + <option value="2131" data-lat="45.9554954" data-lng="-74.1389035">Sainte-Adèle</option>
537 + <option value="7247" data-lat="46.3839860" data-lng="-71.4095160">Sainte-Agathe</option>
538 + <option value="2133" data-lat="46.0525217" data-lng="-74.2789218">Sainte-Agathe-des-Monts</option>
539 + <option value="2137" data-lat="47.0226865" data-lng="-70.9532689">Sainte-Anne-de-Beaupré</option>
540 + <option value="2142" data-lat="45.8656886" data-lng="-74.1219339">Sainte-Anne-des-Lacs</option>
541 + <option value="2143" data-lat="49.1277020" data-lng="-66.4755325">Sainte-Anne-des-Monts</option>
542 + <option value="2144" data-lat="45.7614399" data-lng="-73.8138808">Sainte-Anne-des-Plaines</option>
543 + <option value="2146" data-lat="46.8264134" data-lng="-70.2111036">Sainte-Apolline-de-Patton</option>
544 + <option value="2153" data-lat="45.3965140" data-lng="-73.5637709">Sainte-Catherine</option>
545 + <option value="2164" data-lat="45.1536621" data-lng="-73.6642719">Sainte-Clotilde-de-Châteauguay</option>
546 + <option value="2173" data-lat="48.6108810" data-lng="-68.2302840">Sainte-Flavie</option>
547 + <option value="2176" data-lat="46.5296048" data-lng="-72.3393038">Sainte-Geneviève-de-Batiscan</option>
548 + <option value="2187" data-lat="45.5840065" data-lng="-73.3338083">Sainte-Julie</option>
549 + <option value="2192" data-lat="48.5303699" data-lng="-68.3429591">Sainte-Luce</option>
550 + <option value="2195" data-lat="45.5952810" data-lng="-73.0956770">Sainte-Madeleine</option>
551 + <option value="2196" data-lat="46.1168683" data-lng="-73.5982296">Sainte-Marcelline-de-Kildare</option>
552 + <option value="2198" data-lat="46.0404028" data-lng="-74.0665215">Sainte-Marguerite-du-Lac-Masson</option>
553 + <option value="2200" data-lat="46.4462165" data-lng="-71.0240314">Sainte-Marie</option>
554 + <option value="2203" data-lat="45.4047042" data-lng="-74.2735182">Sainte-Marthe</option>
555 + <option value="2204" data-lat="45.5290408" data-lng="-73.9334969">Sainte-Marthe-sur-le-Lac</option>
556 + <option value="2209" data-lat="46.8533027" data-lng="-71.1326291">Sainte-Pétronille</option>
557 + <option value="2217" data-lat="46.1548840" data-lng="-71.7142336">Sainte-Sophie-d&#039;Halifax</option>
558 + <option value="2220" data-lat="45.6435983" data-lng="-73.8438121">Sainte-Thérèse</option>
559 + <option value="46575" data-lat="45.9238430" data-lng="-72.0771150">Sainte-Élizabeth-de-Warwick</option>
560 + <option value="2226" data-lat="45.2599739" data-lng="-74.1216253">Salaberry-de-Valleyfield</option>
561 + <option value="2227" data-lat="48.5664854" data-lng="-67.6860637">Sayabec</option>
562 + <option value="2229" data-lat="46.5092450" data-lng="-71.0713552">Scott</option>
563 + <option value="2232" data-lat="50.2240723" data-lng="-66.3780393">Sept-Îles</option>
564 + <option value="2234" data-lat="46.5708421" data-lng="-72.7299907">Shawinigan</option>
565 + <option value="2236" data-lat="45.6036630" data-lng="-76.4914639">Shawville</option>
566 + <option value="2237" data-lat="45.3615863" data-lng="-72.6008993">Shefford</option>
567 + <option value="2238" data-lat="45.3967932" data-lng="-71.9134557">Sherbrooke</option>
568 + <option value="460" data-lat="47.7405275" data-lng="-64.7178342">Shippagan</option>
569 + <option value="2241" data-lat="46.0272625" data-lng="-73.1203332">Sorel-Tracy</option>
570 + <option value="1145" data-lat="45.4582170" data-lng="-74.8000490">St-Bernardin</option>
571 + <option value="84363" data-lat="46.4154513" data-lng="-63.8032447">Summerside</option>
572 + <option value="2254" data-lat="45.1066910" data-lng="-72.6128042">Sutton</option>
573 + <option value="2255" data-lat="48.1690345" data-lng="-69.6954523">Tadoussac</option>
574 + <option value="2260" data-lat="45.7132887" data-lng="-73.6605767">Terrebonne</option>
575 + <option value="2263" data-lat="46.0987377" data-lng="-71.3002027">Thetford Mines</option>
576 + <option value="1183" data-lat="48.4181356" data-lng="-89.2588242">Thunder Bay</option>
577 + <option value="1190" data-lat="43.6935695" data-lng="-79.3974755">Toronto</option>
578 + <option value="2270" data-lat="48.1192674" data-lng="-69.1652622">Trois-Pistoles</option>
579 + <option value="2272" data-lat="46.3568941" data-lng="-72.5708691">Trois-Rivières</option>
580 + <option value="2258" data-lat="47.6431327" data-lng="-68.8750912">Témiscouata-sur-le-Lac</option>
581 + <option value="2273" data-lat="45.7219980" data-lng="-72.2335740">Ulverton</option>
582 + <option value="2275" data-lat="46.4150934" data-lng="-71.7528187">Val-Alain</option>
583 + <option value="2278" data-lat="46.0314326" data-lng="-74.2054144">Val-David</option>
584 + <option value="2277" data-lat="48.0970199" data-lng="-77.7903263">Val-d&#039;Or</option>
585 + <option value="26567" data-lat="45.9151710" data-lng="-75.5986439">Val-des-Bois</option>
586 + <option value="64303" data-lat="45.7738063" data-lng="-71.9321380">Val-des-Sources</option>
587 + <option value="2285" data-lat="46.3803939" data-lng="-70.9242421">Vallée-Jonction</option>
588 + <option value="273" data-lat="49.2597467" data-lng="-123.1179443">Vancouver</option>
589 + <option value="2286" data-lat="45.6770020" data-lng="-73.4283482">Varennes</option>
590 + <option value="49016" data-lat="43.8154087" data-lng="-79.5286718">Vaughan</option>
591 + <option value="2290" data-lat="45.7733380" data-lng="-73.3527469">Verchères</option>
592 + <option value="2291" data-lat="46.0573240" data-lng="-71.9592935">Victoriaville</option>
593 + <option value="2300" data-lat="45.3439685" data-lng="-72.5167002">Waterloo</option>
594 + <option value="4725" data-lat="47.9025860" data-lng="-73.7863430">Wemotaci</option>
595 + <option value="2309" data-lat="45.5602770" data-lng="-72.0383484">Windsor</option>
596 + <option value="310" data-lat="49.8727303" data-lng="-97.1475289">Winnipeg</option>
597 + <option value="2313" data-lat="46.3202261" data-lng="-72.8121883">Yamachiche</option>
598 + </select>
599 + </div>
600 + <div class="range-wrapper">
601 + <label for="search-advanced-locality-radius" class="visuallyhidden">Distance</label>
602 + <input id="search-advanced-locality-radius" type="range" name="radius" min="0" max="500" step="10" value="100" data-postfix=" km">
603 + </div>
604 + <div id="search-advanced-locality-map-folder" class="map-folder js-folder">
605 + <button class="folder-trigger js-folder-trigger">Situer sur la carte</button>
606 + <div class="folder-content js-folder-content">
607 + <div id="search-advanced-locality-map" class="canvas" style="height:600px;max-width:100%;width:450;"></div>
608 + </div>
609 + </div>
610 + </fieldset>
611 + <fieldset id="search-advanced-dates">
612 + <legend>Dates</legend>
613 + <div class="checkbox-wrapper">
614 + <input id="search-advanced-dates-week" type="checkbox" name="dates" value="week">
615 + <label for="search-advanced-dates-week">Cette semaine</label>
616 + </div>
617 + <div class="checkbox-wrapper">
618 + <input id="search-advanced-dates-weekend" type="checkbox" name="dates" value="weekend">
619 + <label for="search-advanced-dates-weekend">Ce week-end</label>
620 + </div>
621 + <div class="checkbox-wrapper">
622 + <input id="search-advanced-dates-month" type="checkbox" name="dates" value="month">
623 + <label for="search-advanced-dates-month">Ce mois-ci</label>
624 + </div>
625 + <div class="datepicker-folder js-folder">
626 + <div class="checkbox-wrapper">
627 + <input id="search-advanced-dates-range" type="checkbox" name="dates" value="range">
628 + <label for="search-advanced-dates-range">Période personnalisée</label>
629 + <button id="search-advanced-dates-range-trigger" class="js-folder-trigger visuallyhidden"></button>
630 + </div>
631 + <div class="input-daterange input-group folder-content js-folder-content">
632 + <div class="datepicker-wrapper">
633 + <label for="search-advanced-dates-range-start">Début</label>
634 + <input id="search-advanced-dates-range-start" type="text" name="start" readonly disabled>
635 + </div>
636 + <div class="datepicker-wrapper">
637 + <label for="search-advanced-dates-range-end">Fin</label>
638 + <input id="search-advanced-dates-range-end" type="text" name="end" readonly disabled>
639 + </div>
640 + </div>
641 + </div>
642 + </fieldset>
643 + <fieldset id="search-advanced-categories">
644 + <input id="search-advanced-categories-input" type="hidden" name="categories" value="">
645 + <legend>Catégories</legend>
646 + <ul class="categories">
647 + <li class="categories-item categories-category-literary-arts" data-category="6">
648 + <div class="categories-folder">
649 + <button class="categories-trigger">
650 + <span class="categories-text">Arts littéraires</span>
651 + </button>
652 + <div class="categories-content">
653 + <div class="checkbox-wrapper all">
654 + <input id="search-advanced-categories-6-0" type="checkbox" data-value="0">
655 + <label for="search-advanced-categories-6-0">Toutes les sous-catégories</label>
656 + </div>
657 + <div class="checkbox-wrapper">
658 + <input id="search-advanced-categories-6-44" type="checkbox" data-value="44">
659 + <label for="search-advanced-categories-6-44">Atelier</label>
660 + </div>
661 + <div class="checkbox-wrapper">
662 + <input id="search-advanced-categories-6-85" type="checkbox" data-value="85">
663 + <label for="search-advanced-categories-6-85">Conte</label>
664 + </div>
665 + <div class="checkbox-wrapper">
666 + <input id="search-advanced-categories-6-43" type="checkbox" data-value="43">
667 + <label for="search-advanced-categories-6-43">Lecture</label>
668 + </div>
669 + <div class="checkbox-wrapper">
670 + <input id="search-advanced-categories-6-45" type="checkbox" data-value="45">
671 + <label for="search-advanced-categories-6-45">Séminaire</label>
672 + </div>
673 + </div>
674 + </div>
675 + </li>
676 + <li class="categories-item categories-category-visual-arts" data-category="5">
677 + <div class="categories-folder">
678 + <button class="categories-trigger">
679 + <span class="categories-text">Arts Visuels</span>
680 + </button>
681 + <div class="categories-content">
682 + <div class="checkbox-wrapper all">
683 + <input id="search-advanced-categories-5-0" type="checkbox" data-value="0">
684 + <label for="search-advanced-categories-5-0">Toutes les sous-catégories</label>
685 + </div>
686 + <div class="checkbox-wrapper">
687 + <input id="search-advanced-categories-5-41" type="checkbox" data-value="41">
688 + <label for="search-advanced-categories-5-41">Atelier</label>
689 + </div>
690 + <div class="checkbox-wrapper">
691 + <input id="search-advanced-categories-5-38" type="checkbox" data-value="38">
692 + <label for="search-advanced-categories-5-38">Exposition</label>
693 + </div>
694 + <div class="checkbox-wrapper">
695 + <input id="search-advanced-categories-5-76" type="checkbox" data-value="76">
696 + <label for="search-advanced-categories-5-76">Mode</label>
697 + </div>
698 + <div class="checkbox-wrapper">
699 + <input id="search-advanced-categories-5-40" type="checkbox" data-value="40">
700 + <label for="search-advanced-categories-5-40">Peinture</label>
701 + </div>
702 + <div class="checkbox-wrapper">
703 + <input id="search-advanced-categories-5-39" type="checkbox" data-value="39">
704 + <label for="search-advanced-categories-5-39">Projection de film</label>
705 + </div>
706 + <div class="checkbox-wrapper">
707 + <input id="search-advanced-categories-5-42" type="checkbox" data-value="42">
708 + <label for="search-advanced-categories-5-42">Séminaire</label>
709 + </div>
710 + </div>
711 + </div>
712 + </li>
713 + <li class="categories-item categories-category-conventions-and-exhibitions" data-category="8">
714 + <div class="categories-folder">
715 + <button class="categories-trigger">
716 + <span class="categories-text">Congrès et expositions</span>
717 + </button>
718 + <div class="categories-content">
719 + <div class="checkbox-wrapper all">
720 + <input id="search-advanced-categories-8-0" type="checkbox" data-value="0">
721 + <label for="search-advanced-categories-8-0">Toutes les sous-catégories</label>
722 + </div>
723 + <div class="checkbox-wrapper">
724 + <input id="search-advanced-categories-8-54" type="checkbox" data-value="54">
725 + <label for="search-advanced-categories-8-54">Commercial</label>
726 + </div>
727 + <div class="checkbox-wrapper">
728 + <input id="search-advanced-categories-8-51" type="checkbox" data-value="51">
729 + <label for="search-advanced-categories-8-51">Fan</label>
730 + </div>
731 + <div class="checkbox-wrapper">
732 + <input id="search-advanced-categories-8-52" type="checkbox" data-value="52">
733 + <label for="search-advanced-categories-8-52">Politique</label>
734 + </div>
735 + <div class="checkbox-wrapper">
736 + <input id="search-advanced-categories-8-53" type="checkbox" data-value="53">
737 + <label for="search-advanced-categories-8-53">Professionnel</label>
738 + </div>
739 + </div>
740 + </div>
741 + </li>
742 + <li class="categories-item categories-category-courses-and-conferences" data-category="7">
743 + <div class="categories-folder">
744 + <button class="categories-trigger">
745 + <span class="categories-text">Cours et conférences</span>
746 + </button>
747 + <div class="categories-content">
748 + <div class="checkbox-wrapper all">
749 + <input id="search-advanced-categories-7-0" type="checkbox" data-value="0">
750 + <label for="search-advanced-categories-7-0">Toutes les sous-catégories</label>
751 + </div>
752 + <div class="checkbox-wrapper">
753 + <input id="search-advanced-categories-7-46" type="checkbox" data-value="46">
754 + <label for="search-advanced-categories-7-46">Académique</label>
755 + </div>
756 + <div class="checkbox-wrapper">
757 + <input id="search-advanced-categories-7-49" type="checkbox" data-value="49">
758 + <label for="search-advanced-categories-7-49">Commercial</label>
759 + </div>
760 + <div class="checkbox-wrapper">
761 + <input id="search-advanced-categories-7-48" type="checkbox" data-value="48">
762 + <label for="search-advanced-categories-7-48">Conférencier</label>
763 + </div>
764 + <div class="checkbox-wrapper">
765 + <input id="search-advanced-categories-7-47" type="checkbox" data-value="47">
766 + <label for="search-advanced-categories-7-47">Presse</label>
767 + </div>
768 + <div class="checkbox-wrapper">
769 + <input id="search-advanced-categories-7-50" type="checkbox" data-value="50">
770 + <label for="search-advanced-categories-7-50">Professionnel</label>
771 + </div>
772 + </div>
773 + </div>
774 + </li>
775 + <li class="categories-item categories-category-dance" data-category="4">
776 + <div class="categories-folder">
777 + <button class="categories-trigger">
778 + <span class="categories-text">Danse</span>
779 + </button>
780 + <div class="categories-content">
781 + <div class="checkbox-wrapper all">
782 + <input id="search-advanced-categories-4-0" type="checkbox" data-value="0">
783 + <label for="search-advanced-categories-4-0">Toutes les sous-catégories</label>
784 + </div>
785 + <div class="checkbox-wrapper">
786 + <input id="search-advanced-categories-4-30" type="checkbox" data-value="30">
787 + <label for="search-advanced-categories-4-30">Acrobatique</label>
788 + </div>
789 + <div class="checkbox-wrapper">
790 + <input id="search-advanced-categories-4-37" type="checkbox" data-value="37">
791 + <label for="search-advanced-categories-4-37">Atelier</label>
792 + </div>
793 + <div class="checkbox-wrapper">
794 + <input id="search-advanced-categories-4-31" type="checkbox" data-value="31">
795 + <label for="search-advanced-categories-4-31">Ballet</label>
796 + </div>
797 + <div class="checkbox-wrapper">
798 + <input id="search-advanced-categories-4-36" type="checkbox" data-value="36">
799 + <label for="search-advanced-categories-4-36">Compétition</label>
800 + </div>
801 + <div class="checkbox-wrapper">
802 + <input id="search-advanced-categories-4-32" type="checkbox" data-value="32">
803 + <label for="search-advanced-categories-4-32">Contemporaine</label>
804 + </div>
805 + <div class="checkbox-wrapper">
806 + <input id="search-advanced-categories-4-86" type="checkbox" data-value="86">
807 + <label for="search-advanced-categories-4-86">Danse traditionnelle</label>
808 + </div>
809 + <div class="checkbox-wrapper">
810 + <input id="search-advanced-categories-4-79" type="checkbox" data-value="79">
811 + <label for="search-advanced-categories-4-79">Érotisme</label>
812 + </div>
813 + <div class="checkbox-wrapper">
814 + <input id="search-advanced-categories-4-33" type="checkbox" data-value="33">
815 + <label for="search-advanced-categories-4-33">Hip-Hop</label>
816 + </div>
817 + <div class="checkbox-wrapper">
818 + <input id="search-advanced-categories-4-29" type="checkbox" data-value="29">
819 + <label for="search-advanced-categories-4-29">Latine</label>
820 + </div>
821 + <div class="checkbox-wrapper">
822 + <input id="search-advanced-categories-4-34" type="checkbox" data-value="34">
823 + <label for="search-advanced-categories-4-34">Moderne</label>
824 + </div>
825 + <div class="checkbox-wrapper">
826 + <input id="search-advanced-categories-4-35" type="checkbox" data-value="35">
827 + <label for="search-advanced-categories-4-35">Social</label>
828 + </div>
829 + </div>
830 + </div>
831 + </li>
832 + <li class="categories-item categories-category-comedy" data-category="11">
833 + <div class="categories-folder">
834 + <button class="categories-trigger">
835 + <span class="categories-text">Humour</span>
836 + </button>
837 + <div class="categories-content">
838 + <div class="checkbox-wrapper all">
839 + <input id="search-advanced-categories-11-0" type="checkbox" data-value="0">
840 + <label for="search-advanced-categories-11-0">Toutes les sous-catégories</label>
841 + </div>
842 + <div class="checkbox-wrapper">
843 + <input id="search-advanced-categories-11-62" type="checkbox" data-value="62">
844 + <label for="search-advanced-categories-11-62">Improvisation</label>
845 + </div>
846 + <div class="checkbox-wrapper">
847 + <input id="search-advanced-categories-11-63" type="checkbox" data-value="63">
848 + <label for="search-advanced-categories-11-63">Monologue</label>
849 + </div>
850 + <div class="checkbox-wrapper">
851 + <input id="search-advanced-categories-11-65" type="checkbox" data-value="65">
852 + <label for="search-advanced-categories-11-65">Musical</label>
853 + </div>
854 + <div class="checkbox-wrapper">
855 + <input id="search-advanced-categories-11-64" type="checkbox" data-value="64">
856 + <label for="search-advanced-categories-11-64">Pièce de théâtre</label>
857 + </div>
858 + </div>
859 + </div>
860 + </li>
861 + <li class="categories-item categories-category-music" data-category="1">
862 + <div class="categories-folder">
863 + <button class="categories-trigger">
864 + <span class="categories-text">Musique</span>
865 + </button>
866 + <div class="categories-content">
867 + <div class="checkbox-wrapper all">
868 + <input id="search-advanced-categories-1-0" type="checkbox" data-value="0">
869 + <label for="search-advanced-categories-1-0">Toutes les sous-catégories</label>
870 + </div>
871 + <div class="checkbox-wrapper">
872 + <input id="search-advanced-categories-1-1" type="checkbox" data-value="1">
873 + <label for="search-advanced-categories-1-1">Alternatif</label>
874 + </div>
875 + <div class="checkbox-wrapper">
876 + <input id="search-advanced-categories-1-2" type="checkbox" data-value="2">
877 + <label for="search-advanced-categories-1-2">Classique</label>
878 + </div>
879 + <div class="checkbox-wrapper">
880 + <input id="search-advanced-categories-1-4" type="checkbox" data-value="4">
881 + <label for="search-advanced-categories-1-4">Country</label>
882 + </div>
883 + <div class="checkbox-wrapper">
884 + <input id="search-advanced-categories-1-5" type="checkbox" data-value="5">
885 + <label for="search-advanced-categories-1-5">Danse</label>
886 + </div>
887 + <div class="checkbox-wrapper">
888 + <input id="search-advanced-categories-1-6" type="checkbox" data-value="6">
889 + <label for="search-advanced-categories-1-6">Electronique</label>
890 + </div>
891 + <div class="checkbox-wrapper">
892 + <input id="search-advanced-categories-1-15" type="checkbox" data-value="15">
893 + <label for="search-advanced-categories-1-15">Festival</label>
894 + </div>
895 + <div class="checkbox-wrapper">
896 + <input id="search-advanced-categories-1-82" type="checkbox" data-value="82">
897 + <label for="search-advanced-categories-1-82">Francophone</label>
898 + </div>
899 + <div class="checkbox-wrapper">
900 + <input id="search-advanced-categories-1-7" type="checkbox" data-value="7">
901 + <label for="search-advanced-categories-1-7">Hip-Hop/Rap</label>
902 + </div>
903 + <div class="checkbox-wrapper">
904 + <input id="search-advanced-categories-1-3" type="checkbox" data-value="3">
905 + <label for="search-advanced-categories-1-3">Humour</label>
906 + </div>
907 + <div class="checkbox-wrapper">
908 + <input id="search-advanced-categories-1-14" type="checkbox" data-value="14">
909 + <label for="search-advanced-categories-1-14">Indie/Folk</label>
910 + </div>
911 + <div class="checkbox-wrapper">
912 + <input id="search-advanced-categories-1-8" type="checkbox" data-value="8">
913 + <label for="search-advanced-categories-1-8">Jazz/Blues</label>
914 + </div>
915 + <div class="checkbox-wrapper">
916 + <input id="search-advanced-categories-1-9" type="checkbox" data-value="9">
917 + <label for="search-advanced-categories-1-9">Métal</label>
918 + </div>
919 + <div class="checkbox-wrapper">
920 + <input id="search-advanced-categories-1-80" type="checkbox" data-value="80">
921 + <label for="search-advanced-categories-1-80">Monde</label>
922 + </div>
923 + <div class="checkbox-wrapper">
924 + <input id="search-advanced-categories-1-10" type="checkbox" data-value="10">
925 + <label for="search-advanced-categories-1-10">Pop</label>
926 + </div>
927 + <div class="checkbox-wrapper">
928 + <input id="search-advanced-categories-1-11" type="checkbox" data-value="11">
929 + <label for="search-advanced-categories-1-11">Punk</label>
930 + </div>
931 + <div class="checkbox-wrapper">
932 + <input id="search-advanced-categories-1-12" type="checkbox" data-value="12">
933 + <label for="search-advanced-categories-1-12">R&amp;B/Soul</label>
934 + </div>
935 + <div class="checkbox-wrapper">
936 + <input id="search-advanced-categories-1-83" type="checkbox" data-value="83">
937 + <label for="search-advanced-categories-1-83">Reggae/Ska</label>
938 + </div>
939 + <div class="checkbox-wrapper">
940 + <input id="search-advanced-categories-1-13" type="checkbox" data-value="13">
941 + <label for="search-advanced-categories-1-13">Rock</label>
942 + </div>
943 + <div class="checkbox-wrapper">
944 + <input id="search-advanced-categories-1-88" type="checkbox" data-value="88">
945 + <label for="search-advanced-categories-1-88">Traditionnelle</label>
946 + </div>
947 + </div>
948 + </div>
949 + </li>
950 + <li class="categories-item categories-category-food-and-beverages" data-category="9">
951 + <div class="categories-folder">
952 + <button class="categories-trigger">
953 + <span class="categories-text">Nourriture &amp; breuvages</span>
954 + </button>
955 + <div class="categories-content">
956 + <div class="checkbox-wrapper all">
957 + <input id="search-advanced-categories-9-0" type="checkbox" data-value="0">
958 + <label for="search-advanced-categories-9-0">Toutes les sous-catégories</label>
959 + </div>
960 + <div class="checkbox-wrapper">
961 + <input id="search-advanced-categories-9-73" type="checkbox" data-value="73">
962 + <label for="search-advanced-categories-9-73">Atelier</label>
963 + </div>
964 + <div class="checkbox-wrapper">
965 + <input id="search-advanced-categories-9-70" type="checkbox" data-value="70">
966 + <label for="search-advanced-categories-9-70">Collecte de fonds</label>
967 + </div>
968 + <div class="checkbox-wrapper">
969 + <input id="search-advanced-categories-9-72" type="checkbox" data-value="72">
970 + <label for="search-advanced-categories-9-72">Compétition</label>
971 + </div>
972 + <div class="checkbox-wrapper">
973 + <input id="search-advanced-categories-9-75" type="checkbox" data-value="75">
974 + <label for="search-advanced-categories-9-75">Dégustation</label>
975 + </div>
976 + <div class="checkbox-wrapper">
977 + <input id="search-advanced-categories-9-74" type="checkbox" data-value="74">
978 + <label for="search-advanced-categories-9-74">Festival</label>
979 + </div>
980 + <div class="checkbox-wrapper">
981 + <input id="search-advanced-categories-9-71" type="checkbox" data-value="71">
982 + <label for="search-advanced-categories-9-71">Souper-spectacle</label>
983 + </div>
984 + </div>
985 + </div>
986 + </li>
987 + <li class="categories-item categories-category-party" data-category="10">
988 + <div class="categories-folder">
989 + <button class="categories-trigger">
990 + <span class="categories-text">Party</span>
991 + </button>
992 + <div class="categories-content">
993 + <div class="checkbox-wrapper all">
994 + <input id="search-advanced-categories-10-0" type="checkbox" data-value="0">
995 + <label for="search-advanced-categories-10-0">Toutes les sous-catégories</label>
996 + </div>
997 + <div class="checkbox-wrapper">
998 + <input id="search-advanced-categories-10-58" type="checkbox" data-value="58">
999 + <label for="search-advanced-categories-10-58">Carnaval</label>
1000 + </div>
1001 + <div class="checkbox-wrapper">
1002 + <input id="search-advanced-categories-10-60" type="checkbox" data-value="60">
1003 + <label for="search-advanced-categories-10-60">Cocktail</label>
1004 + </div>
1005 + <div class="checkbox-wrapper">
1006 + <input id="search-advanced-categories-10-55" type="checkbox" data-value="55">
1007 + <label for="search-advanced-categories-10-55">Collecte de fonds</label>
1008 + </div>
1009 + <div class="checkbox-wrapper">
1010 + <input id="search-advanced-categories-10-56" type="checkbox" data-value="56">
1011 + <label for="search-advanced-categories-10-56">EDM/Danse</label>
1012 + </div>
1013 + <div class="checkbox-wrapper">
1014 + <input id="search-advanced-categories-10-59" type="checkbox" data-value="59">
1015 + <label for="search-advanced-categories-10-59">Festival</label>
1016 + </div>
1017 + <div class="checkbox-wrapper">
1018 + <input id="search-advanced-categories-10-61" type="checkbox" data-value="61">
1019 + <label for="search-advanced-categories-10-61">Mariage</label>
1020 + </div>
1021 + <div class="checkbox-wrapper">
1022 + <input id="search-advanced-categories-10-57" type="checkbox" data-value="57">
1023 + <label for="search-advanced-categories-10-57">Saisonnier</label>
1024 + </div>
1025 + </div>
1026 + </div>
1027 + </li>
1028 + <li class="categories-item categories-category-politics" data-category="12">
1029 + <div class="categories-folder">
1030 + <button class="categories-trigger">
1031 + <span class="categories-text">Politique</span>
1032 + </button>
1033 + <div class="categories-content">
1034 + <div class="checkbox-wrapper all">
1035 + <input id="search-advanced-categories-12-0" type="checkbox" data-value="0">
1036 + <label for="search-advanced-categories-12-0">Toutes les sous-catégories</label>
1037 + </div>
1038 + <div class="checkbox-wrapper">
1039 + <input id="search-advanced-categories-12-69" type="checkbox" data-value="69">
1040 + <label for="search-advanced-categories-12-69">Cocktail</label>
1041 + </div>
1042 + <div class="checkbox-wrapper">
1043 + <input id="search-advanced-categories-12-66" type="checkbox" data-value="66">
1044 + <label for="search-advanced-categories-12-66">Collecte de fonds</label>
1045 + </div>
1046 + <div class="checkbox-wrapper">
1047 + <input id="search-advanced-categories-12-67" type="checkbox" data-value="67">
1048 + <label for="search-advanced-categories-12-67">Congrès</label>
1049 + </div>
1050 + <div class="checkbox-wrapper">
1051 + <input id="search-advanced-categories-12-68" type="checkbox" data-value="68">
1052 + <label for="search-advanced-categories-12-68">Réunion</label>
1053 + </div>
1054 + </div>
1055 + </div>
1056 + </li>
1057 + <li class="categories-item categories-category-sports" data-category="2">
1058 + <div class="categories-folder">
1059 + <button class="categories-trigger">
1060 + <span class="categories-text">Sports</span>
1061 + </button>
1062 + <div class="categories-content">
1063 + <div class="checkbox-wrapper all">
1064 + <input id="search-advanced-categories-2-0" type="checkbox" data-value="0">
1065 + <label for="search-advanced-categories-2-0">Toutes les sous-catégories</label>
1066 + </div>
1067 + <div class="checkbox-wrapper">
1068 + <input id="search-advanced-categories-2-17" type="checkbox" data-value="17">
1069 + <label for="search-advanced-categories-2-17">Atelier</label>
1070 + </div>
1071 + <div class="checkbox-wrapper">
1072 + <input id="search-advanced-categories-2-21" type="checkbox" data-value="21">
1073 + <label for="search-advanced-categories-2-21">Compétition</label>
1074 + </div>
1075 + <div class="checkbox-wrapper">
1076 + <input id="search-advanced-categories-2-18" type="checkbox" data-value="18">
1077 + <label for="search-advanced-categories-2-18">Course</label>
1078 + </div>
1079 + <div class="checkbox-wrapper">
1080 + <input id="search-advanced-categories-2-81" type="checkbox" data-value="81">
1081 + <label for="search-advanced-categories-2-81">Extrêmes</label>
1082 + </div>
1083 + <div class="checkbox-wrapper">
1084 + <input id="search-advanced-categories-2-84" type="checkbox" data-value="84">
1085 + <label for="search-advanced-categories-2-84">Jeux vidéos</label>
1086 + </div>
1087 + <div class="checkbox-wrapper">
1088 + <input id="search-advanced-categories-2-87" type="checkbox" data-value="87">
1089 + <label for="search-advanced-categories-2-87">Marche</label>
1090 + </div>
1091 + <div class="checkbox-wrapper">
1092 + <input id="search-advanced-categories-2-16" type="checkbox" data-value="16">
1093 + <label for="search-advanced-categories-2-16">Match</label>
1094 + </div>
1095 + <div class="checkbox-wrapper">
1096 + <input id="search-advanced-categories-2-19" type="checkbox" data-value="19">
1097 + <label for="search-advanced-categories-2-19">Spectacle</label>
1098 + </div>
1099 + <div class="checkbox-wrapper">
1100 + <input id="search-advanced-categories-2-20" type="checkbox" data-value="20">
1101 + <label for="search-advanced-categories-2-20">Tournoi</label>
1102 + </div>
1103 + </div>
1104 + </div>
1105 + </li>
1106 + <li class="categories-item categories-category-theatre" data-category="3">
1107 + <div class="categories-folder">
1108 + <button class="categories-trigger">
1109 + <span class="categories-text">Théâtre</span>
1110 + </button>
1111 + <div class="categories-content">
1112 + <div class="checkbox-wrapper all">
1113 + <input id="search-advanced-categories-3-0" type="checkbox" data-value="0">
1114 + <label for="search-advanced-categories-3-0">Toutes les sous-catégories</label>
1115 + </div>
1116 + <div class="checkbox-wrapper">
1117 + <input id="search-advanced-categories-3-78" type="checkbox" data-value="78">
1118 + <label for="search-advanced-categories-3-78">Cirque</label>
1119 + </div>
1120 + <div class="checkbox-wrapper">
1121 + <input id="search-advanced-categories-3-22" type="checkbox" data-value="22">
1122 + <label for="search-advanced-categories-3-22">Comédie</label>
1123 + </div>
1124 + <div class="checkbox-wrapper">
1125 + <input id="search-advanced-categories-3-23" type="checkbox" data-value="23">
1126 + <label for="search-advanced-categories-3-23">Drame</label>
1127 + </div>
1128 + <div class="checkbox-wrapper">
1129 + <input id="search-advanced-categories-3-24" type="checkbox" data-value="24">
1130 + <label for="search-advanced-categories-3-24">Expérimental</label>
1131 + </div>
1132 + <div class="checkbox-wrapper">
1133 + <input id="search-advanced-categories-3-25" type="checkbox" data-value="25">
1134 + <label for="search-advanced-categories-3-25">Fantastique</label>
1135 + </div>
1136 + <div class="checkbox-wrapper">
1137 + <input id="search-advanced-categories-3-26" type="checkbox" data-value="26">
1138 + <label for="search-advanced-categories-3-26">Historique</label>
1139 + </div>
1140 + <div class="checkbox-wrapper">
1141 + <input id="search-advanced-categories-3-27" type="checkbox" data-value="27">
1142 + <label for="search-advanced-categories-3-27">Improvisation</label>
1143 + </div>
1144 + <div class="checkbox-wrapper">
1145 + <input id="search-advanced-categories-3-77" type="checkbox" data-value="77">
1146 + <label for="search-advanced-categories-3-77">Magie</label>
1147 + </div>
1148 + <div class="checkbox-wrapper">
1149 + <input id="search-advanced-categories-3-28" type="checkbox" data-value="28">
1150 + <label for="search-advanced-categories-3-28">Musical</label>
1151 + </div>
1152 + </div>
1153 + </div>
1154 + </li>
1155 + </ul>
1156 + </fieldset>
1157 + <button id="search-advanced-submit" type="submit" class="btn search">Rechercher</button>
1158 + <button id="search-advanced-close" type="button" class="btn-close js-folder-close"><span class="visuallyhidden">Fermer les options de recherche</span></button>
1159 + </form>
1160 + </div>
1161 + </div>
1162 + </div>
1163 + <a id="cart" href="/panier" class="banner-cart" data-items="0" data-turbolinks-permanent></a>
1164 + </div>
1165 + </div>
1166 + </div>
1167 + <main id="main" class="l-main" role="main">
1168 + <div class="l-block-content">
1169 + <h1 class="visuallyhidden">Accueil</h1>
1170 + <section class="l-page-wrapper embed-hidden is-full">
1171 + <h2 class="visuallyhidden">Suggestions</h2>
1172 + <div id="carousel" class="owl-carousel top-slider" data-count="5">
1173 + <article class="item top-slider-slide">
1174 + <a href="/billets/8IB260519001" class="top-slider-link embed-self" data-id="1786">
1175 + <picture><source srcset="https://tpos.s3.amazonaws.com/featured/8/I/B/8IB0012-FR-6a7cd998.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/featured/8/I/B/8IB0012-FR-6a7cd998.png" alt="Mendelssohn voyage!"></picture>
1176 + </a>
1177 + </article>
1178 + <article class="item top-slider-slide">
1179 + <a href="/billets/CEL260829024" class="top-slider-link embed-self" data-id="1784">
1180 + <picture><source srcset="https://tpos.s3.amazonaws.com/featured/C/E/L/CEL0012-FR-6a691c90.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/featured/C/E/L/CEL0012-FR-6a691c90.png" alt="Parcours des Terrasses Gourmandes Grande-Allée 2026"></picture>
1181 + </a>
1182 + </article>
1183 + <article class="item top-slider-slide">
1184 + <a href="/billets/D7P260827001" class="top-slider-link embed-self" data-id="1775">
1185 + <picture><source srcset="https://tpos.s3.amazonaws.com/featured/D/7/P/D7P0073-FR-6a352e94.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/featured/D/7/P/D7P0073-FR-6a352e94.png" alt="Polo &amp; Pan - TOURNÉE AMERICAS 2026"></picture>
1186 + </a>
1187 + </article>
1188 + <article class="item top-slider-slide">
1189 + <a href="/featured/1785" class="top-slider-link embed-self" data-id="1785">
1190 + <picture><source srcset="https://tpos.s3.amazonaws.com/featured/2/N/D/2ND0001-FR-6a6ffe88.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/featured/2/N/D/2ND0001-FR-6a6ffe88.png" alt="Poule pas d&#039;tête"></picture>
1191 + </a>
1192 + </article>
1193 + <article class="item top-slider-slide">
1194 + <a href="/featured/1782" class="top-slider-link embed-self" data-id="1782">
1195 + <picture><source srcset="https://tpos.s3.amazonaws.com/featured/E/N/V/ENV0013-FR-6a47b83e.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/featured/E/N/V/ENV0013-FR-6a47b83e.png" alt="Envol et Macadam 2026"></picture>
1196 + </a>
1197 + </article>
1198 + </div>
1199 + </section>
1200 + <section id="rubrics" class="l-page-wrapper embed-hidden">
1201 + <div class="feature feature-split">
1202 + <div class="feature-row">
1203 + <div class="feature-col">
1204 + <h2>À venir</h2>
1205 + <div class="feature-slider owl-carousel">
1206 + <article class="feature-canvas item">
1207 + <a href="/billets/m4c260910001" class="feature-link embed-self" data-id="538.7.1201">
1208 + <div class="feature-image-wrapper">
1209 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-320x180-6a6ff86e.png, https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-640x360-6a6ff86f.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-640x360-6a6ff86f.png, https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-6a6ff86e.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-320x180-6a6ff86e.png, https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-640x360-6a6ff86f.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-640x360-6a6ff86f.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/M/4/C/498894-1201-6a6ff86e.png" alt="MUSIC 4 CANCER 2026"></picture>
1210 + </div>
1211 + <div class="feature-content">
1212 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>MUSIC 4 CANCER 2026</h3>
1213 + <div class="feature-date">Du 10 au 12 septembre 2026</div>
1214 + <div class="feature-city">Parking Hotel de Ville, Sainte-Thérèse, QC</div>
1215 + </div>
1216 + </a>
1217 + </article>
1218 + <article class="feature-canvas item">
1219 + <a href="/billets/fcl2026all" class="feature-link embed-self" data-id="538.7.1202">
1220 + <div class="feature-image-wrapper">
1221 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-320x180-6a6ff8b9.png, https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-640x360-6a6ff8ba.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-640x360-6a6ff8ba.png, https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-6a6ff8b9.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-320x180-6a6ff8b9.png, https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-640x360-6a6ff8ba.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-640x360-6a6ff8ba.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/F/C/L/15275-1202-6a6ff8b9.png" alt="FESTIVAL COUNTRY LOTBINIÈRE 2026"></picture>
1222 + </div>
1223 + <div class="feature-content">
1224 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>FESTIVAL COUNTRY LOTBINIÈRE 2026</h3>
1225 + <div class="feature-date">Du 27 au 29 août 2026</div>
1226 + <div class="feature-city">Festival country de Lotbinière, Saint-Agapit, QC</div>
1227 + </div>
1228 + </a>
1229 + </article>
1230 + <article class="feature-canvas item">
1231 + <a href="/billets/joat2026" class="feature-link embed-self" data-id="538.7.1203">
1232 + <div class="feature-image-wrapper">
1233 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-320x180-6a6ff93f.png, https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-640x360-6a6ff93f.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-640x360-6a6ff93f.png, https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-6a6ff93e.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-320x180-6a6ff93f.png, https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-640x360-6a6ff93f.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-640x360-6a6ff93f.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/C/L/B/16108-1203-6a6ff93e.png" alt="JOAT 2026"></picture>
1234 + </div>
1235 + <div class="feature-content">
1236 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>JOAT 2026</h3>
1237 + <div class="feature-date">Du 3 au 6 septembre 2026</div>
1238 + <div class="feature-city">Club Soda, Montréal, QC</div>
1239 + </div>
1240 + </a>
1241 + </article>
1242 + <article class="feature-canvas item">
1243 + <a href="/billets/mns260918001" class="feature-link embed-self" data-id="538.7.1200">
1244 + <div class="feature-image-wrapper">
1245 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-320x180-6a6ff82b.png, https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-640x360-6a6ff82b.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-640x360-6a6ff82b.png, https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-6a6ff82a.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-320x180-6a6ff82b.png, https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-640x360-6a6ff82b.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-640x360-6a6ff82b.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/M/N/S/533021-1200-6a6ff82a.png" alt="LA JOURNÉE DE L&#039;ALBUM QUÉBÉCOIS 2026"></picture>
1246 + </div>
1247 + <div class="feature-content">
1248 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>LA JOURNÉE DE L&#039;ALBUM QUÉBÉCOIS 2026</h3>
1249 + <div class="feature-date">18 septembre 2026, 20h00</div>
1250 + <div class="feature-city">Le Ministère, Montréal, QC</div>
1251 + </div>
1252 + </a>
1253 + </article>
1254 + </div>
1255 + </div>
1256 + <div class="feature-col">
1257 + <h2>En vedette - Musique</h2>
1258 + <div class="feature-slider owl-carousel">
1259 + <article class="feature-canvas item">
1260 + <a href="/billets/soeur-boulay-elliot-maginot" class="feature-link embed-self" data-id="538.8.1197">
1261 + <div class="feature-image-wrapper">
1262 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-320x180-6a6ff681.png, https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-640x360-6a6ff681.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-640x360-6a6ff681.png, https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-6a6ff681.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-320x180-6a6ff681.png, https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-640x360-6a6ff681.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-640x360-6a6ff681.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/2/V/8/516101-1197-6a6ff681.png" alt="LES SŒURS BOULAY &amp; ELLIOT MAGINOT"></picture>
1263 + </div>
1264 + <div class="feature-content">
1265 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>LES SŒURS BOULAY &amp; ELLIOT MAGINOT</h3>
1266 + <div class="feature-date">28 août 2026, 20h00</div>
1267 + <div class="feature-city">Espace Culturel Val-des-Sources, Val-des-Sources, QC</div>
1268 + </div>
1269 + </a>
1270 + </article>
1271 + <article class="feature-canvas item">
1272 + <a href="/billets/mti260312002" class="feature-link embed-self" data-id="538.8.1186">
1273 + <div class="feature-image-wrapper">
1274 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-320x180-6a3532fb.png, https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-640x360-6a3532fc.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-640x360-6a3532fc.png, https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-6a3532fb.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-320x180-6a3532fb.png, https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-640x360-6a3532fc.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-640x360-6a3532fc.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/M/T/I/511727-1186-6a3532fb.png" alt="DE FLORE ET HAPPY SUNFLOWER"></picture>
1275 + </div>
1276 + <div class="feature-content">
1277 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>DE FLORE ET HAPPY SUNFLOWER</h3>
1278 + <div class="feature-date">29 août 2026, 20h00</div>
1279 + <div class="feature-city">Minotaure, Gatineau, QC</div>
1280 + </div>
1281 + </a>
1282 + </article>
1283 + <article class="feature-canvas item">
1284 + <a href="/billets/7f9260919001" class="feature-link embed-self" data-id="538.8.1195">
1285 + <div class="feature-image-wrapper">
1286 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-320x180-6a6ff38b.png, https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-640x360-6a6ff38c.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-640x360-6a6ff38c.png, https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-6a6ff38b.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-320x180-6a6ff38b.png, https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-640x360-6a6ff38c.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-640x360-6a6ff38c.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/7/F/9/511898-1195-6a6ff38b.png" alt="MARIE CÉLESTE"></picture>
1287 + </div>
1288 + <div class="feature-content">
1289 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>MARIE CÉLESTE</h3>
1290 + <div class="feature-date">19 septembre 2026, 21h00</div>
1291 + <div class="feature-city">La Baleine Endiablée, Rivière Ouelle, QC</div>
1292 + </div>
1293 + </a>
1294 + </article>
1295 + <article class="feature-canvas item">
1296 + <a href="/billets/ringo-luan" class="feature-link embed-self" data-id="538.8.1196">
1297 + <div class="feature-image-wrapper">
1298 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-320x180-6a6ff3c3.png, https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-640x360-6a6ff3c4.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-640x360-6a6ff3c4.png, https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-6a6ff3c3.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-320x180-6a6ff3c3.png, https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-640x360-6a6ff3c4.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-640x360-6a6ff3c4.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/I/C/B/518129-1196-6a6ff3c3.png" alt="LUAN LAROBINA"></picture>
1299 + </div>
1300 + <div class="feature-content">
1301 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>LUAN LAROBINA</h3>
1302 + <div class="feature-date">22 septembre 2026, 20h00</div>
1303 + <div class="feature-city">Café RINGO Saint-Casimir, Saint-Casimir, QC</div>
1304 + </div>
1305 + </a>
1306 + </article>
1307 + </div>
1308 + </div>
1309 + <div class="feature-col">
1310 + <h2>En vedette - Humour</h2>
1311 + <div class="feature-slider owl-carousel">
1312 + <article class="feature-canvas item">
1313 + <a href="/billets/clo260922001" class="feature-link embed-self" data-id="538.9.1198">
1314 + <div class="feature-image-wrapper">
1315 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-320x180-6a6ff6f1.png, https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-640x360-6a6ff6f1.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-640x360-6a6ff6f1.png, https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-6a6ff6f0.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-320x180-6a6ff6f1.png, https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-640x360-6a6ff6f1.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-640x360-6a6ff6f1.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/C/L/O/530214-1198-6a6ff6f0.png" alt="ROLLY ASSAL"></picture>
1316 + </div>
1317 + <div class="feature-content">
1318 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>ROLLY ASSAL</h3>
1319 + <div class="feature-date">22 septembre 2026, 20h00</div>
1320 + <div class="feature-city">Cabaret Lion d&#039;Or, Montréal, QC</div>
1321 + </div>
1322 + </a>
1323 + </article>
1324 + <article class="feature-canvas item">
1325 + <a href="/billets/erz260819001" class="feature-link embed-self" data-id="538.9.1192">
1326 + <div class="feature-image-wrapper">
1327 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-320x180-6a525d09.png, https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-640x360-6a525d0a.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-640x360-6a525d0a.png, https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-6a525d09.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-320x180-6a525d09.png, https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-640x360-6a525d0a.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-640x360-6a525d0a.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/E/R/Z/517166-1192-6a525d09.png" alt="SUZIE BOUCHARD"></picture>
1328 + </div>
1329 + <div class="feature-content">
1330 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>SUZIE BOUCHARD</h3>
1331 + <div class="feature-date">19 août 2026, 19h00</div>
1332 + <div class="feature-city">Le Terminal Comédie Club, Montréal, QC</div>
1333 + </div>
1334 + </a>
1335 + </article>
1336 + <article class="feature-canvas item">
1337 + <a href="/billets/szg260905001" class="feature-link embed-self" data-id="538.9.1180">
1338 + <div class="feature-image-wrapper">
1339 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-320x180-6a3030b8.png, https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-640x360-6a3030b8.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-640x360-6a3030b8.png, https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-6a3030b7.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-320x180-6a3030b8.png, https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-640x360-6a3030b8.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-640x360-6a3030b8.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/S/Z/G/493707-1180-6a3030b7.png" alt="VIRGINIE FORTIN"></picture>
1340 + </div>
1341 + <div class="feature-content">
1342 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>VIRGINIE FORTIN</h3>
1343 + <div class="feature-date">5 septembre 2026, 20h00</div>
1344 + <div class="feature-city">Salle Fenplast, Longueuil, QC</div>
1345 + </div>
1346 + </a>
1347 + </article>
1348 + <article class="feature-canvas item">
1349 + <a href="/billets/zct260916001" class="feature-link embed-self" data-id="538.9.1199">
1350 + <div class="feature-image-wrapper">
1351 + <picture><source media="(max-width:640px)" srcset="https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-320x180-6a6ff7e5.png, https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-640x360-6a6ff7e6.png 2x"><source media="(max-width:649px)" srcset="https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-640x360-6a6ff7e6.png, https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-6a6ff7e5.png 3x"><source media="(max-width:960px)" srcset="https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-320x180-6a6ff7e5.png, https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-640x360-6a6ff7e6.png 2x"><source srcset="https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-640x360-6a6ff7e6.png"><img itemprop="image" src="https://tpos.s3.amazonaws.com/rubrics/Z/C/T/484052-1199-6a6ff7e5.png" alt="MAXIME GERVAIS"></picture>
1352 + </div>
1353 + <div class="feature-content">
1354 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>MAXIME GERVAIS</h3>
1355 + <div class="feature-date">16 septembre 2026, 20h00</div>
1356 + <div class="feature-city">Le Zaricot, Saint-Hyacinthe, QC</div>
1357 + </div>
1358 + </a>
1359 + </article>
1360 + </div>
1361 + </div>
1362 + </div>
1363 + </div>
1364 + </section>
1365 + <section class="section-b2b">
1366 + <div class="shape-1"></div>
1367 + <div class="shape-2"></div>
1368 + <div class="l-page-wrapper marketing">
1369 + <div class="b2b-cta-container">
1370 + <h2>La création <br class="same-line">de votre événement <br>commence ici</h2>
1371 + <a href="/organisateurs/creer-un-compte" target="_BLANK" class="btn arrow" id="b2b-create-account">Créer un événement</a>
1372 + </div>
1373 + <div class="img-fr"></div>
1374 + </div>
1375 + </section>
1376 + <section id="events" class="l-page-wrapper">
1377 + <h2>Événements à venir</h2>
1378 + <p id="events-search-summary" class="embed-hidden"><span data-param="locality">Dans un rayon de <button type="button" class="link-alike">100 km</button> de <button type="button" class="link-alike search-locality-name">Montreal</button></span><span class="hide-on-mobile"> &bullet; Page 1 de 65</span></p>
1379 + <div id="events-list" class="feature feature-grid">
1380 + <div class="feature-row">
1381 + <div class="feature-col is-small">
1382 + <article class="feature-canvas">
1383 + <a href="/billets/passe2026" class="feature-link embed-self">
1384 + <div class="feature-image-wrapper">
1385 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-320x180-fr-s-6a58c4d8.png, https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-640x360-fr-m-6a58c4d9.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-640x360-fr-m-6a58c4d9.png"><source srcset="https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-320x180-fr-s-6a58c4d8.png, https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-640x360-fr-m-6a58c4d9.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/PK0/26/07/15/001/passe2026-1152x648-fr-6a58c4d8.png" alt="Passe EOW Accès Total - Saison 11"></picture>
1386 + </div>
1387 + <div class="feature-content">
1388 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Passe EOW Accès Total - Saison 11</h3>
1389 + <div class="feature-date">Du 14 août au 5 septembre 2026</div>
1390 + <div class="feature-city">Projet Tyxna, Montréal, QC</div>
1391 + </div>
1392 + </a>
1393 + </article>
1394 + </div>
1395 + <div class="feature-col is-small">
1396 + <article class="feature-canvas">
1397 + <a href="/billets/passesaison2627" class="feature-link embed-self">
1398 + <div class="feature-image-wrapper">
1399 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-320x180-fr-s-6a394e9f.png, https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-640x360-fr-m-6a394ea0.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-640x360-fr-m-6a394ea0.png"><source srcset="https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-320x180-fr-s-6a394e9f.png, https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-640x360-fr-m-6a394ea0.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/9O9/26/08/17/001/9o9260817001-1152x648-fr-6a394e9f.png" alt="Passe de saison TRIADES hockey collégial"></picture>
1400 + </div>
1401 + <div class="feature-content">
1402 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Passe de saison TRIADES hockey collégial</h3>
1403 + <div class="feature-date">17 août 2026, 8h00</div>
1404 + <div class="feature-city">Complexe JC Perreault, Saint-Roch-de-l&#039;Achigan, QC</div>
1405 + </div>
1406 + </a>
1407 + </article>
1408 + </div>
1409 + <div class="feature-col is-small">
1410 + <article class="feature-canvas">
1411 + <a href="/billets/cineastesestries" class="feature-link embed-self">
1412 + <div class="feature-image-wrapper">
1413 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-320x180-fr-s-6a441dcd.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-640x360-fr-m-6a441dcd.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-640x360-fr-m-6a441dcd.png"><source srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-320x180-fr-s-6a441dcd.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-640x360-fr-m-6a441dcd.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UKQ/26/08/17/003/cineastesestries-1152x648-fr-6a441dcc.png" alt="RENCONTRE DES CINÉASTES DE L&#039;ESTRIE"></picture>
1414 + </div>
1415 + <div class="feature-content">
1416 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>RENCONTRE DES CINÉASTES DE L&#039;ESTRIE</h3>
1417 + <div class="feature-date">17 août 2026, 17h00</div>
1418 + <div class="feature-city">THÉÂTRE DE LAC-BROME, Knowlton, QC</div>
1419 + </div>
1420 + </a>
1421 + </article>
1422 + </div>
1423 + <div class="feature-col is-small">
1424 + <article class="feature-canvas has-tag">
1425 + <a href="/billets/v10260817001" class="feature-link embed-self">
1426 + <div class="feature-image-wrapper">
1427 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-320x180-fr-s-694b780f.png, https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-640x360-fr-m-694b780f.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-640x360-fr-m-694b780f.png"><source srcset="https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-320x180-fr-s-694b780f.png, https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-640x360-fr-m-694b780f.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/V10/26/01/19/001/v10260119001-1152x648-fr-694b780e.png" alt="L’Auberge Ludique / The Ludique Inn ( Soirée de jeux - Game Night )"></picture>
1428 + </div>
1429 + <div class="feature-content">
1430 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>L’Auberge Ludique / The Ludique Inn ( Soirée de jeux - Game Night )</h3>
1431 + <div class="feature-date">17 août 2026, 18h00</div>
1432 + <div class="feature-city">La Ferme Kerr, Gore, QC</div>
1433 + <div class="feature-tag">Annulé</div>
1434 + </div>
1435 + </a>
1436 + </article>
1437 + </div>
1438 + <div class="feature-col is-small">
1439 + <article class="feature-canvas">
1440 + <a href="/billets/reseautagetetescreatives" class="feature-link embed-self">
1441 + <div class="feature-image-wrapper">
1442 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-320x180-fr-s-6a729f3d.png, https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-640x360-fr-m-6a729f3d.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-640x360-fr-m-6a729f3d.png"><source srcset="https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-320x180-fr-s-6a729f3d.png, https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-640x360-fr-m-6a729f3d.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/CKT/26/08/17/001/reseautagetetescreatives-1152x648-fr-6a729f3c.png" alt="Le réseautage artistique au féminin"></picture>
1443 + </div>
1444 + <div class="feature-content">
1445 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Le réseautage artistique au féminin</h3>
1446 + <div class="feature-date">17 août 2026, 18h00</div>
1447 + <div class="feature-city">Bar le Jockey, Montréal, QC</div>
1448 + </div>
1449 + </a>
1450 + </article>
1451 + </div>
1452 + <div class="feature-col is-small">
1453 + <article class="feature-canvas">
1454 + <a href="/billets/erz260817001" class="feature-link embed-self">
1455 + <div class="feature-image-wrapper">
1456 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-320x180-fr-s-69333e03.png, https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-640x360-fr-m-69333e03.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-640x360-fr-m-69333e03.png"><source srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-320x180-fr-s-69333e03.png, https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-640x360-fr-m-69333e03.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/ERZ/26/02/09/001/erz260209001-1152x648-fr-69333e03.png" alt="Suzie Bouchard - Répétitive"></picture>
1457 + </div>
1458 + <div class="feature-content">
1459 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Suzie Bouchard - Répétitive</h3>
1460 + <div class="feature-date">17 août 2026, 19h00</div>
1461 + <div class="feature-city">Le Terminal Comédie Club, Montréal, QC</div>
1462 + </div>
1463 + </a>
1464 + </article>
1465 + </div>
1466 + <div class="feature-col is-small">
1467 + <article class="feature-canvas">
1468 + <a href="/billets/steevenclasses" class="feature-link embed-self">
1469 + <div class="feature-image-wrapper">
1470 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-320x180-en-s-6a3b6902.png, https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-640x360-en-m-6a3b6903.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-640x360-en-m-6a3b6903.png"><source srcset="https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-320x180-en-s-6a3b6902.png, https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-640x360-en-m-6a3b6903.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/DFJ/26/06/15/001/dfj260615001-1152x648-en-6a3b6902.png" alt="Haitian Lyrical Dance Classes &amp; Training"></picture>
1471 + </div>
1472 + <div class="feature-content">
1473 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Haitian Lyrical Dance Classes &amp; Training</h3>
1474 + <div class="feature-date">17 et 22 août 2026</div>
1475 + <div class="feature-city">Zeugma Danse, Montreal, QC</div>
1476 + </div>
1477 + </a>
1478 + </article>
1479 + </div>
1480 + <div class="feature-col is-small">
1481 + <article class="feature-canvas">
1482 + <a href="/billets/4ym260817001" class="feature-link embed-self">
1483 + <div class="feature-image-wrapper">
1484 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-320x180-fr-s-6a14725d.png, https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-640x360-fr-m-6a14725d.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-640x360-fr-m-6a14725d.png"><source srcset="https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-320x180-fr-s-6a14725d.png, https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-640x360-fr-m-6a14725d.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/4YM/26/06/01/001/4ym260601001-1152x648-fr-6a14725d.png" alt="Humour Climatisé - Bar Chez Roger (dernière de la saison)"></picture>
1485 + </div>
1486 + <div class="feature-content">
1487 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Humour Climatisé - Bar Chez Roger (dernière de la saison)</h3>
1488 + <div class="feature-date">17 août 2026, 20h00</div>
1489 + <div class="feature-city">BAR CHEZ ROGER, MONTRÉAL, QC</div>
1490 + </div>
1491 + </a>
1492 + </article>
1493 + </div>
1494 + <div class="feature-col is-small">
1495 + <article class="feature-canvas">
1496 + <a href="/billets/improriginale200" class="feature-link embed-self">
1497 + <div class="feature-image-wrapper">
1498 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-320x180-fr-s-6a1f183f.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-640x360-fr-m-6a1f1840.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-640x360-fr-m-6a1f1840.png"><source srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-320x180-fr-s-6a1f183f.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-640x360-fr-m-6a1f1840.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/1NH/26/08/17/001/1nh260817001-1152x648-fr-6a1f183f.png" alt="Impro l&#039;Originale - Bleus et Jaunes (17 août @Aux Angles Ronds)"></picture>
1499 + </div>
1500 + <div class="feature-content">
1501 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Impro l&#039;Originale - Bleus et Jaunes (17 août @Aux Angles Ronds)</h3>
1502 + <div class="feature-date">17 août 2026, 20h00</div>
1503 + <div class="feature-city">Aux Angles Ronds, Montréal, QC</div>
1504 + </div>
1505 + </a>
1506 + </article>
1507 + </div>
1508 + <div class="feature-col is-small">
1509 + <article class="feature-canvas">
1510 + <a href="/billets/tsc260817001" class="feature-link embed-self">
1511 + <div class="feature-image-wrapper">
1512 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-320x180-fr-s-608321aa.png, https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-640x360-fr-m-608321ab.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-640x360-fr-m-608321ab.png"><source srcset="https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-320x180-fr-s-608321aa.png, https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-640x360-fr-m-608321ab.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/TSC/21/05/03/001/tsc210503001-1152x648-fr-608321aa.png" alt="Les Lundis"></picture>
1513 + </div>
1514 + <div class="feature-content">
1515 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Les Lundis</h3>
1516 + <div class="feature-date">17 août 2026, 20h00</div>
1517 + <div class="feature-city">Théâtre Ste-Catherine, Montréal, QC</div>
1518 + </div>
1519 + </a>
1520 + </article>
1521 + </div>
1522 + <div class="feature-col is-small">
1523 + <article class="feature-canvas has-tag">
1524 + <a href="/billets/rdz260817001" class="feature-link embed-self">
1525 + <div class="feature-image-wrapper">
1526 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-320x180-fr-s-5d52ebef.png, https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-640x360-fr-m-5d52ebef.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-640x360-fr-m-5d52ebef.png"><source srcset="https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-320x180-fr-s-5d52ebef.png, https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-640x360-fr-m-5d52ebef.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/RDZ/19/09/02/001/rdz190902001-1152x648-fr-5d52ebee.png" alt="Les Lundis d&#039;rire"></picture>
1527 + </div>
1528 + <div class="feature-content">
1529 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Les Lundis d&#039;rire</h3>
1530 + <div class="feature-date">17 août 2026, 20h00</div>
1531 + <div class="feature-city">Les Insulaires Microbrasseurs, Laval, QC</div>
1532 + <div class="feature-tag">Complet</div>
1533 + </div>
1534 + </a>
1535 + </article>
1536 + </div>
1537 + <div class="feature-col is-small">
1538 + <article class="feature-canvas">
1539 + <a href="/billets/humourauvieuxsthubert" class="feature-link embed-self">
1540 + <div class="feature-image-wrapper">
1541 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-320x180-fr-s-6931efa4.png, https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-640x360-fr-m-6931efa5.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-640x360-fr-m-6931efa5.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-320x180-fr-s-6931efa4.png, https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-640x360-fr-m-6931efa5.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/VO6/humourauvieuxsthubert-1152x648-fr-6931efa4.png" alt="LUNDI HUMOUR au Vieux St-Hubert"></picture>
1542 + </div>
1543 + <div class="feature-content">
1544 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>LUNDI HUMOUR au Vieux St-Hubert</h3>
1545 + <div class="feature-date">Du 17 au 31 août 2026</div>
1546 + <div class="feature-city">Au Vieux Saint-Hub, Montréal, QC</div>
1547 + </div>
1548 + </a>
1549 + </article>
1550 + </div>
1551 + <div class="feature-col is-small">
1552 + <article class="feature-canvas">
1553 + <a href="/billets/demartinobisaillon30minuteschacun" class="feature-link embed-self">
1554 + <div class="feature-image-wrapper">
1555 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-320x180-fr-s-6a6a7551.png, https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-640x360-fr-m-6a6a7552.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-640x360-fr-m-6a6a7552.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-320x180-fr-s-6a6a7551.png, https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-640x360-fr-m-6a6a7552.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/ERZ/demartinobisaillon30minuteschacun-1152x648-fr-6a6a7551.png" alt="De Martino - Bisaillon | 30 minutes chacun"></picture>
1556 + </div>
1557 + <div class="feature-content">
1558 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>De Martino - Bisaillon | 30 minutes chacun</h3>
1559 + <div class="feature-date">17 et 19 août 2026</div>
1560 + <div class="feature-city">Le Terminal Comédie Club, Montréal, QC</div>
1561 + </div>
1562 + </a>
1563 + </article>
1564 + </div>
1565 + <div class="feature-col is-small">
1566 + <article class="feature-canvas">
1567 + <a href="/billets/xc1260817001" class="feature-link embed-self">
1568 + <div class="feature-image-wrapper">
1569 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-320x180-fr-s-6a1e5122.png, https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-640x360-fr-m-6a1e5122.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-640x360-fr-m-6a1e5122.png"><source srcset="https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-320x180-fr-s-6a1e5122.png, https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-640x360-fr-m-6a1e5122.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/XC1/26/06/08/001/xc1260608001-1152x648-fr-6a1e5121.png" alt="Lundi d&#039;l&#039;Humour Gratuit - HUMOUR STAND UP COMÉDIE - rire [LUNDIDLHUMOUR.COM]"></picture>
1570 + </div>
1571 + <div class="feature-content">
1572 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Lundi d&#039;l&#039;Humour Gratuit - HUMOUR STAND UP COMÉDIE - rire [LUNDIDLHUMOUR.COM]</h3>
1573 + <div class="feature-date">17 août 2026, 21h00</div>
1574 + <div class="feature-city">Taverne la chic régal, Montreal, QC</div>
1575 + </div>
1576 + </a>
1577 + </article>
1578 + </div>
1579 + <div class="feature-col is-small">
1580 + <article class="feature-canvas">
1581 + <a href="/billets/comedieclubdernest" class="feature-link embed-self">
1582 + <div class="feature-image-wrapper">
1583 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-320x180-fr-s-6a7a5710.png, https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-640x360-fr-m-6a7a5711.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-640x360-fr-m-6a7a5711.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-320x180-fr-s-6a7a5710.png, https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-640x360-fr-m-6a7a5711.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/L8D/comedieclubdernest-1152x648-fr-6a7a5710.png" alt="Soirée d&#039;Humour | Comédie Club d&#039;Ernest"></picture>
1584 + </div>
1585 + <div class="feature-content">
1586 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Soirée d&#039;Humour | Comédie Club d&#039;Ernest</h3>
1587 + <div class="feature-date">17 et 24 août 2026</div>
1588 + <div class="feature-city">Chez Ernest - Comptoir de curiosités, Montréal, QC</div>
1589 + </div>
1590 + </a>
1591 + </article>
1592 + </div>
1593 + <div class="feature-col is-small">
1594 + <article class="feature-canvas">
1595 + <a href="/billets/8zy260818001" class="feature-link embed-self">
1596 + <div class="feature-image-wrapper">
1597 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-320x180-fr-s-69fb66dd.png, https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-640x360-fr-m-69fb66dd.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-640x360-fr-m-69fb66dd.png"><source srcset="https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-320x180-fr-s-69fb66dd.png, https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-640x360-fr-m-69fb66dd.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/8ZY/26/08/18/001/8zy260818001-1152x648-fr-69fb66dd.png" alt="Club TD MédiaLAB: Lyle le crocodile"></picture>
1598 + </div>
1599 + <div class="feature-content">
1600 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Club TD MédiaLAB: Lyle le crocodile</h3>
1601 + <div class="feature-date">18 août 2026, 10h00</div>
1602 + <div class="feature-city">Bibliothèque Julio-Jean-Pierre, Montréal, QC</div>
1603 + </div>
1604 + </a>
1605 + </article>
1606 + </div>
1607 + <div class="feature-col is-small">
1608 + <article class="feature-canvas">
1609 + <a href="/billets/ffkffpasseport2026" class="feature-link embed-self">
1610 + <div class="feature-image-wrapper">
1611 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-320x180-fr-s-69ef91f1.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-640x360-fr-m-69ef91f1.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-640x360-fr-m-69ef91f1.png"><source srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-320x180-fr-s-69ef91f1.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-640x360-fr-m-69ef91f1.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UKQ/26/08/18/001/ffkffpasseport2026-1152x648-fr-69ef91f1.png" alt="Passeport 2026 - Festival Pass 2026"></picture>
1612 + </div>
1613 + <div class="feature-content">
1614 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Passeport 2026 - Festival Pass 2026</h3>
1615 + <div class="feature-date">Du 18 au 23 août 2026</div>
1616 + <div class="feature-city">THÉÂTRE DE LAC-BROME, Knowlton, QC</div>
1617 + </div>
1618 + </a>
1619 + </article>
1620 + </div>
1621 + <div class="feature-col is-small">
1622 + <article class="feature-canvas">
1623 + <a href="/billets/ashtum-mitshu-aout" class="feature-link embed-self">
1624 + <div class="feature-image-wrapper">
1625 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-320x180-fr-s-67bddaf4.png, https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-640x360-fr-m-67bddaf5.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-640x360-fr-m-67bddaf5.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-320x180-fr-s-67bddaf4.png, https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-640x360-fr-m-67bddaf5.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/SEG/ashtummitshu-1152x648-fr-67bddaf4.png" alt="Ashtum Mitshu / Viens manger avec UTAPI - Suzy Basile"></picture>
1626 + </div>
1627 + <div class="feature-content">
1628 + <h3 class="feature-title"><div class="tag-attendance-online" title="Événement virtuel"></div>Ashtum Mitshu / Viens manger avec UTAPI - Suzy Basile</h3>
1629 + <div class="feature-date">18 août 2026, 12h00</div>
1630 + </div>
1631 + </a>
1632 + </article>
1633 + </div>
1634 + <div class="feature-col is-small">
1635 + <article class="feature-canvas">
1636 + <a href="/billets/party-ouverture-ffkff" class="feature-link embed-self">
1637 + <div class="feature-image-wrapper">
1638 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-320x180-fr-s-6a70fef2.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-640x360-fr-m-6a70fef2.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-640x360-fr-m-6a70fef2.png"><source srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-320x180-fr-s-6a70fef2.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-640x360-fr-m-6a70fef2.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/009/party-ouverture-ffkff-1152x648-fr-6a70fef1.png" alt="Tapis rouge &amp; soirée d&#039;ouverture | FFKFF 2026"></picture>
1639 + </div>
1640 + <div class="feature-content">
1641 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Tapis rouge &amp; soirée d&#039;ouverture | FFKFF 2026</h3>
1642 + <div class="feature-date">18 août 2026, 17h00</div>
1643 + <div class="feature-city">THÉÂTRE DE LAC-BROME, Knowlton, QC</div>
1644 + </div>
1645 + </a>
1646 + </article>
1647 + </div>
1648 + <div class="feature-col is-small">
1649 + <article class="feature-canvas">
1650 + <a href="/billets/32o260818001" class="feature-link embed-self">
1651 + <div class="feature-image-wrapper">
1652 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-320x180-fr-s-6a287f50.png, https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-640x360-fr-m-6a287f51.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-640x360-fr-m-6a287f51.png"><source srcset="https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-320x180-fr-s-6a287f50.png, https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-640x360-fr-m-6a287f51.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/32O/26/07/07/001/32o260707001-1152x648-fr-6a287f4f.png" alt="Famille INSTABLE 2026"></picture>
1653 + </div>
1654 + <div class="feature-content">
1655 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Famille INSTABLE 2026</h3>
1656 + <div class="feature-date">18 août 2026, 17h30</div>
1657 + <div class="feature-city">JIM de L&#039;Instable, Montréal, QC</div>
1658 + </div>
1659 + </a>
1660 + </article>
1661 + </div>
1662 + <div class="feature-col is-small">
1663 + <article class="feature-canvas">
1664 + <a href="/billets/improv20" class="feature-link embed-self">
1665 + <div class="feature-image-wrapper">
1666 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-320x180-fr-s-6a6a886b.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-640x360-fr-m-6a6a886c.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-640x360-fr-m-6a6a886c.png"><source srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-320x180-fr-s-6a6a886b.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-640x360-fr-m-6a6a886c.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/002/1nh260818002-1152x648-fr-6a6a886b.png" alt="Improv The Rambunctious - Yellow vs Red (August 18 - 6:30pm @Aux Angles Ronds)"></picture>
1667 + </div>
1668 + <div class="feature-content">
1669 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Improv The Rambunctious - Yellow vs Red (August 18 - 6:30pm @Aux Angles Ronds)</h3>
1670 + <div class="feature-date">18 août 2026, 18h30</div>
1671 + <div class="feature-city">Aux Angles Ronds, Montréal, QC</div>
1672 + </div>
1673 + </a>
1674 + </article>
1675 + </div>
1676 + <div class="feature-col is-small">
1677 + <article class="feature-canvas has-tag">
1678 + <a href="/billets/125ruedesmalaises-ffkff" class="feature-link embed-self">
1679 + <div class="feature-image-wrapper">
1680 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-320x180-fr-s-6a4fa976.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-640x360-fr-m-6a4fa977.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-640x360-fr-m-6a4fa977.png"><source srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-320x180-fr-s-6a4fa976.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-640x360-fr-m-6a4fa977.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UKQ/26/08/20/007/ukq260820007-1152x648-fr-6a4fa976.png" alt="Soirée d&#039;ouverture du FFKFF | 125, rue des Malaises"></picture>
1681 + </div>
1682 + <div class="feature-content">
1683 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Soirée d&#039;ouverture du FFKFF | 125, rue des Malaises</h3>
1684 + <div class="feature-date">18 août 2026, 18h30</div>
1685 + <div class="feature-city">THÉÂTRE DE LAC-BROME, Knowlton, QC</div>
1686 + <div class="feature-tag">Complet</div>
1687 + </div>
1688 + </a>
1689 + </article>
1690 + </div>
1691 + <div class="feature-col is-small">
1692 + <article class="feature-canvas">
1693 + <a href="/billets/f7p260818001" class="feature-link embed-self">
1694 + <div class="feature-image-wrapper">
1695 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-320x180-fr-s-6a748f1d.png, https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-640x360-fr-m-6a748f1d.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-640x360-fr-m-6a748f1d.png"><source srcset="https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-320x180-fr-s-6a748f1d.png, https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-640x360-fr-m-6a748f1d.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/F7P/26/08/18/001/f7p260818001-1152x648-fr-6a748f1c.png" alt="Mardi Open-Mack"></picture>
1696 + </div>
1697 + <div class="feature-content">
1698 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Mardi Open-Mack</h3>
1699 + <div class="feature-date">18 août 2026, 19h00</div>
1700 + <div class="feature-city">Le Baratineur, Longueuil, QC</div>
1701 + </div>
1702 + </a>
1703 + </article>
1704 + </div>
1705 + <div class="feature-col is-small">
1706 + <article class="feature-canvas">
1707 + <a href="/billets/lacabinecomedieclub" class="feature-link embed-self">
1708 + <div class="feature-image-wrapper">
1709 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-320x180-fr-s-69cd8594.png, https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-640x360-fr-m-69cd8594.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-640x360-fr-m-69cd8594.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-320x180-fr-s-69cd8594.png, https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-640x360-fr-m-69cd8594.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/N6A/lacabinecomedieclub-1152x648-fr-69cd8593.png" alt="La Cabine Comédie Club"></picture>
1710 + </div>
1711 + <div class="feature-content">
1712 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>La Cabine Comédie Club</h3>
1713 + <div class="feature-date">Du 18 août au 20 novembre 2026</div>
1714 + <div class="feature-city">La Cabine Comédie Club, Montréal, QC</div>
1715 + </div>
1716 + </a>
1717 + </article>
1718 + </div>
1719 + <div class="feature-col is-small">
1720 + <article class="feature-canvas">
1721 + <a href="/billets/nouvellevagueetcomedieclub" class="feature-link embed-self">
1722 + <div class="feature-image-wrapper">
1723 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-320x180-fr-s-6a46907f.png, https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-640x360-fr-m-6a46907f.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-640x360-fr-m-6a46907f.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-320x180-fr-s-6a46907f.png, https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-640x360-fr-m-6a46907f.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/N6A/nouvellevagueetcomedieclub-1152x648-fr-6a46907e.png" alt="Nouvelle Vague et Comédie Club"></picture>
1724 + </div>
1725 + <div class="feature-content">
1726 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Nouvelle Vague et Comédie Club</h3>
1727 + <div class="feature-date">Du 18 août au 1er octobre 2026</div>
1728 + <div class="feature-city">La Cabine Comédie Club, Montréal, QC</div>
1729 + </div>
1730 + </a>
1731 + </article>
1732 + </div>
1733 + <div class="feature-col is-small">
1734 + <article class="feature-canvas">
1735 + <a href="/billets/replica-bromont" class="feature-link embed-self">
1736 + <div class="feature-image-wrapper">
1737 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-320x180-fr-s-6a4c0e21.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-640x360-fr-m-6a4c0e22.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-640x360-fr-m-6a4c0e22.png"><source srcset="https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-320x180-fr-s-6a4c0e21.png, https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-640x360-fr-m-6a4c0e22.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UKQ/26/08/23/002/ukq260823002-1152x648-fr-6a4c0e21.png" alt="Replica, de Chouwa Liang | Projection"></picture>
1738 + </div>
1739 + <div class="feature-content">
1740 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Replica, de Chouwa Liang | Projection</h3>
1741 + <div class="feature-date">18 août 2026, 19h00</div>
1742 + <div class="feature-city">Espace culturel St. John, Bromont, QC</div>
1743 + </div>
1744 + </a>
1745 + </article>
1746 + </div>
1747 + <div class="feature-col is-small">
1748 + <article class="feature-canvas">
1749 + <a href="/billets/erm260818001" class="feature-link embed-self">
1750 + <div class="feature-image-wrapper">
1751 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-320x180-fr-s-67f95847.png, https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-640x360-fr-m-67f95847.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-640x360-fr-m-67f95847.png"><source srcset="https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-320x180-fr-s-67f95847.png, https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-640x360-fr-m-67f95847.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/ERM/25/05/24/002/erm250524002-1152x648-fr-67f95846.png" alt="Soirée Stand-Up"></picture>
1752 + </div>
1753 + <div class="feature-content">
1754 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Soirée Stand-Up</h3>
1755 + <div class="feature-date">18 août 2026, 19h00</div>
1756 + <div class="feature-city">Le Terminal Comédie Club, Montréal, QC</div>
1757 + </div>
1758 + </a>
1759 + </article>
1760 + </div>
1761 + <div class="feature-col is-small">
1762 + <article class="feature-canvas">
1763 + <a href="/billets/svarecital" class="feature-link embed-self">
1764 + <div class="feature-image-wrapper">
1765 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-320x180-fr-s-6a8337bc.png, https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-640x360-fr-m-6a8337bc.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-640x360-fr-m-6a8337bc.png"><source srcset="https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-320x180-fr-s-6a8337bc.png, https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-640x360-fr-m-6a8337bc.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/J12/26/08/18/002/svarecital-1152x648-fr-6a8337bb.png" alt="SVA - Série été 2026 - &quot;Jeunes violonistes en scène&quot;"></picture>
1766 + </div>
1767 + <div class="feature-content">
1768 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>SVA - Série été 2026 - &quot;Jeunes violonistes en scène&quot;</h3>
1769 + <div class="feature-date">18 août 2026, 19h00</div>
1770 + <div class="feature-city">Piano Vertu - nouvelle succursale, Montréal, QC</div>
1771 + </div>
1772 + </a>
1773 + </article>
1774 + </div>
1775 + <div class="feature-col is-small">
1776 + <article class="feature-canvas">
1777 + <a href="/billets/bagsboutin" class="feature-link embed-self">
1778 + <div class="feature-image-wrapper">
1779 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-320x180-en-s-6a764db1.png, https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-640x360-en-m-6a764db1.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-640x360-en-m-6a764db1.png"><source srcset="https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-320x180-en-s-6a764db1.png, https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-640x360-en-m-6a764db1.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/N01/26/08/18/001/n01260818001-1152x648-en-6a764db0.png" alt="Bags Trio + Boutin Quartet"></picture>
1780 + </div>
1781 + <div class="feature-content">
1782 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Bags Trio + Boutin Quartet</h3>
1783 + <div class="feature-date">18 août 2026, 19h30</div>
1784 + <div class="feature-city">Brasserie Beaubien, Montréal, QC</div>
1785 + </div>
1786 + </a>
1787 + </article>
1788 + </div>
1789 + <div class="feature-col is-small">
1790 + <article class="feature-canvas">
1791 + <a href="/billets/caouca" class="feature-link embed-self">
1792 + <div class="feature-image-wrapper">
1793 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-320x180-fr-s-68d1cdca.png, https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-640x360-fr-m-68d1cdcb.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-640x360-fr-m-68d1cdcb.png"><source srcset="https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-320x180-fr-s-68d1cdca.png, https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-640x360-fr-m-68d1cdcb.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/event-groups/WSN/caouca-1152x648-fr-68d1cdca.png" alt="Ça ou Ça au Siboire Saint-Laurent"></picture>
1794 + </div>
1795 + <div class="feature-content">
1796 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Ça ou Ça au Siboire Saint-Laurent</h3>
1797 + <div class="feature-date">Du 18 août au 14 septembre 2026</div>
1798 + <div class="feature-city">Siboire Saint-Laurent, Montréal, QC</div>
1799 + </div>
1800 + </a>
1801 + </article>
1802 + </div>
1803 + <div class="feature-col is-small">
1804 + <article class="feature-canvas">
1805 + <a href="/billets/7jz260818001" class="feature-link embed-self">
1806 + <div class="feature-image-wrapper">
1807 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-320x180-fr-s-6a0384bf.png, https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-640x360-fr-m-6a0384bf.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-640x360-fr-m-6a0384bf.png"><source srcset="https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-320x180-fr-s-6a0384bf.png, https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-640x360-fr-m-6a0384bf.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/7JZ/26/08/18/001/7jz260818001-1152x648-fr-6a0384be.png" alt="Open Mic de l&#039;Est - 18 août 2026"></picture>
1808 + </div>
1809 + <div class="feature-content">
1810 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Open Mic de l&#039;Est - 18 août 2026</h3>
1811 + <div class="feature-date">18 août 2026, 19h30</div>
1812 + <div class="feature-city">La Tétro, Montréal, QC</div>
1813 + </div>
1814 + </a>
1815 + </article>
1816 + </div>
1817 + <div class="feature-col is-small">
1818 + <article class="feature-canvas has-tag">
1819 + <a href="/billets/dvo260818001" class="feature-link embed-self">
1820 + <div class="feature-image-wrapper">
1821 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-320x180-fr-s-6a0de599.png, https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-640x360-fr-m-6a0de599.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-640x360-fr-m-6a0de599.png"><source srcset="https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-320x180-fr-s-6a0de599.png, https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-640x360-fr-m-6a0de599.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/DVO/26/06/09/001/dvo260609001-1152x648-fr-6a0de598.png" alt="Apocalypse Show"></picture>
1822 + </div>
1823 + <div class="feature-content">
1824 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Apocalypse Show</h3>
1825 + <div class="feature-date">18 août 2026, 20h00</div>
1826 + <div class="feature-city">Le Fermentor, L&#039;Assomption, QC</div>
1827 + <div class="feature-tag">Complet</div>
1828 + </div>
1829 + </a>
1830 + </article>
1831 + </div>
1832 + <div class="feature-col is-small">
1833 + <article class="feature-canvas">
1834 + <a href="/billets/o1m260818002" class="feature-link embed-self">
1835 + <div class="feature-image-wrapper">
1836 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-320x180-en-s-6a17b821.png, https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-640x360-en-m-6a17b822.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-640x360-en-m-6a17b822.png"><source srcset="https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-320x180-en-s-6a17b821.png, https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-640x360-en-m-6a17b822.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/O1M/26/05/27/001/o1m260527001-1152x648-en-6a17b821.png" alt="Best Tuesday Night Montreal Comedy | Comedy on Mackay | #1 Rated Show"></picture>
1837 + </div>
1838 + <div class="feature-content">
1839 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Best Tuesday Night Montreal Comedy | Comedy on Mackay | #1 Rated Show</h3>
1840 + <div class="feature-date">18 août 2026, 20h00</div>
1841 + <div class="feature-city">NsurMackay, Montreal, QC</div>
1842 + </div>
1843 + </a>
1844 + </article>
1845 + </div>
1846 + <div class="feature-col is-small">
1847 + <article class="feature-canvas has-tag">
1848 + <a href="/billets/v10260623001" class="feature-link embed-self">
1849 + <div class="feature-image-wrapper">
1850 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-320x180-fr-s-68eedce3.png, https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-640x360-fr-m-68eedce3.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-640x360-fr-m-68eedce3.png"><source srcset="https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-320x180-fr-s-68eedce3.png, https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-640x360-fr-m-68eedce3.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/V10/26/06/23/001/v10260623001-1152x648-fr-68eedce2.png" alt="Cabaret Rachelle à La Ferme Kerr Comedie Grange (en Français)"></picture>
1851 + </div>
1852 + <div class="feature-content">
1853 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Cabaret Rachelle à La Ferme Kerr Comedie Grange (en Français)</h3>
1854 + <div class="feature-date">18 août 2026, 20h00</div>
1855 + <div class="feature-city">La Ferme Kerr, Gore, QC</div>
1856 + <div class="feature-tag">Annulé</div>
1857 + </div>
1858 + </a>
1859 + </article>
1860 + </div>
1861 + <div class="feature-col is-small">
1862 + <article class="feature-canvas">
1863 + <a href="/billets/improlige207" class="feature-link embed-self">
1864 + <div class="feature-image-wrapper">
1865 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-320x180-fr-s-6a206b95.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-640x360-fr-m-6a206b95.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-640x360-fr-m-6a206b95.png"><source srcset="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-320x180-fr-s-6a206b95.png, https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-640x360-fr-m-6a206b95.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/1NH/26/08/18/001/1nh260818001-1152x648-fr-6a206b95.png" alt="Impro LIGE - Bleus vs Jaunes (18 août @Aux Angles Ronds)"></picture>
1866 + </div>
1867 + <div class="feature-content">
1868 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Impro LIGE - Bleus vs Jaunes (18 août @Aux Angles Ronds)</h3>
1869 + <div class="feature-date">18 août 2026, 20h00</div>
1870 + <div class="feature-city">Aux Angles Ronds, Montréal, QC</div>
1871 + </div>
1872 + </a>
1873 + </article>
1874 + </div>
1875 + <div class="feature-col is-small">
1876 + <article class="feature-canvas has-tag">
1877 + <a href="/billets/ux8260818001" class="feature-link embed-self">
1878 + <div class="feature-image-wrapper">
1879 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-320x180-fr-s-6a64cad3.png, https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-640x360-fr-m-6a64cad4.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-640x360-fr-m-6a64cad4.png"><source srcset="https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-320x180-fr-s-6a64cad3.png, https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-640x360-fr-m-6a64cad4.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/UX8/26/08/18/001/ux8260818001-1152x648-fr-6a64cad3.png" alt="LOU K + CHENIER @ Quai des brumes, Montréal"></picture>
1880 + </div>
1881 + <div class="feature-content">
1882 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>LOU K + CHENIER @ Quai des brumes, Montréal</h3>
1883 + <div class="feature-date">18 août 2026, 20h00</div>
1884 + <div class="feature-city">LE QUAI DES BRUMES, Montréal, QC</div>
1885 + <div class="feature-tag">Annulé</div>
1886 + </div>
1887 + </a>
1888 + </article>
1889 + </div>
1890 + <div class="feature-col is-small">
1891 + <article class="feature-canvas">
1892 + <a href="/billets/b97260818001" class="feature-link embed-self">
1893 + <div class="feature-image-wrapper">
1894 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-320x180-fr-s-6a5f930e.png, https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-640x360-fr-m-6a5f930e.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-640x360-fr-m-6a5f930e.png"><source srcset="https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-320x180-fr-s-6a5f930e.png, https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-640x360-fr-m-6a5f930e.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/B97/26/08/18/001/b97260818001-1152x648-fr-6a5f930d.png" alt="Full Gisèle!"></picture>
1895 + </div>
1896 + <div class="feature-content">
1897 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Full Gisèle!</h3>
1898 + <div class="feature-date">18 août 2026, 21h00</div>
1899 + <div class="feature-city">Cabaret Mado, Montréal, QC</div>
1900 + </div>
1901 + </a>
1902 + </article>
1903 + </div>
1904 + <div class="feature-col is-small">
1905 + <article class="feature-canvas">
1906 + <a href="/billets/erz260803001" class="feature-link embed-self">
1907 + <div class="feature-image-wrapper">
1908 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-320x180-fr-s-6a441674.png, https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-640x360-fr-m-6a441674.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-640x360-fr-m-6a441674.png"><source srcset="https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-320x180-fr-s-6a441674.png, https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-640x360-fr-m-6a441674.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/ERZ/26/08/03/001/erz260803001-1152x648-fr-6a441673.png" alt="Les chiennes à Jacques sur la route"></picture>
1909 + </div>
1910 + <div class="feature-content">
1911 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Les chiennes à Jacques sur la route</h3>
1912 + <div class="feature-date">18 août 2026, 21h00</div>
1913 + <div class="feature-city">Le Terminal Comédie Club, Montréal, QC</div>
1914 + </div>
1915 + </a>
1916 + </article>
1917 + </div>
1918 + <div class="feature-col is-small">
1919 + <article class="feature-canvas">
1920 + <a href="/billets/260818-cafe-campus-mardi-retro-club-dance" class="feature-link embed-self">
1921 + <div class="feature-image-wrapper">
1922 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-320x180-fr-s-68927276.png, https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-640x360-fr-m-68927276.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-640x360-fr-m-68927276.png"><source srcset="https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-320x180-fr-s-68927276.png, https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-640x360-fr-m-68927276.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/B4Z/25/09/02/001/cafe-campus-danse-club-discotheque-mardis-retro-2-sept-2025-1152x648-fr-68927275.png" alt="Les Mardis Rétro du Café Campus"></picture>
1923 + </div>
1924 + <div class="feature-content">
1925 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>Les Mardis Rétro du Café Campus</h3>
1926 + <div class="feature-date">18 août 2026, 21h30</div>
1927 + <div class="feature-city">Café Campus, Montréal, QC</div>
1928 + </div>
1929 + </a>
1930 + </article>
1931 + </div>
1932 + <div class="feature-col is-small">
1933 + <article class="feature-canvas">
1934 + <a href="/billets/o1m260818001" class="feature-link embed-self">
1935 + <div class="feature-image-wrapper">
1936 + <picture><source media="(max-width:350px)" srcset="https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-320x180-en-s-6971be5a.png, https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-640x360-en-m-6971be5a.png 2x"><source media="(max-width:374px)" srcset="https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-640x360-en-m-6971be5a.png"><source srcset="https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-320x180-en-s-6971be5a.png, https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-640x360-en-m-6971be5a.png 2x"><img itemprop="image" src="https://tpos.s3.amazonaws.com/events/O1M/26/01/27/001/o1m260127001-1152x648-en-6971be59.png" alt="English &amp; French Stand-Up Comedy"></picture>
1937 + </div>
1938 + <div class="feature-content">
1939 + <h3 class="feature-title"><div class="tag-attendance-offline" title="Événement en personne"></div>English &amp; French Stand-Up Comedy</h3>
1940 + <div class="feature-date">18 août 2026, 22h00</div>
1941 + <div class="feature-city">NsurMackay, Montréal, QC</div>
1942 + </div>
1943 + </a>
1944 + </article>
1945 + </div>
1946 + </div>
1947 + </div>
1948 + <nav id="events-pages" class="nav-pagination">
1949 + <a id="events-pages-prev" href="#" onclick="return false;" class="btn prev embed-self disabled">Précédent</a>
1950 + <ul>
1951 + <li><span>1</span></li>
1952 + <li><a href="/billets?page=2" title="Page 2" class="embed-self"><span>2</span></a></li>
1953 + <li><a href="/billets?page=3" title="Page 3" class="embed-self"><span>3</span></a></li>
1954 + <li class="hide-on-mobile"><a href="/billets?page=4" title="Page 4" class="embed-self"><span>4</span></a></li>
1955 + <li class="hide-on-mobile"><a href="/billets?page=5" title="Page 5" class="embed-self"><span>5</span></a></li>
1956 + <li class="hide-on-mobile"><a href="/billets?page=6" title="Page 6" class="embed-self"><span>6</span></a></li>
1957 + <li class="hide-on-mobile"><a href="/billets?page=7" title="Page 7" class="embed-self"><span>7</span></a></li>
1958 + <li class="hide-on-mobile"><a href="/billets?page=8" title="Page 8" class="embed-self"><span>8</span></a></li>
1959 + <li class="hide-on-mobile"><span>...</span></li>
1960 + <li class="hide-on-mobile"><a href="/billets?page=65" title="Page 65" class="embed-self"><span>65</span></a></li>
1961 + </ul>
1962 + <a id="events-pages-next" href="/billets?page=2" class="btn next embed-self">Suivant</a>
1963 + </nav>
1964 + </section>
1965 + </div>
1966 + </main>
1967 +<style>
1968 + .intercom-lightweight-app, #intercom-container, .intercom-namespace {
1969 + display:none;
1970 + opacity:0;
1971 + }
1972 +
1973 + body[data-model="contact"] .intercom-lightweight-app, body[data-model="contact"] #intercom-container, body[data-model="contact"] .intercom-namespace {
1974 + display:block;
1975 + opacity:1;
1976 + }
1977 +
1978 +
1979 +</style>
1980 +<script>
1981 +
1982 +function loadIntercom() {
1983 +
1984 + window.intercomSettings = { app_id: "t5w1iezi", custom_launcher_selector: ".intercom-link" };
1985 + var w = window;
1986 + var ic = w.Intercom;
1987 + if (typeof ic === "function") {
1988 +
1989 + if(document.querySelector('body').getAttribute('data-model') !== 'contact') {
1990 + ic('shutdown')
1991 + } else {
1992 + $('.intercom-lightweight-app').css('display','block');
1993 + $('.intercom-lightweight-app').css('opacity', 1);
1994 + ic("reattach_activator");
1995 + ic("update", window.intercomSettings);
1996 + }
1997 +
1998 + } else {
1999 +
2000 + var d = document;
2001 + var i = function () {
2002 + i.c(arguments);
2003 + };
2004 + i.q = [];
2005 + i.c = function (args) {
2006 + i.q.push(args);
2007 + };
2008 + w.Intercom = i;
2009 + function l() {
2010 + var s = d.createElement("script");
2011 + s.type = "text/javascript";
2012 + s.async = true;
2013 + s.src = "https://widget.intercom.io/widget/t5w1iezi";
2014 + var x = d.getElementsByTagName("script")[0];
2015 + x.parentNode.insertBefore(s, x);
2016 + }
2017 +
2018 + if (w.attachEvent) {
2019 + w.attachEvent("onload", l);
2020 + } else {
2021 + w.addEventListener("load", l, false);
2022 + }
2023 + }
2024 +
2025 +}
2026 +
2027 +function unloadIntercom() {
2028 + if(window.Intercom) window.Intercom('shutdown')
2029 +}
2030 +
2031 +
2032 +$(document).ready(function() {
2033 + loadIntercom();
2034 +})
2035 +
2036 +
2037 +</script>
2038 + <div class="l-prefooter">
2039 + <div class="l-page-wrapper">
2040 + <div class="prefooter-content">
2041 + <div>
2042 + <a href="/" class="logo">
2043 + <img src="https://cdn.lepointdevente.com/themes/tpos/logo-on-dark-fr-5f32b556.svg" alt="Lepointdevente.com">
2044 + </a>
2045 + <span class="copy">&copy; 2010&ndash;2026 Tous droits réservés</span>
2046 + <nav class="nav-medias">
2047 + <ul>
2048 + <li class="nav-medias-item">
2049 + <a href="https://facebook.com/lepointdevente" class="facebook" target="_blank"><span class="visuallyhidden" lang="en">Facebook</span></a>
2050 + </li>
2051 + <li class="nav-medias-item">
2052 + <a href="https://twitter.com/lepointdevente" class="twitter" target="_blank"><span class="visuallyhidden" lang="en">Twitter</span></a>
2053 + </li>
2054 + <li class="nav-medias-item">
2055 + <a href="https://instagram.com/lepointdevente" class="instagram" target="_blank"><span class="visuallyhidden" lang="en">Instagram</span></a>
2056 + </li>
2057 + <li class="nav-medias-item">
2058 + <a href="https://www.linkedin.com/company/lepointdevente-com" class="linkedin" target="_blank"><span class="visuallyhidden" lang="en">LinkedIn</span></a>
2059 + </li>
2060 + <li class="nav-medias-item">
2061 + <a href="https://www.tiktok.com/@lepointdevente.com" class="tiktok" target="_blank"><span class="visuallyhidden" lang="en">Tiktok</span></a>
2062 + </li>
2063 + </ul>
2064 + </nav>
2065 + </div>
2066 + <nav class="nav-prefooter">
2067 + <div>
2068 + <h2><a href="/">Acheter des billets</a></h2>
2069 + <ul>
2070 + <li class="nav-prefooter-item"><a id="modal-giftcard-link-trigger" tabindex="0">Offrir une carte-cadeau</a></li>
2071 + <li class="nav-prefooter-item"><a href="/conditions/#achat">Politique d’achat</a></li>
2072 + <li class="nav-prefooter-item"><a href="http://aide.lepointdevente.com/fr/collections/572670-f-a-q-achat-de-billets-en-ligne" target="blank">Aide</a></li>
2073 + </ul>
2074 + </div>
2075 + <div>
2076 + <h2><a href="/organisateurs" data-turbolinks="false">Organisateurs</a></h2>
2077 + <ul>
2078 + <li class="nav-prefooter-item"><a href="/organisateurs" data-turbolinks="false">Créer un compte</a></li>
2079 + </ul>
2080 + <a href="/organisateurs/a-propos#compliance-security" class="b2c-footer-pci-link" target="_blank">
2081 + <img src="https://tpos-cdn.s3.ca-central-1.amazonaws.com/b2b/about/logo-pci-white.png" alt="PCI Logo">
2082 + </a>
2083 + </div>
2084 + <div>
2085 + <h2><a href="/">Événements</a></h2>
2086 + <ul>
2087 + <li class="nav-prefooter-item"><a href="/?type=virtual&amp;locality=">Événements virtuels</a></li>
2088 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=11&amp;radius=0">Événements à Montréal</a></li>
2089 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=1&amp;radius=0">Événements à Québec</a></li>
2090 + <li class="nav-prefooter-item"><a href="/?type=physical&amp;locality=2&amp;radius=0">Événements à Longueuil</a></li>
2091 + </ul>
2092 + </div>
2093 + </nav>
2094 + </div>
2095 + </div>
2096 + </div>
2097 + <footer class="l-footer">
2098 + <div class="l-page-wrapper">
2099 + <div class="footer-content">
2100 + <nav class="nav-footer">
2101 + <ul>
2102 + <li class="nav-footer-item">
2103 + <a href="http://blog.lepointdevente.com/" target="blank"><span class="nav-footer-text">Blogue</span></a>
2104 + </li>
2105 + <li class="nav-footer-item">
2106 + <a href="/contact/"><span class="nav-footer-text">Contact</span></a>
2107 + </li>
2108 + <li class="nav-footer-item">
2109 + <a href="https://lepointdevente.com/organisateurs/a-propos/emplois/" target="blank"><span class="nav-footer-text">Emplois</span></a>
2110 + </li>
2111 + <li class="nav-footer-item">
2112 + <a href="/conditions/"><span class="nav-footer-text">Conditions d'utilisation</span></a>
2113 + </li>
2114 + <li class="nav-footer-item">
2115 + <a href="/conditions/#confidentialite"><span class="nav-footer-text">Politique de confidentialité</span></a>
2116 + </li>
2117 + <li class="nav-footer-item">
2118 + <a role="button" tabindex="0" onclick="openCookiePreferences()" style="cursor:pointer"><span class="nav-footer-text">Préférences de confidentialité</span></a>
2119 + </li>
2120 + </ul>
2121 + </nav>
2122 + <div class="langselector embed-hidden">
2123 + <a href="/?lang=en" lang="en" class="langselector-trigger">
2124 + <span class="langselector-icon" aria-hidden="true"></span>
2125 + <span class="langselector-trigger-text">English</span>
2126 + </a>
2127 + </div>
2128 + </div>
2129 + </div>
2130 + </footer>
2131 + <div id="modal" class="js-modal modal"></div>
2132 +<div id="modal-overlay"></div>
2133 +<div id="modal-background">
2134 + <div class="modal-c">
2135 + <div class="c-utilisation">
2136 + <img src="https://cdn.lepointdevente.com/themes/tpos/logo-on-dark-fr-5f32b556.svg" alt="Lepointdevente.com">
2137 + <h2>Utilisation de cookies</h2>
2138 + <p>Nous utilisons des témoins (cookies) pour améliorer votre expérience de navigation, offrir des fonctionnalités relatives à notre site et analyser le trafic de nos sites.</p>
2139 +<p>En cliquant sur « Tout accepter », vous consentez à ce que nous utilisions tous les types de témoins. Des informations complémentaires se trouvent dans nos <a href="/conditions/#confidentialite" target="_blank">politiques de confidentialités</a>.</p> <div class="mod-footer">
2140 + <button type="button" class="square-btn preferences" id="c-preferences" onclick="setPreferences()">
2141 + <span>Gérer les préférences</span>
2142 + </button>
2143 + <button type="button" class="square-btn accept-all" id="accept-c" onclick="acceptAllCookies()">
2144 + <span>Tout accepter</span>
2145 + </button>
2146 + </div>
2147 + </div>
2148 + <div class="set-preferences">
2149 + <h2>Gérer les préférences</h2>
2150 + <h4>Essentiels</h4>
2151 + <div class="essential-c">
2152 + <div class="description">
2153 + Ces témoins sont essentiels au fonctionnement de notre site et sans eux, notre site ne fonctionnera pas correctement. Ces témoins sont nécessaires, par exemple, pour la connexion à un compte client et pour assurer la sécurité de nos sites. L’activation des témoins essentiels est donc obligatoire. </div>
2154 + <div class="toggle">
2155 + <label class="switch">
2156 + <input type="checkbox" checked disabled>
2157 + <span class="slider round"></span>
2158 + </label>
2159 + </div>
2160 + </div>
2161 + <div class="divider-sections"></div>
2162 + <h4>Fonctionnels</h4>
2163 + <div class="functional-c">
2164 + <div class="description">
2165 + Ces témoins servent à améliorer votre expérience de navigation en enregistrant certaines de vos préférences. Si vous désactivez ces témoins, certaines de vos préférences ne seront plus enregistrées, mais vous pourrez quand même utiliser nos sites sans problème. </div>
2166 + <div class="toggle">
2167 + <label class="switch">
2168 + <input type="checkbox" id="input-functional-c" checked>
2169 + <span class="slider round"></span>
2170 + </label>
2171 + </div>
2172 + </div>
2173 + <div class="divider-sections"></div>
2174 + <h4>Marketing</h4>
2175 + <div class="marketing-c">
2176 + <div class="description">
2177 + Ces témoins collectent de l’information sur la façon dont les utilisateurs naviguent sur nos sites sans permettre de les identifier directement. Les données recueillies sont utilisées afin d’étudier et d’améliorer l’expérience de navigation sur nos sites. Ces témoins nous permettent, par exemple, d’identifier les pages les plus visitées sur nos sites. </div>
2178 + <div class="toggle">
2179 + <label class="switch">
2180 + <input type="checkbox" id="input-marketing-c" checked>
2181 + <span class="slider round"></span>
2182 + </label>
2183 + </div>
2184 + </div>
2185 + <div class="mod-footer">
2186 + <button type="button" class="square-btn back" id="c-preferences" onclick="back()">
2187 + <span>Retour</span>
2188 + </button>
2189 + <button type="button" class="square-btn confirm" id="accept-c" onclick="confirmChoices()">
2190 + <span>Confirmer</span>
2191 + </button>
2192 + </div>
2193 + </div>
2194 + </div>
2195 +</div>
2196 +
2197 +
2198 +<div class="nvp">
2199 + <div>
2200 + Ce panier d'achat est hébergé par <a href="https://lepointdevente.com" target="_blank">Lepointdevente.com</a>. Des témoins (cookies) essentiels au fonctionnement du panier d'achat sont en place pour activer des fonctions de base, comme la navigation et la sécurité. Ce sont les seuls témoins activés. En utilisant ce panier d'achat, vous acceptez l'utilisation de ces témoins. </div>
2201 +</div>
2202 +<script>
2203 +
2204 + $(document).ready(function() {
2205 +
2206 + var isIframe = window.self !== window.top;
2207 +
2208 + if(isIframe) {
2209 + var cookieNotice = $(".nvp")
2210 + var closeButton = $('<button class="btn-close js-c-close"></button>')
2211 + closeButton.on('click', function() {
2212 + cookieNotice.hide()
2213 + })
2214 + cookieNotice.append(closeButton)
2215 + cookieNotice.prependTo( $('main') ).show();
2216 + }
2217 + })
2218 +
2219 +</script>
2220 +
2221 +<div class="modal-giftcard">
2222 + <div class="js-modal modal">
2223 + <div class="js-modal-trigger"></div>
2224 + <div class="js-modal-wrapper modal-wrapper">
2225 + <div class="js-modal-content modal-content">
2226 + <div class="modal-body">
2227 + <div id="giftcard-carousel" class="owl-carousel">
2228 + <picture class="item">
2229 + <a href="/carte-cadeau/salle-augustin-norbert-morin" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-6/giftcard-img-fr-5fe0cf6f.png" class="item" alt="Comité culturel de la Salle Augustin-Norbert-Morin"></a>
2230 + </picture>
2231 + <picture class="item">
2232 + <a href="/carte-cadeau/surlascenedavignononstage" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-3/giftcard-img-fr-5fe0dbf4.png" class="item" alt="Sur la scène Davignon On Stage"></a>
2233 + </picture>
2234 + <picture class="item">
2235 + <a href="/carte-cadeau/lesaintjeancomedieclub" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartescadeaux/giftcard-img-fr-6a2714da.png" class="item" alt="Le Saint-Jean Comédie Club"></a>
2236 + </picture>
2237 + <picture class="item">
2238 + <a href="/carte-cadeau/moulin-marcoux" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-9/giftcard-img-fr-61ed6b35.png" class="item" alt="Moulin Marcoux"></a>
2239 + </picture>
2240 + <picture class="item">
2241 + <a href="/carte-cadeau/lesproductionsles2vallees" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-12/giftcard-img-fr-673f78f3.png" class="item" alt="Les Productions Les 2 vallées"></a>
2242 + </picture>
2243 + <picture class="item">
2244 + <a href="/carte-cadeau/espace-sophia" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartescadeauxesp/giftcard-img-fr-655671f9.png" class="item" alt="Espace Sophia"></a>
2245 + </picture>
2246 + <picture class="item">
2247 + <a href="/carte-cadeau/laslague" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-11/giftcard-img-fr-686ffc7d.png" class="item" alt="La Slague"></a>
2248 + </picture>
2249 + <picture class="item">
2250 + <a href="/carte-cadeau/rocklacauze" target="_blank"><img src="//cdn.lepointdevente.com/themes/rocklacauze2021/giftcard-img-fr-61dca5a5.png" class="item" alt="Rock la cauze"></a>
2251 + </picture>
2252 + <picture class="item">
2253 + <a href="/carte-cadeau/district-7-production" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau/giftcard-img-fr-61c4e5c7.png" class="item" alt="District 7 Production"></a>
2254 + </picture>
2255 + <picture class="item">
2256 + <a href="/carte-cadeau/nspwprowrestlingacademy" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-7/giftcard-img-fr-5fe0e7fc.png" class="item" alt="NSPW Pro Wrestling Academy"></a>
2257 + </picture>
2258 + <picture class="item">
2259 + <a href="/carte-cadeau/salle-julie-pothier" target="_blank"><img src="//cdn.lepointdevente.com/themes/sallejuliepothie/giftcard-img-fr-66be0822.png" class="item" alt="Salle Julie-Pothier"></a>
2260 + </picture>
2261 + <picture class="item">
2262 + <a href="/carte-cadeau/celeb-evenements-inc" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-8/giftcard-img-fr-61d30660.png" class="item" alt="CELEB&#039;Événements INC."></a>
2263 + </picture>
2264 + <picture class="item">
2265 + <a href="/carte-cadeau/lesgrandsbois" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeaulesgr/giftcard-img-fr-654bb2a3.png" class="item" alt="Les Grands Bois - Arts et Spectacles"></a>
2266 + </picture>
2267 + <picture class="item">
2268 + <a href="/carte-cadeau/le-zaricot" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeau-4/giftcard-img-fr-61ed6b87.png" class="item" alt="Le Zaricot"></a>
2269 + </picture>
2270 + <picture class="item">
2271 + <a href="/carte-cadeau/lesgrandesfetestelus" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeaugft/giftcard-img-fr-63810db0.png" class="item" alt="Les Grandes Fêtes TELUS"></a>
2272 + </picture>
2273 + <picture class="item">
2274 + <a href="/carte-cadeau/festivaldemusiquedechambredemontreal" target="_blank"><img src="//cdn.lepointdevente.com/themes/cartecadeaumusiq/giftcard-img-fr-66be07a8.png" class="item" alt="Festival de musique de chambre de Montréal"></a>
2275 + </picture>
2276 + </div>
2277 + </div>
2278 + </div>
2279 + </div>
2280 + </div>
2281 +</div>
2282 +
2283 +</body>
2284 +</html>
added tests/fixtures/montreal_sample.json +1 −0
@@ -0,0 +1 @@
1 +{"help": "https://donnees.montreal.ca/api/3/action/help_show?name=datastore_search", "success": true, "result": {"include_total": true, "limit": 4, "records_format": "objects", "resource_id": "6decf611-6f11-4f34-bb36-324d804c9bad", "total_estimation_threshold": null, "records": [{"_id":1,"titre":"Séance extraordinaire du conseil d'Anjou","url_fiche":"https://montreal.ca/evenements/seance-extraordinaire-du-conseil-danjou-3145","description":"Séance extraordinaire du conseil d'arrondissement d'Anjou.","date_debut":"2025-09-30","date_fin":"2025-09-30","type_evenement":"Conseil et comité","public_cible":"Pour tous","emplacement":"En salle","inscription":"Entrée libre","cout":"Gratuit","arrondissement":"Anjou","titre_adresse":"Salle du conseil de la mairie d'arrondissement d'Anjou","adresse_principale":"7701, boulevard Louis-H. La Fontaine","adresse_secondaire":"None","code_postal":"H1K4B9","lat":"45.60186845582814","long":"-73.55757158442222","X":"300308.9","Y":"5051331.1"},{"_id":2,"titre":"Séance du conseil de L'Île-Bizard–Sainte-Geneviève d'octobre","url_fiche":"https://montreal.ca/evenements/seance-du-conseil-de-lile-bizard-sainte-genevieve-doctobre-3419","description":"La séance du conseil de L'Île-Bizard-Sainte-Geneviève a lieu le 1er octobre 2025 à 19 h 30 au 500, montée de l'Église.","date_debut":"2025-10-01","date_fin":"2025-10-01","type_evenement":"Conseil et comité","public_cible":"Pour tous","emplacement":"En ligne","inscription":"Entrée libre","cout":"Gratuit","arrondissement":"L'Île-Bizard–Sainte-Geneviève","titre_adresse":"nan","adresse_principale":"nan","adresse_secondaire":"nan","code_postal":"nan","lat":"45.488345","long":"-73.882029","X":"274938.2","Y":"5038784.4"},{"_id":3,"titre":"Séance extraordinaire du conseil de Pierrefonds-Roxboro du 7 octobre : Budget 2025 et PDI 2025-2034","url_fiche":"https://montreal.ca/evenements/seance-extraordinaire-du-conseil-de-pierrefonds-roxboro-du-7-octobre-budget-2025-et-pdi-2025-2034-5783","description":"Tout sur la séance extraordinaire du conseil d'arrondissement de Pierrefonds-Roxboro du lundi 7 octobre portant sur le Budget 2025 et le PDI 2024-2034.","date_debut":"2024-10-07","date_fin":"2024-10-07","type_evenement":"Conseil et comité","public_cible":"Pour tous","emplacement":"En salle","inscription":"Entrée libre","cout":"Gratuit","arrondissement":"Pierrefonds-Roxboro","titre_adresse":"nan","adresse_principale":"nan","adresse_secondaire":"nan","code_postal":"nan","lat":"45.495489","long":"-73.848159","X":"277589.1","Y":"5039566.3"},{"_id":4,"titre":"Séance du conseil de L'Île-Bizard–Sainte-Geneviève de septembre","url_fiche":"https://montreal.ca/evenements/seance-du-conseil-de-lile-bizard-sainte-genevieve-de-septembre-28938","description":"Le conseil d’arrondissement de L’Île-Bizard–Sainte-Geneviève se tiendra le mardi 2 septembre 2025, à 19 h 30, à la salle multifonctionnelle Madeleine-Lahaye.","date_debut":"2025-09-02","date_fin":"2025-09-02","type_evenement":"Conseil et comité","public_cible":"Pour tous","emplacement":"En ligne","inscription":"Entrée libre","cout":"Gratuit","arrondissement":"L'Île-Bizard–Sainte-Geneviève","titre_adresse":"nan","adresse_principale":"nan","adresse_secondaire":"nan","code_postal":"nan","lat":"45.488345","long":"-73.882029","X":"274938.2","Y":"5038784.4"}], "fields": [{"id": "_id", "type": "int"}, {"id": "titre", "type": "text"}, {"id": "url_fiche", "type": "text"}, {"id": "description", "type": "text"}, {"id": "date_debut", "type": "text"}, {"id": "date_fin", "type": "text"}, {"id": "type_evenement", "type": "text"}, {"id": "public_cible", "type": "text"}, {"id": "emplacement", "type": "text"}, {"id": "inscription", "type": "text"}, {"id": "cout", "type": "text"}, {"id": "arrondissement", "type": "text"}, {"id": "titre_adresse", "type": "text"}, {"id": "adresse_principale", "type": "text"}, {"id": "adresse_secondaire", "type": "text"}, {"id": "code_postal", "type": "text"}, {"id": "lat", "type": "text"}, {"id": "long", "type": "text"}, {"id": "X", "type": "text"}, {"id": "Y", "type": "text"}], "_links": {"start": "/api/3/action/datastore_search?resource_id=6decf611-6f11-4f34-bb36-324d804c9bad&limit=4", "next": "/api/3/action/datastore_search?resource_id=6decf611-6f11-4f34-bb36-324d804c9bad&limit=4&offset=4"}, "total": 5592, "total_was_estimated": false}}
\ No newline at end of file
added tests/fixtures/sherbrooke_page.html +1 −0
@@ -0,0 +1 @@
1 +<html><script> window['TRANSFER_CACHE'] = {"value": {"selected": {"events": [{"id": "101ac7c2-f58b-4e39-93d5-914cd5cf3ada", "visibleId": 2722, "releaseDate": "2026-08-01T00:00:00Z", "modificationDate": "2026-05-27T14:05:45.953Z", "periods": [{"type": 0, "dateDebut": "2026-08-01T00:00:00Z", "dateFin": "2026-08-01T02:30:00Z", "jourLundi": true, "jourMardi": true, "jourMercredi": true, "jourJeudi": true, "jourVendredi": true, "jourSamedi": true, "jourDimanche": true, "label": {"fr": "Le 31 juillet 2026", "en": "July 31st, 2026"}, "labelHours": {"fr": "de 20h00 à 22h30", "en": "from 20h00 to 22h30"}, "allDays": true, "labelDays": {"fr": "tous les jours", "en": "every day"}}], "languageAvailability": {"fr": true, "en": true}, "name": {"fr": "Cinéma en plein air au parc Beaulieu", "en": "Cinéma en plein air au parc Beaulieu"}, "serializeContent": false, "alias": {"fr": "cinema-en-plein-air-au-parc-beaulieu", "en": "cinema-en-plein-air-au-parc-beaulieu"}, "keyWords": {"fr": "", "en": ""}, "moduleId": "333327b9-4d53-e611-80ea-00155d09650f", "moduleURL": {"fr": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites/{id}/{slug}", "en": "https://www.sherbrooke.ca/en/culture-sports-and-recreation/event-calendar/{id}/{slug}"}, "fullUrl": {"fr": "/fr/culture-sports-et-loisirs/calendrier-des-activites/2722/cinema-en-plein-air-au-parc-beaulieu", "en": "/en/culture-sports-and-recreation/event-calendar/2722/cinema-en-plein-air-au-parc-beaulieu"}, "meta": {"fr": "Cinéma en plein air au parc Beaulieu le vendredi 31 juillet.", "en": "Cinéma plein air au parc Beaulieu le vendredi 31 juillet."}, "mainPicture": "cinema-plein-air-evenement-720x560px.jpg", "publishInfos": {"isPublished": true, "dateMiseEnLigne": "2026-05-27T14:05:45.957Z", "isHighlighted": false, "isVisibleInMenus": true, "hasSharingButtons": true}, "client": {"id": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "visibleId": 419, "name": "Parcs", "address": {"country": "", "latitude": 0.0, "longitude": 0.0}, "phone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": ""}, "faxPhone": {"number": "", "countryCode": ""}, "otherAddresses": [{"id": "88334365-1518-4086-79df-08d7404e34b7", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacques-Cartier", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 2C3", "country": "Canada", "latitude": 45.395034, "longitude": -71.9175917, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "c2fe816c-6bb5-4fdd-5462-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Atto-Beaver", "street": "Rue Saint Francis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0B3", "country": "Canada", "latitude": 45.3766588, "longitude": -71.8542815, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2dd3adb7-6d89-4a87-5463-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Jardins-Fleuris", "street": "1245 Rue Papineau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 4C4", "country": "Canada", "latitude": 45.416137, "longitude": -71.862489, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "bbad822d-002b-4377-5464-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Nault", "street": "", "city": "Sherbrooke", "province": "Québec", "postalCode": "", "country": "", "latitude": 45.468768, "longitude": -71.940445, "streetViewOrientation": 0}, {"id": "bd36345e-76fa-4781-5df5-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Lucien-Blanchard", "street": "755, rue Cabana", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1K 0A6", "country": "Canada", "latitude": 45.3944121, "longitude": -71.9283057, "streetViewOrientation": 0}, {"id": "fb212169-5ae9-492f-5df6-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bureau", "street": "925, rue Walsh", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4L7", "country": "Canada", "latitude": 45.3924261, "longitude": -71.8692436, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "ee847adc-9043-4ce3-5df7-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue", "street": "1440, rue Brébeuf", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3G2", "country": "Canada", "latitude": 45.384407, "longitude": -71.908385, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d00e7948-3a03-4ad3-5df8-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Domaine-Howard", "street": "1350, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1S3", "country": "Canada", "latitude": 45.4033749, "longitude": -71.9096689, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "8e45604f-dd8a-4e4a-5df9-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Beaulieu", "street": "755, rue Coombs", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2B7", "country": "Canada", "latitude": 45.3732878, "longitude": -71.9730694, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "885556b0-8786-49b9-5dfa-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc André-Viger", "street": "3275, rue Richard", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1Y2", "country": "Canada", "latitude": 45.410288, "longitude": -71.948647, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "07d31315-0a0f-4f37-5dfb-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Sylvie-Daigle", "street": "400, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2J9", "country": "Canada", "latitude": 45.411855, "longitude": -71.8844021, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1a70fcaa-a0f8-4571-5dfc-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ma-Villa", "street": "166, rue des Colibris", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0P2", "country": "Canada", "latitude": 45.398797, "longitude": -72.007653, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3bfba6af-39d6-4d60-5dfd-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Central", "street": "6161, rue du Président-Kennedy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2P5", "country": "Canada", "latitude": 45.3546979, "longitude": -71.9943063, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73883e6d-bcc9-40bf-5dfe-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Andrew-Sangster", "street": "1220, rue Kitchener", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3L1", "country": "Canada", "latitude": 45.387524, "longitude": -71.904691, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73eb2f6e-ed3f-4b1b-5dff-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Victoria", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135081, "longitude": -71.8876118, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "44581296-11af-4fbc-5e00-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Adrien-Cambron", "street": "365, 10e Avenue Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2T4", "country": "Canada", "latitude": 45.412988, "longitude": -71.874525, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5b1456f2-b2e2-409e-5e01-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Olivier-Chauveau", "street": "4445, rue Chauveau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1W2", "country": "Canada", "latitude": 45.385005, "longitude": -71.974137, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "72c90ce2-c50d-4a06-5e02-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Villageois", "street": "4540, chemin Saint-Roch Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0K8", "country": "Canada", "latitude": 45.3935267, "longitude": -72.0403563, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7b60689b-ca7d-4937-5e03-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Marquis-de-Montcalm", "street": "2070, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1T9", "country": "Canada", "latitude": 45.403079, "longitude": -71.92521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "85cee1c7-f735-4dd7-5e04-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alfred-Élie-Dufresne", "street": "521, rue Galt Ouest", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1Y8", "country": "Canada", "latitude": 45.395532, "longitude": -71.892718, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "db08e77a-d3ea-4f5d-5e05-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3748433, "longitude": -71.8920002, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6b96c415-5edc-4167-5e06-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Quintal", "street": "1671, chemin Duplessis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0C2", "country": "Canada", "latitude": 45.4306045, "longitude": -71.8499278, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "71c0d030-4262-41d0-5e07-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antoine-Racine", "street": "rue Ball", "city": "Sherbrooke", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "1dc8735c-fee5-4daa-5e08-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Champ-de-Mars", "street": "515, rue Moore", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1C1", "country": "Canada", "latitude": 45.4056849, "longitude": -71.9013374, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fcc84a46-7515-4c2c-5e09-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alguéric-Bussière", "street": "600, rue McManamy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M7", "country": "Canada", "latitude": 45.389014, "longitude": -71.895577, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "deda3e4a-8c41-4dc8-5e0a-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Debonair", "street": "445, rue de la Caravelle", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3B4", "country": "Canada", "latitude": 45.4301099, "longitude": -71.8941065, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e3593a96-40a5-4902-5e0b-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Reine-du-Monde", "street": "1190, rue Bowen Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2H1", "country": "Canada", "latitude": 45.388958, "longitude": -71.868988, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f00387de-3846-4cf2-5e0c-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Bois-Beckett", "street": "2315, rue Beckett", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 4N4", "country": "Canada", "latitude": 45.4219286, "longitude": -71.9309155, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0f503560-0641-41a2-5e0d-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dorilas-Gagnon", "street": "115, rue Notre Dame", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0S1", "country": "Canada", "latitude": 45.474913, "longitude": -71.948891, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "97c35dc0-7807-4a15-5e0e-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Gilles-Charland", "street": "2515, chemin Galvin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4K5", "country": "Canada", "latitude": 45.40675, "longitude": -71.834059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "773febbe-fda8-4094-5e0f-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Rive", "street": "1, rue Wilfrid-Laurier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0P3", "country": "Canada", "latitude": 45.475847, "longitude": -71.939976, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "218f6e69-c787-41e7-5e10-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Couturier", "street": "1372, rue Couturier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T4", "country": "Canada", "latitude": 45.42588, "longitude": -71.878447, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f8c880d8-bfe1-449c-5e11-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Judes-O.-Camirand", "street": "175, rue Camirand", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4J5", "country": "Canada", "latitude": 45.3976299, "longitude": -71.8940285, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3d54e520-0642-41d1-5e12-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Saint-Alphonse-de-Liguori", "street": "Rue Argyll", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1J", "country": "Canada", "latitude": 45.4158546, "longitude": -71.9138151, "streetViewOrientation": 0}, {"id": "85101be8-eae3-43a3-5e13-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Saint-François", "street": "215, rue Saint-François Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2C7", "country": "Canada", "latitude": 45.406531, "longitude": -71.889129, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "41d23a0b-43a2-43b7-5e14-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antonio-Montour", "street": "393, rue Murray", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2K5", "country": "Canada", "latitude": 45.4018299, "longitude": -71.879594, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e04d8933-f82b-4453-2901-08d82a5edbb9", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Édouard-Boudreau", "street": "44, 10e Avenue Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2R5", "country": "Canada", "latitude": 45.407188, "longitude": -71.872455, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d5e9e154-4ee8-43ef-2933-08d94c531395", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Stade Amédée-Roy", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135424, "longitude": -71.8874873, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b1b931e8-7532-4a78-dd4a-08d9841b93ab", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Square Queen", "street": "152, rue Queen", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0E5", "country": "Canada", "latitude": 45.368173, "longitude": -71.8564843, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "08b6edfa-50b9-4c9a-15af-08da4a204016", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Félix-Thibault", "street": "Rue King Est", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2C4", "country": "Canada", "latitude": 45.4037528, "longitude": -71.8851667, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "4ea284dd-6540-4817-b4a3-08da65c7628b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Plage-Municipale", "street": "790, avenue du Parc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 3N6", "country": "Canada", "latitude": 45.3352161, "longitude": -72.0240339, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "eb5bde3d-80bb-434f-a7cb-08da7a54905f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Placette King Est", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.4078878, "longitude": -71.8774954, "streetViewOrientation": 0}, {"id": "2835e91d-6f9a-49da-df92-08dab0681bf1", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des vétérans", "street": "150, rue Queen", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1M 1Z6", "country": "Canada", "latitude": 45.36793, "longitude": -71.85662, "streetViewOrientation": 0}, {"id": "82fcc444-7f48-408d-367d-08db3f6bf203", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bibeau", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.412851, "longitude": -71.8260361, "streetViewOrientation": 0}, {"id": "3125d780-fa7b-4039-2bdd-08db4fcc083b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Optimistes", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4159317, "longitude": -71.8780521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "49965d13-f5b2-4b74-f9aa-08db52214d8a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Base de plein air André-Nadeau", "street": "5302, chemin Blanchette", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 0C7", "country": "Canada", "latitude": 45.334204, "longitude": -71.992604, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e0cfe122-20a2-4b4e-fc95-08db613a5614", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Coeur-Immaculé-de-Marie", "street": "1055, rue Chalifoux", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 1S5", "country": "Canada", "latitude": 45.40222, "longitude": -71.865765, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2854ce67-8b7c-451d-eab9-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Maillé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4247504, "longitude": -71.8642885, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7910d2eb-15d2-41f8-eaba-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arthur-Tardif", "street": "Rue Saint-Nicolas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0T6", "country": "Canada", "latitude": 45.3888704, "longitude": -71.9988492, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2b14a409-af80-4a94-eabb-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Deux-Ruisseaux", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3644971, "longitude": -71.9610942, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fab8cd91-06d5-4d59-eabc-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ernest-Bergeron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4840971, "longitude": -71.9488059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b22885dc-e27f-48af-eabd-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Centennial", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3693775, "longitude": -71.8604137, "streetViewOrientation": 0}, {"id": "6ca5e8e3-50e7-4c47-eabe-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jules-Richard", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.345154, "longitude": -72.0392869, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b6697215-3221-40c6-eabf-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mi-Vallon", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3805964, "longitude": -71.9838314, "streetViewOrientation": 0}, {"id": "515b7f65-aea4-44e6-eac0-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Saint-Boniface", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3993541, "longitude": -71.9369409, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "94856a02-5573-476c-af4d-08db78aaea5c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Hélène-Boullé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4213455, "longitude": -71.9152519, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "374155d2-4dda-4c4d-e516-08dbb521299c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Carré Strathcona", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4045254, "longitude": -71.8925497, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "275859be-04f1-4d39-522a-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Victorin", "street": "Parc Marie-Victorin, Sherbrooke, QC J1L 2K5", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3919388, "longitude": -71.9825535, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "417827be-fe21-406a-522b-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arsène-Goyette", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3853384, "longitude": -71.8879616, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f111fa1f-ebc3-4502-120a-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Quatre-Pins", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3972165, "longitude": -71.9285062, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1e38a22f-b306-4bc5-120b-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de l'Ancienne-Caserne", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4020698, "longitude": -71.9042146, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "87604c2b-dadf-488c-dc88-08dc8fce9f0a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Thérèse-Lecomte", "street": "Sherbrooke, QC J1N 2L2", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3622594, "longitude": -71.9953775, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7ae82c6d-846c-4c6a-63e2-08dca1b91eaf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Marais Réal-D.-Carbonneau", "street": "Rue Lévesque", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 1M4", "country": "Canada", "latitude": 45.4152242, "longitude": -71.8977658, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e57d8282-fa2b-4bcb-23b1-08dcab3da2a8", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Marie-Médiatrice", "street": "3050, rue Jeanne-d'Arc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1K 1C3", "country": "Canada", "latitude": 45.3881361, "longitude": -71.9477288, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6c0b8639-7799-4e33-fb71-08dccd20d792", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Cénotaphe sur la rue King au centre ville", "street": "166, rue King Ouest", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1H", "country": "Canada", "latitude": 45.4014123, "longitude": -71.8913006, "streetViewOrientation": 0}, {"id": "48b4bb25-5698-40a2-65d0-08dcd296f618", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Pyrénées", "street": "2631, rue de l'Atlas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 0C7", "country": "Canada", "latitude": 45.4224771, "longitude": -71.8370252, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5c73443e-799c-4823-a51e-08dd1ed17b2d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Desranleau", "street": "1795, rue Ovila-Beaudoin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 3Z4", "country": "Canada", "latitude": 45.4077799, "longitude": -71.8494655, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6f8cb08e-882b-4f36-5f95-08dd828905dc", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère, devant le jardin communautaire Espace de la diversité", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3743284, "longitude": -71.8933644, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2037eb54-3ffa-4ed2-b3a3-08ddd9a5b408", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Kiosque Laurette-Giguère-de-Montigny (parc Jacques-Cartier)", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 3V2", "country": "Canada", "latitude": 45.3975395, "longitude": -71.9169177, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0dea5555-a7ef-46c1-4733-08dde0baec3f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Halte Turgeon-Gaudreau", "street": "4815, boulevard de l'Université", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M4", "country": "Canada", "latitude": 45.354616, "longitude": -71.9754349, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "56b39a47-288e-4527-0c78-08ddff86527d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue, stationnement Dunant", "street": "1875, rue Dunant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6L5", "country": "Canada", "latitude": 45.377376, "longitude": -71.9090846, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0ea059d9-79a5-4f46-fdc1-08de74726865", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place de la Gare", "street": "806, place de la Gare", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0G8", "country": "Canada", "latitude": 45.3973162, "longitude": -71.8972144, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3291a259-688d-4b2e-af8e-08de794851fb", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dolor-Biron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4034079, "longitude": -71.8805717, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "047d3941-25ce-4faa-489e-08de8e601892", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Léonard-Cohen", "street": "Rue du Chardonnay", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1L 0J3", "country": "Canada", "latitude": 45.4177174, "longitude": -71.9490767, "streetViewOrientation": 0}, {"id": "90dd2962-cd0e-424a-4b7d-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "8f20ba56-8528-4a7e-4b7e-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Petit-Canada", "street": "rue Alexandre", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4S9", "country": "Canada", "latitude": 45.3954233, "longitude": -71.8910082, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "27b162b1-9060-4f92-ce34-08ded06aecba", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacob-Nicol", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4040761, "longitude": -71.918024, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "95f3ecbe-0aa9-4d8c-0f1f-08def49980b6", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place Kassiwi", "street": "68, rue Wellington Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 5E9", "country": "Canada", "latitude": 45.4006351, "longitude": -71.8889649, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}], "customFields": []}, "clients": [], "clientAddressId": "8e45604f-dd8a-4e4a-5df9-08d79f8c29b0", "phone": {}, "noFeePhone": {}, "addressURL": {"fr": "", "en": ""}, "categories": {"language": "fr", "block": [{"id": "c50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Secteur", "en": "Secteur"}, "children": [{"id": "c58e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "de Rock Forest", "en": "de Rock Forest"}, "parentId": "c50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 75}], "isBlock": true, "order": 75}, {"id": "b50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Clientèle", "en": "Clientele"}, "children": [{"id": "b55e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Aîné", "en": "Seniors"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b51e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Bébé et tout-petit", "en": "Babies and toddlers"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b56e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Famille", "en": "Families"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b52e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Enfant", "en": "Children"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b54e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adulte", "en": "Adults"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b53e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adolescent", "en": "Teens"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}], "isBlock": true, "order": 76}], "base": [{"id": "333f4f46-5953-e611-80ea-00155d09650f", "name": {"fr": "Animation", "en": "Entertainment"}, "isBlock": false, "order": 0}, {"id": "33398c39-5953-e611-80ea-00155d09650f", "name": {"fr": "Loisir", "en": "Loisir"}, "isBlock": false, "order": 0}, {"id": "333d1576-01e7-e611-80f8-00155d09650f", "name": {"fr": "Spectacle", "en": "Shows"}, "isBlock": false, "order": 0}], "rawIds": ["b55e6814-478d-e911-9b6f-00155da78728", "33398c39-5953-e611-80ea-00155d09650f", "c58e6814-478d-e911-9b6f-00155da78728", "333d1576-01e7-e611-80f8-00155d09650f", "b51e6814-478d-e911-9b6f-00155da78728", "b56e6814-478d-e911-9b6f-00155da78728", "b52e6814-478d-e911-9b6f-00155da78728", "c50e6814-478d-e911-9b6f-00155da78728", "333f4f46-5953-e611-80ea-00155d09650f", "b54e6814-478d-e911-9b6f-00155da78728", "b50e6814-478d-e911-9b6f-00155da78728", "b53e6814-478d-e911-9b6f-00155da78728"]}, "nePasAfficherLePrix": false, "payant": false, "customFields": [{"customFieldId": "ba8bfe3d-27da-e911-8192-00155d4d070d", "textValue": "https://www.facebook.com/mdjSergeForest", "dateModified": "2026-05-27T14:05:45.997Z"}, {"customFieldId": "9606ac36-27da-e911-8192-00155d4d070d", "textValue": "loisirsetviecommunautaire@sherbrooke.ca", "dateModified": "2026-05-27T14:05:45.997Z"}], "otherPictures": [], "isHighlighted": false, "isOnline": true}, {"id": "99164862-e299-442a-8932-9e8d9e86ee25", "visibleId": 2261, "releaseDate": "2026-08-01T00:00:00Z", "modificationDate": "2026-05-27T13:06:13.41Z", "periods": [{"type": 0, "dateDebut": "2026-08-01T00:00:00Z", "dateFin": "2026-08-01T02:30:00Z", "jourLundi": true, "jourMardi": true, "jourMercredi": true, "jourJeudi": true, "jourVendredi": true, "jourSamedi": true, "jourDimanche": true, "label": {"fr": "Le 31 juillet 2026", "en": "July 31st, 2026"}, "labelHours": {"fr": "de 20h00 à 22h30", "en": "from 20h00 to 22h30"}, "allDays": true, "labelDays": {"fr": "tous les jours", "en": "every day"}}], "languageAvailability": {"fr": true, "en": true}, "name": {"fr": "Cinéma en plein air au parc Arsène-Goyette", "en": "Cinéma en plein air au parc Arsène-Goyette"}, "serializeContent": false, "alias": {"fr": "cinema-en-plein-air-au-parc-arsene-goyette", "en": "cinema-en-plein-air-au-parc-arsene-goyette"}, "keyWords": {"fr": "", "en": ""}, "moduleId": "333327b9-4d53-e611-80ea-00155d09650f", "moduleURL": {"fr": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites/{id}/{slug}", "en": "https://www.sherbrooke.ca/en/culture-sports-and-recreation/event-calendar/{id}/{slug}"}, "fullUrl": {"fr": "/fr/culture-sports-et-loisirs/calendrier-des-activites/2261/cinema-en-plein-air-au-parc-arsene-goyette", "en": "/en/culture-sports-and-recreation/event-calendar/2261/cinema-en-plein-air-au-parc-arsene-goyette"}, "meta": {"fr": "Cinéma en plein air parc Arsène-Goyette le vendredi 31 juillet.", "en": "Cinéma en plein air parc Arsène-Goyette le vendredi 31 juillet."}, "mainPicture": "cinema-plein-air-evenement-720x560px.jpg", "publishInfos": {"isPublished": true, "dateMiseEnLigne": "2026-05-27T13:06:13.417Z", "isHighlighted": false, "isVisibleInMenus": true, "hasSharingButtons": true}, "client": {"id": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "visibleId": 419, "name": "Parcs", "address": {"country": "", "latitude": 0.0, "longitude": 0.0}, "phone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": ""}, "faxPhone": {"number": "", "countryCode": ""}, "otherAddresses": [{"id": "88334365-1518-4086-79df-08d7404e34b7", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacques-Cartier", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 2C3", "country": "Canada", "latitude": 45.395034, "longitude": -71.9175917, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "c2fe816c-6bb5-4fdd-5462-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Atto-Beaver", "street": "Rue Saint Francis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0B3", "country": "Canada", "latitude": 45.3766588, "longitude": -71.8542815, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2dd3adb7-6d89-4a87-5463-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Jardins-Fleuris", "street": "1245 Rue Papineau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 4C4", "country": "Canada", "latitude": 45.416137, "longitude": -71.862489, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "bbad822d-002b-4377-5464-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Nault", "street": "", "city": "Sherbrooke", "province": "Québec", "postalCode": "", "country": "", "latitude": 45.468768, "longitude": -71.940445, "streetViewOrientation": 0}, {"id": "bd36345e-76fa-4781-5df5-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Lucien-Blanchard", "street": "755, rue Cabana", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1K 0A6", "country": "Canada", "latitude": 45.3944121, "longitude": -71.9283057, "streetViewOrientation": 0}, {"id": "fb212169-5ae9-492f-5df6-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bureau", "street": "925, rue Walsh", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4L7", "country": "Canada", "latitude": 45.3924261, "longitude": -71.8692436, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "ee847adc-9043-4ce3-5df7-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue", "street": "1440, rue Brébeuf", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3G2", "country": "Canada", "latitude": 45.384407, "longitude": -71.908385, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d00e7948-3a03-4ad3-5df8-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Domaine-Howard", "street": "1350, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1S3", "country": "Canada", "latitude": 45.4033749, "longitude": -71.9096689, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "8e45604f-dd8a-4e4a-5df9-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Beaulieu", "street": "755, rue Coombs", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2B7", "country": "Canada", "latitude": 45.3732878, "longitude": -71.9730694, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "885556b0-8786-49b9-5dfa-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc André-Viger", "street": "3275, rue Richard", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1Y2", "country": "Canada", "latitude": 45.410288, "longitude": -71.948647, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "07d31315-0a0f-4f37-5dfb-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Sylvie-Daigle", "street": "400, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2J9", "country": "Canada", "latitude": 45.411855, "longitude": -71.8844021, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1a70fcaa-a0f8-4571-5dfc-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ma-Villa", "street": "166, rue des Colibris", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0P2", "country": "Canada", "latitude": 45.398797, "longitude": -72.007653, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3bfba6af-39d6-4d60-5dfd-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Central", "street": "6161, rue du Président-Kennedy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2P5", "country": "Canada", "latitude": 45.3546979, "longitude": -71.9943063, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73883e6d-bcc9-40bf-5dfe-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Andrew-Sangster", "street": "1220, rue Kitchener", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3L1", "country": "Canada", "latitude": 45.387524, "longitude": -71.904691, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73eb2f6e-ed3f-4b1b-5dff-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Victoria", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135081, "longitude": -71.8876118, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "44581296-11af-4fbc-5e00-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Adrien-Cambron", "street": "365, 10e Avenue Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2T4", "country": "Canada", "latitude": 45.412988, "longitude": -71.874525, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5b1456f2-b2e2-409e-5e01-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Olivier-Chauveau", "street": "4445, rue Chauveau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1W2", "country": "Canada", "latitude": 45.385005, "longitude": -71.974137, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "72c90ce2-c50d-4a06-5e02-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Villageois", "street": "4540, chemin Saint-Roch Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0K8", "country": "Canada", "latitude": 45.3935267, "longitude": -72.0403563, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7b60689b-ca7d-4937-5e03-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Marquis-de-Montcalm", "street": "2070, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1T9", "country": "Canada", "latitude": 45.403079, "longitude": -71.92521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "85cee1c7-f735-4dd7-5e04-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alfred-Élie-Dufresne", "street": "521, rue Galt Ouest", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1Y8", "country": "Canada", "latitude": 45.395532, "longitude": -71.892718, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "db08e77a-d3ea-4f5d-5e05-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3748433, "longitude": -71.8920002, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6b96c415-5edc-4167-5e06-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Quintal", "street": "1671, chemin Duplessis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0C2", "country": "Canada", "latitude": 45.4306045, "longitude": -71.8499278, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "71c0d030-4262-41d0-5e07-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antoine-Racine", "street": "rue Ball", "city": "Sherbrooke", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "1dc8735c-fee5-4daa-5e08-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Champ-de-Mars", "street": "515, rue Moore", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1C1", "country": "Canada", "latitude": 45.4056849, "longitude": -71.9013374, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fcc84a46-7515-4c2c-5e09-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alguéric-Bussière", "street": "600, rue McManamy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M7", "country": "Canada", "latitude": 45.389014, "longitude": -71.895577, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "deda3e4a-8c41-4dc8-5e0a-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Debonair", "street": "445, rue de la Caravelle", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3B4", "country": "Canada", "latitude": 45.4301099, "longitude": -71.8941065, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e3593a96-40a5-4902-5e0b-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Reine-du-Monde", "street": "1190, rue Bowen Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2H1", "country": "Canada", "latitude": 45.388958, "longitude": -71.868988, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f00387de-3846-4cf2-5e0c-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Bois-Beckett", "street": "2315, rue Beckett", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 4N4", "country": "Canada", "latitude": 45.4219286, "longitude": -71.9309155, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0f503560-0641-41a2-5e0d-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dorilas-Gagnon", "street": "115, rue Notre Dame", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0S1", "country": "Canada", "latitude": 45.474913, "longitude": -71.948891, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "97c35dc0-7807-4a15-5e0e-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Gilles-Charland", "street": "2515, chemin Galvin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4K5", "country": "Canada", "latitude": 45.40675, "longitude": -71.834059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "773febbe-fda8-4094-5e0f-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Rive", "street": "1, rue Wilfrid-Laurier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0P3", "country": "Canada", "latitude": 45.475847, "longitude": -71.939976, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "218f6e69-c787-41e7-5e10-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Couturier", "street": "1372, rue Couturier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T4", "country": "Canada", "latitude": 45.42588, "longitude": -71.878447, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f8c880d8-bfe1-449c-5e11-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Judes-O.-Camirand", "street": "175, rue Camirand", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4J5", "country": "Canada", "latitude": 45.3976299, "longitude": -71.8940285, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3d54e520-0642-41d1-5e12-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Saint-Alphonse-de-Liguori", "street": "Rue Argyll", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1J", "country": "Canada", "latitude": 45.4158546, "longitude": -71.9138151, "streetViewOrientation": 0}, {"id": "85101be8-eae3-43a3-5e13-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Saint-François", "street": "215, rue Saint-François Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2C7", "country": "Canada", "latitude": 45.406531, "longitude": -71.889129, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "41d23a0b-43a2-43b7-5e14-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antonio-Montour", "street": "393, rue Murray", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2K5", "country": "Canada", "latitude": 45.4018299, "longitude": -71.879594, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e04d8933-f82b-4453-2901-08d82a5edbb9", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Édouard-Boudreau", "street": "44, 10e Avenue Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2R5", "country": "Canada", "latitude": 45.407188, "longitude": -71.872455, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d5e9e154-4ee8-43ef-2933-08d94c531395", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Stade Amédée-Roy", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135424, "longitude": -71.8874873, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b1b931e8-7532-4a78-dd4a-08d9841b93ab", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Square Queen", "street": "152, rue Queen", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0E5", "country": "Canada", "latitude": 45.368173, "longitude": -71.8564843, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "08b6edfa-50b9-4c9a-15af-08da4a204016", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Félix-Thibault", "street": "Rue King Est", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2C4", "country": "Canada", "latitude": 45.4037528, "longitude": -71.8851667, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "4ea284dd-6540-4817-b4a3-08da65c7628b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Plage-Municipale", "street": "790, avenue du Parc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 3N6", "country": "Canada", "latitude": 45.3352161, "longitude": -72.0240339, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "eb5bde3d-80bb-434f-a7cb-08da7a54905f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Placette King Est", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.4078878, "longitude": -71.8774954, "streetViewOrientation": 0}, {"id": "2835e91d-6f9a-49da-df92-08dab0681bf1", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des vétérans", "street": "150, rue Queen", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1M 1Z6", "country": "Canada", "latitude": 45.36793, "longitude": -71.85662, "streetViewOrientation": 0}, {"id": "82fcc444-7f48-408d-367d-08db3f6bf203", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bibeau", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.412851, "longitude": -71.8260361, "streetViewOrientation": 0}, {"id": "3125d780-fa7b-4039-2bdd-08db4fcc083b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Optimistes", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4159317, "longitude": -71.8780521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "49965d13-f5b2-4b74-f9aa-08db52214d8a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Base de plein air André-Nadeau", "street": "5302, chemin Blanchette", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 0C7", "country": "Canada", "latitude": 45.334204, "longitude": -71.992604, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e0cfe122-20a2-4b4e-fc95-08db613a5614", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Coeur-Immaculé-de-Marie", "street": "1055, rue Chalifoux", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 1S5", "country": "Canada", "latitude": 45.40222, "longitude": -71.865765, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2854ce67-8b7c-451d-eab9-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Maillé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4247504, "longitude": -71.8642885, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7910d2eb-15d2-41f8-eaba-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arthur-Tardif", "street": "Rue Saint-Nicolas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0T6", "country": "Canada", "latitude": 45.3888704, "longitude": -71.9988492, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2b14a409-af80-4a94-eabb-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Deux-Ruisseaux", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3644971, "longitude": -71.9610942, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fab8cd91-06d5-4d59-eabc-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ernest-Bergeron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4840971, "longitude": -71.9488059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b22885dc-e27f-48af-eabd-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Centennial", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3693775, "longitude": -71.8604137, "streetViewOrientation": 0}, {"id": "6ca5e8e3-50e7-4c47-eabe-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jules-Richard", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.345154, "longitude": -72.0392869, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b6697215-3221-40c6-eabf-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mi-Vallon", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3805964, "longitude": -71.9838314, "streetViewOrientation": 0}, {"id": "515b7f65-aea4-44e6-eac0-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Saint-Boniface", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3993541, "longitude": -71.9369409, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "94856a02-5573-476c-af4d-08db78aaea5c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Hélène-Boullé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4213455, "longitude": -71.9152519, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "374155d2-4dda-4c4d-e516-08dbb521299c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Carré Strathcona", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4045254, "longitude": -71.8925497, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "275859be-04f1-4d39-522a-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Victorin", "street": "Parc Marie-Victorin, Sherbrooke, QC J1L 2K5", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3919388, "longitude": -71.9825535, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "417827be-fe21-406a-522b-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arsène-Goyette", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3853384, "longitude": -71.8879616, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f111fa1f-ebc3-4502-120a-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Quatre-Pins", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3972165, "longitude": -71.9285062, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1e38a22f-b306-4bc5-120b-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de l'Ancienne-Caserne", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4020698, "longitude": -71.9042146, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "87604c2b-dadf-488c-dc88-08dc8fce9f0a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Thérèse-Lecomte", "street": "Sherbrooke, QC J1N 2L2", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3622594, "longitude": -71.9953775, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7ae82c6d-846c-4c6a-63e2-08dca1b91eaf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Marais Réal-D.-Carbonneau", "street": "Rue Lévesque", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 1M4", "country": "Canada", "latitude": 45.4152242, "longitude": -71.8977658, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e57d8282-fa2b-4bcb-23b1-08dcab3da2a8", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Marie-Médiatrice", "street": "3050, rue Jeanne-d'Arc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1K 1C3", "country": "Canada", "latitude": 45.3881361, "longitude": -71.9477288, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6c0b8639-7799-4e33-fb71-08dccd20d792", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Cénotaphe sur la rue King au centre ville", "street": "166, rue King Ouest", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1H", "country": "Canada", "latitude": 45.4014123, "longitude": -71.8913006, "streetViewOrientation": 0}, {"id": "48b4bb25-5698-40a2-65d0-08dcd296f618", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Pyrénées", "street": "2631, rue de l'Atlas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 0C7", "country": "Canada", "latitude": 45.4224771, "longitude": -71.8370252, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5c73443e-799c-4823-a51e-08dd1ed17b2d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Desranleau", "street": "1795, rue Ovila-Beaudoin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 3Z4", "country": "Canada", "latitude": 45.4077799, "longitude": -71.8494655, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6f8cb08e-882b-4f36-5f95-08dd828905dc", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère, devant le jardin communautaire Espace de la diversité", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3743284, "longitude": -71.8933644, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2037eb54-3ffa-4ed2-b3a3-08ddd9a5b408", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Kiosque Laurette-Giguère-de-Montigny (parc Jacques-Cartier)", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 3V2", "country": "Canada", "latitude": 45.3975395, "longitude": -71.9169177, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0dea5555-a7ef-46c1-4733-08dde0baec3f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Halte Turgeon-Gaudreau", "street": "4815, boulevard de l'Université", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M4", "country": "Canada", "latitude": 45.354616, "longitude": -71.9754349, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "56b39a47-288e-4527-0c78-08ddff86527d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue, stationnement Dunant", "street": "1875, rue Dunant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6L5", "country": "Canada", "latitude": 45.377376, "longitude": -71.9090846, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0ea059d9-79a5-4f46-fdc1-08de74726865", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place de la Gare", "street": "806, place de la Gare", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0G8", "country": "Canada", "latitude": 45.3973162, "longitude": -71.8972144, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3291a259-688d-4b2e-af8e-08de794851fb", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dolor-Biron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4034079, "longitude": -71.8805717, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "047d3941-25ce-4faa-489e-08de8e601892", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Léonard-Cohen", "street": "Rue du Chardonnay", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1L 0J3", "country": "Canada", "latitude": 45.4177174, "longitude": -71.9490767, "streetViewOrientation": 0}, {"id": "90dd2962-cd0e-424a-4b7d-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "8f20ba56-8528-4a7e-4b7e-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Petit-Canada", "street": "rue Alexandre", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4S9", "country": "Canada", "latitude": 45.3954233, "longitude": -71.8910082, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "27b162b1-9060-4f92-ce34-08ded06aecba", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacob-Nicol", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4040761, "longitude": -71.918024, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "95f3ecbe-0aa9-4d8c-0f1f-08def49980b6", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place Kassiwi", "street": "68, rue Wellington Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 5E9", "country": "Canada", "latitude": 45.4006351, "longitude": -71.8889649, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}], "customFields": []}, "clients": [], "clientAddressId": "417827be-fe21-406a-522b-08dc6e07ec54", "phone": {}, "noFeePhone": {}, "addressURL": {"fr": "", "en": ""}, "categories": {"language": "fr", "block": [{"id": "c50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Secteur", "en": "Secteur"}, "children": [{"id": "c57e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "du Mont-Bellevue", "en": "du Mont-Bellevue"}, "parentId": "c50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 75}], "isBlock": true, "order": 75}, {"id": "b50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Clientèle", "en": "Clientele"}, "children": [{"id": "b56e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Famille", "en": "Families"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b55e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Aîné", "en": "Seniors"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b51e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Bébé et tout-petit", "en": "Babies and toddlers"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b54e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adulte", "en": "Adults"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b52e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Enfant", "en": "Children"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b53e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adolescent", "en": "Teens"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}], "isBlock": true, "order": 76}], "base": [{"id": "333f4f46-5953-e611-80ea-00155d09650f", "name": {"fr": "Animation", "en": "Entertainment"}, "isBlock": false, "order": 0}, {"id": "33398c39-5953-e611-80ea-00155d09650f", "name": {"fr": "Loisir", "en": "Loisir"}, "isBlock": false, "order": 0}, {"id": "333d1576-01e7-e611-80f8-00155d09650f", "name": {"fr": "Spectacle", "en": "Shows"}, "isBlock": false, "order": 0}], "rawIds": ["b56e6814-478d-e911-9b6f-00155da78728", "c50e6814-478d-e911-9b6f-00155da78728", "b55e6814-478d-e911-9b6f-00155da78728", "b51e6814-478d-e911-9b6f-00155da78728", "33398c39-5953-e611-80ea-00155d09650f", "333d1576-01e7-e611-80f8-00155d09650f", "333f4f46-5953-e611-80ea-00155d09650f", "b54e6814-478d-e911-9b6f-00155da78728", "c57e6814-478d-e911-9b6f-00155da78728", "b52e6814-478d-e911-9b6f-00155da78728", "b50e6814-478d-e911-9b6f-00155da78728", "b53e6814-478d-e911-9b6f-00155da78728"]}, "nePasAfficherLePrix": false, "payant": false, "customFields": [{"customFieldId": "9606ac36-27da-e911-8192-00155d4d070d", "textValue": "loisirsetviecommunautaire@sherbrooke.ca", "dateModified": "2026-05-27T13:06:13.473Z"}], "otherPictures": [], "isHighlighted": false, "isOnline": true}, {"id": "2b253a44-ba1a-4a95-931e-08d93579e506", "visibleId": 287, "releaseDate": "2026-08-01T11:30:00Z", "modificationDate": "2026-07-10T17:14:31.043Z", "periods": [{"type": 0, "dateDebut": "2026-08-01T11:30:00Z", "dateFin": "2026-08-01T21:30:00Z", "jourLundi": true, "jourMardi": true, "jourMercredi": true, "jourJeudi": true, "jourVendredi": true, "jourSamedi": true, "jourDimanche": true, "label": {"fr": "Le 1er août 2026", "en": "August 1st, 2026"}, "labelHours": {"fr": "de 7h30 à 17h30", "en": "from 7h30 to 17h30"}, "allDays": true, "labelDays": {"fr": "tous les jours", "en": "every day"}}], "languageAvailability": {"fr": true, "en": true}, "name": {"fr": "Relais du Mont-Bellevue 10-4", "en": "Relais du Mont-Bellevue 10-4"}, "serializeContent": false, "alias": {"fr": "relais-du-mont-bellevue-10-4", "en": "relais-du-mont-bellevue-10-4"}, "keyWords": {"fr": "Relais, campagne, jouets, pompiers", "en": ""}, "moduleId": "333327b9-4d53-e611-80ea-00155d09650f", "moduleURL": {"fr": "https://www.sherbrooke.ca/fr/culture-sports-et-loisirs/calendrier-des-activites/{id}/{slug}", "en": "https://www.sherbrooke.ca/en/culture-sports-and-recreation/event-calendar/{id}/{slug}"}, "fullUrl": {"fr": "/fr/culture-sports-et-loisirs/calendrier-des-activites/287/relais-du-mont-bellevue-10-4", "en": "/en/culture-sports-and-recreation/event-calendar/287/relais-du-mont-bellevue-10-4"}, "meta": {"fr": "Le Relais du mont Bellevue 10-4 est une course en sentiers par équipe, au cœur de la Ville de Sherbrooke.", "en": ""}, "mainPicture": "287-eve-25-2-relais-mb-10-4.jpg", "publishInfos": {"isPublished": true, "dateMiseEnLigne": "2026-07-10T17:14:31.05Z", "isHighlighted": false, "isVisibleInMenus": true, "hasSharingButtons": true}, "client": {"id": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "visibleId": 419, "name": "Parcs", "address": {"country": "", "latitude": 0.0, "longitude": 0.0}, "phone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": ""}, "faxPhone": {"number": "", "countryCode": ""}, "otherAddresses": [{"id": "88334365-1518-4086-79df-08d7404e34b7", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacques-Cartier", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 2C3", "country": "Canada", "latitude": 45.395034, "longitude": -71.9175917, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "c2fe816c-6bb5-4fdd-5462-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Atto-Beaver", "street": "Rue Saint Francis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0B3", "country": "Canada", "latitude": 45.3766588, "longitude": -71.8542815, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2dd3adb7-6d89-4a87-5463-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Jardins-Fleuris", "street": "1245 Rue Papineau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 4C4", "country": "Canada", "latitude": 45.416137, "longitude": -71.862489, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "bbad822d-002b-4377-5464-08d79846d004", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Nault", "street": "", "city": "Sherbrooke", "province": "Québec", "postalCode": "", "country": "", "latitude": 45.468768, "longitude": -71.940445, "streetViewOrientation": 0}, {"id": "bd36345e-76fa-4781-5df5-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Lucien-Blanchard", "street": "755, rue Cabana", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1K 0A6", "country": "Canada", "latitude": 45.3944121, "longitude": -71.9283057, "streetViewOrientation": 0}, {"id": "fb212169-5ae9-492f-5df6-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bureau", "street": "925, rue Walsh", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4L7", "country": "Canada", "latitude": 45.3924261, "longitude": -71.8692436, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "ee847adc-9043-4ce3-5df7-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue", "street": "1440, rue Brébeuf", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3G2", "country": "Canada", "latitude": 45.384407, "longitude": -71.908385, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d00e7948-3a03-4ad3-5df8-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Domaine-Howard", "street": "1350, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1S3", "country": "Canada", "latitude": 45.4033749, "longitude": -71.9096689, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "8e45604f-dd8a-4e4a-5df9-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Beaulieu", "street": "755, rue Coombs", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2B7", "country": "Canada", "latitude": 45.3732878, "longitude": -71.9730694, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "885556b0-8786-49b9-5dfa-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc André-Viger", "street": "3275, rue Richard", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1Y2", "country": "Canada", "latitude": 45.410288, "longitude": -71.948647, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "07d31315-0a0f-4f37-5dfb-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Sylvie-Daigle", "street": "400, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2J9", "country": "Canada", "latitude": 45.411855, "longitude": -71.8844021, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1a70fcaa-a0f8-4571-5dfc-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ma-Villa", "street": "166, rue des Colibris", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0P2", "country": "Canada", "latitude": 45.398797, "longitude": -72.007653, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3bfba6af-39d6-4d60-5dfd-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Central", "street": "6161, rue du Président-Kennedy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 2P5", "country": "Canada", "latitude": 45.3546979, "longitude": -71.9943063, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73883e6d-bcc9-40bf-5dfe-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Andrew-Sangster", "street": "1220, rue Kitchener", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 3L1", "country": "Canada", "latitude": 45.387524, "longitude": -71.904691, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "73eb2f6e-ed3f-4b1b-5dff-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Victoria", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135081, "longitude": -71.8876118, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "44581296-11af-4fbc-5e00-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Adrien-Cambron", "street": "365, 10e Avenue Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2T4", "country": "Canada", "latitude": 45.412988, "longitude": -71.874525, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5b1456f2-b2e2-409e-5e01-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Olivier-Chauveau", "street": "4445, rue Chauveau", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1L 1W2", "country": "Canada", "latitude": 45.385005, "longitude": -71.974137, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "72c90ce2-c50d-4a06-5e02-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Villageois", "street": "4540, chemin Saint-Roch Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0K8", "country": "Canada", "latitude": 45.3935267, "longitude": -72.0403563, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7b60689b-ca7d-4937-5e03-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Marquis-de-Montcalm", "street": "2070, boulevard de Portland", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 1T9", "country": "Canada", "latitude": 45.403079, "longitude": -71.92521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "85cee1c7-f735-4dd7-5e04-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alfred-Élie-Dufresne", "street": "521, rue Galt Ouest", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1Y8", "country": "Canada", "latitude": 45.395532, "longitude": -71.892718, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "db08e77a-d3ea-4f5d-5e05-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3748433, "longitude": -71.8920002, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6b96c415-5edc-4167-5e06-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Quintal", "street": "1671, chemin Duplessis", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0C2", "country": "Canada", "latitude": 45.4306045, "longitude": -71.8499278, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "71c0d030-4262-41d0-5e07-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antoine-Racine", "street": "rue Ball", "city": "Sherbrooke", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "1dc8735c-fee5-4daa-5e08-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Champ-de-Mars", "street": "515, rue Moore", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 1C1", "country": "Canada", "latitude": 45.4056849, "longitude": -71.9013374, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fcc84a46-7515-4c2c-5e09-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Alguéric-Bussière", "street": "600, rue McManamy", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M7", "country": "Canada", "latitude": 45.389014, "longitude": -71.895577, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "deda3e4a-8c41-4dc8-5e0a-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Debonair", "street": "445, rue de la Caravelle", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3B4", "country": "Canada", "latitude": 45.4301099, "longitude": -71.8941065, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e3593a96-40a5-4902-5e0b-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Reine-du-Monde", "street": "1190, rue Bowen Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2H1", "country": "Canada", "latitude": 45.388958, "longitude": -71.868988, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f00387de-3846-4cf2-5e0c-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Bois-Beckett", "street": "2315, rue Beckett", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 4N4", "country": "Canada", "latitude": 45.4219286, "longitude": -71.9309155, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0f503560-0641-41a2-5e0d-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dorilas-Gagnon", "street": "115, rue Notre Dame", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0S1", "country": "Canada", "latitude": 45.474913, "longitude": -71.948891, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "97c35dc0-7807-4a15-5e0e-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Gilles-Charland", "street": "2515, chemin Galvin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 4K5", "country": "Canada", "latitude": 45.40675, "longitude": -71.834059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "773febbe-fda8-4094-5e0f-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Rive", "street": "1, rue Wilfrid-Laurier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1C 0P3", "country": "Canada", "latitude": 45.475847, "longitude": -71.939976, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "218f6e69-c787-41e7-5e10-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Couturier", "street": "1372, rue Couturier", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T4", "country": "Canada", "latitude": 45.42588, "longitude": -71.878447, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f8c880d8-bfe1-449c-5e11-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Judes-O.-Camirand", "street": "175, rue Camirand", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4J5", "country": "Canada", "latitude": 45.3976299, "longitude": -71.8940285, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3d54e520-0642-41d1-5e12-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Saint-Alphonse-de-Liguori", "street": "Rue Argyll", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1J", "country": "Canada", "latitude": 45.4158546, "longitude": -71.9138151, "streetViewOrientation": 0}, {"id": "85101be8-eae3-43a3-5e13-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Saint-François", "street": "215, rue Saint-François Nord", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 2C7", "country": "Canada", "latitude": 45.406531, "longitude": -71.889129, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "41d23a0b-43a2-43b7-5e14-08d79f8c29b0", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Antonio-Montour", "street": "393, rue Murray", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2K5", "country": "Canada", "latitude": 45.4018299, "longitude": -71.879594, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e04d8933-f82b-4453-2901-08d82a5edbb9", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Édouard-Boudreau", "street": "44, 10e Avenue Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2R5", "country": "Canada", "latitude": 45.407188, "longitude": -71.872455, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "d5e9e154-4ee8-43ef-2933-08d94c531395", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Stade Amédée-Roy", "street": "600, rue du Cégep", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 3T6", "country": "Canada", "latitude": 45.4135424, "longitude": -71.8874873, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b1b931e8-7532-4a78-dd4a-08d9841b93ab", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Square Queen", "street": "152, rue Queen", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1M 0E5", "country": "Canada", "latitude": 45.368173, "longitude": -71.8564843, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "08b6edfa-50b9-4c9a-15af-08da4a204016", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Félix-Thibault", "street": "Rue King Est", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 2C4", "country": "Canada", "latitude": 45.4037528, "longitude": -71.8851667, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "4ea284dd-6540-4817-b4a3-08da65c7628b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de la Plage-Municipale", "street": "790, avenue du Parc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 3N6", "country": "Canada", "latitude": 45.3352161, "longitude": -72.0240339, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "eb5bde3d-80bb-434f-a7cb-08da7a54905f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Placette King Est", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.4078878, "longitude": -71.8774954, "streetViewOrientation": 0}, {"id": "2835e91d-6f9a-49da-df92-08dab0681bf1", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des vétérans", "street": "150, rue Queen", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1M 1Z6", "country": "Canada", "latitude": 45.36793, "longitude": -71.85662, "streetViewOrientation": 0}, {"id": "82fcc444-7f48-408d-367d-08db3f6bf203", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Bibeau", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.412851, "longitude": -71.8260361, "streetViewOrientation": 0}, {"id": "3125d780-fa7b-4039-2bdd-08db4fcc083b", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Optimistes", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4159317, "longitude": -71.8780521, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "49965d13-f5b2-4b74-f9aa-08db52214d8a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Base de plein air André-Nadeau", "street": "5302, chemin Blanchette", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1N 0C7", "country": "Canada", "latitude": 45.334204, "longitude": -71.992604, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e0cfe122-20a2-4b4e-fc95-08db613a5614", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Coeur-Immaculé-de-Marie", "street": "1055, rue Chalifoux", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 1S5", "country": "Canada", "latitude": 45.40222, "longitude": -71.865765, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2854ce67-8b7c-451d-eab9-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Maillé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4247504, "longitude": -71.8642885, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7910d2eb-15d2-41f8-eaba-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arthur-Tardif", "street": "Rue Saint-Nicolas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1R 0T6", "country": "Canada", "latitude": 45.3888704, "longitude": -71.9988492, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2b14a409-af80-4a94-eabb-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Deux-Ruisseaux", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3644971, "longitude": -71.9610942, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "fab8cd91-06d5-4d59-eabc-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Ernest-Bergeron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4840971, "longitude": -71.9488059, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b22885dc-e27f-48af-eabd-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Centennial", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3693775, "longitude": -71.8604137, "streetViewOrientation": 0}, {"id": "6ca5e8e3-50e7-4c47-eabe-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jules-Richard", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.345154, "longitude": -72.0392869, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "b6697215-3221-40c6-eabf-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mi-Vallon", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 45.3805964, "longitude": -71.9838314, "streetViewOrientation": 0}, {"id": "515b7f65-aea4-44e6-eac0-08db67817679", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Saint-Boniface", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3993541, "longitude": -71.9369409, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "94856a02-5573-476c-af4d-08db78aaea5c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Hélène-Boullé", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4213455, "longitude": -71.9152519, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "374155d2-4dda-4c4d-e516-08dbb521299c", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Carré Strathcona", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4045254, "longitude": -71.8925497, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "275859be-04f1-4d39-522a-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Marie-Victorin", "street": "Parc Marie-Victorin, Sherbrooke, QC J1L 2K5", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3919388, "longitude": -71.9825535, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "417827be-fe21-406a-522b-08dc6e07ec54", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Arsène-Goyette", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3853384, "longitude": -71.8879616, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "f111fa1f-ebc3-4502-120a-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Quatre-Pins", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3972165, "longitude": -71.9285062, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "1e38a22f-b306-4bc5-120b-08dc6f921bdf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de l'Ancienne-Caserne", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4020698, "longitude": -71.9042146, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "87604c2b-dadf-488c-dc88-08dc8fce9f0a", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Thérèse-Lecomte", "street": "Sherbrooke, QC J1N 2L2", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.3622594, "longitude": -71.9953775, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "7ae82c6d-846c-4c6a-63e2-08dca1b91eaf", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Marais Réal-D.-Carbonneau", "street": "Rue Lévesque", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 1M4", "country": "Canada", "latitude": 45.4152242, "longitude": -71.8977658, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "e57d8282-fa2b-4bcb-23b1-08dcab3da2a8", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc de Marie-Médiatrice", "street": "3050, rue Jeanne-d'Arc", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1K 1C3", "country": "Canada", "latitude": 45.3881361, "longitude": -71.9477288, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6c0b8639-7799-4e33-fb71-08dccd20d792", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Cénotaphe sur la rue King au centre ville", "street": "166, rue King Ouest", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1H", "country": "Canada", "latitude": 45.4014123, "longitude": -71.8913006, "streetViewOrientation": 0}, {"id": "48b4bb25-5698-40a2-65d0-08dcd296f618", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc des Pyrénées", "street": "2631, rue de l'Atlas", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1E 0C7", "country": "Canada", "latitude": 45.4224771, "longitude": -71.8370252, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "5c73443e-799c-4823-a51e-08dd1ed17b2d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Desranleau", "street": "1795, rue Ovila-Beaudoin", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1G 3Z4", "country": "Canada", "latitude": 45.4077799, "longitude": -71.8494655, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "6f8cb08e-882b-4f36-5f95-08dd828905dc", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Belvédère, devant le jardin communautaire Espace de la diversité", "street": "500, rue Thibault", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6G7", "country": "Canada", "latitude": 45.3743284, "longitude": -71.8933644, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "2037eb54-3ffa-4ed2-b3a3-08ddd9a5b408", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Kiosque Laurette-Giguère-de-Montigny (parc Jacques-Cartier)", "street": "220, rue Marchant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1J 3V2", "country": "Canada", "latitude": 45.3975395, "longitude": -71.9169177, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0dea5555-a7ef-46c1-4733-08dde0baec3f", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Halte Turgeon-Gaudreau", "street": "4815, boulevard de l'Université", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 2M4", "country": "Canada", "latitude": 45.354616, "longitude": -71.9754349, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "56b39a47-288e-4527-0c78-08ddff86527d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Mont-Bellevue, stationnement Dunant", "street": "1875, rue Dunant", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 6L5", "country": "Canada", "latitude": 45.377376, "longitude": -71.9090846, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "0ea059d9-79a5-4f46-fdc1-08de74726865", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place de la Gare", "street": "806, place de la Gare", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 0G8", "country": "Canada", "latitude": 45.3973162, "longitude": -71.8972144, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "3291a259-688d-4b2e-af8e-08de794851fb", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Dolor-Biron", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4034079, "longitude": -71.8805717, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "047d3941-25ce-4faa-489e-08de8e601892", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Léonard-Cohen", "street": "Rue du Chardonnay", "city": "Sherbrooke", "province": "Québec", "postalCode": "J1L 0J3", "country": "Canada", "latitude": 45.4177174, "longitude": -71.9490767, "streetViewOrientation": 0}, {"id": "90dd2962-cd0e-424a-4b7d-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "", "street": "", "city": "", "province": "", "postalCode": "", "country": "", "latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, {"id": "8f20ba56-8528-4a7e-4b7e-08dec19c2a5d", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc du Petit-Canada", "street": "rue Alexandre", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 4S9", "country": "Canada", "latitude": 45.3954233, "longitude": -71.8910082, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "27b162b1-9060-4f92-ce34-08ded06aecba", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Parc Jacob-Nicol", "street": "", "city": "", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "", "postalCode": "", "country": "", "latitude": 45.4040761, "longitude": -71.918024, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}, {"id": "95f3ecbe-0aa9-4d8c-0f1f-08def49980b6", "clientId": "64f6eb7d-b3bf-4f14-eb66-08d740272496", "email": "", "phone": {"number": "", "countryCode": ""}, "faxPhone": {"number": "", "countryCode": ""}, "noFeePhone": {"number": "", "countryCode": ""}, "name": "Place Kassiwi", "street": "68, rue Wellington Sud", "city": "Sherbrooke", "mrcId": "48b256d8-3eac-e611-80f5-00155d09650f", "regionId": "908bde69-40ac-e611-80f5-00155d09650f", "province": "Québec", "postalCode": "J1H 5E9", "country": "Canada", "latitude": 45.4006351, "longitude": -71.8889649, "streetViewOrientation": 0, "regionName": "Estrie", "mrcName": "Sherbrooke"}], "customFields": []}, "clients": [], "address": {"latitude": 0.0, "longitude": 0.0, "streetViewOrientation": 0}, "clientAddressId": "ee847adc-9043-4ce3-5df7-08d79f8c29b0", "phone": {}, "noFeePhone": {}, "addressURL": {"fr": "https://www.relaismontbellevue.com/", "en": ""}, "categories": {"language": "fr", "block": [{"id": "c50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Secteur", "en": "Secteur"}, "children": [{"id": "c57e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "du Mont-Bellevue", "en": "du Mont-Bellevue"}, "parentId": "c50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 75}], "isBlock": true, "order": 75}, {"id": "b50e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Clientèle", "en": "Clientele"}, "children": [{"id": "b53e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adolescent", "en": "Teens"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}, {"id": "b54e6814-478d-e911-9b6f-00155da78728", "name": {"fr": "Adulte", "en": "Adults"}, "parentId": "b50e6814-478d-e911-9b6f-00155da78728", "isBlock": false, "order": 76}], "isBlock": true, "order": 76}], "base": [{"id": "333d1577-01e7-e611-80f8-00155d09650f", "name": {"fr": "Sport", "en": "Sports"}, "isBlock": false, "order": 0}], "rawIds": ["b50e6814-478d-e911-9b6f-00155da78728", "b53e6814-478d-e911-9b6f-00155da78728", "b54e6814-478d-e911-9b6f-00155da78728", "c57e6814-478d-e911-9b6f-00155da78728", "c50e6814-478d-e911-9b6f-00155da78728", "333d1577-01e7-e611-80f8-00155d09650f"]}, "prix": 0.0, "nePasAfficherLePrix": true, "payant": false, "registrationStartDate": "2021-06-22T00:00:00Z", "customFields": [{"customFieldId": "9606ac36-27da-e911-8192-00155d4d070d", "textValue": "Relais10.4@gmail.com", "dateModified": "2026-07-10T17:14:31.1Z"}, {"customFieldId": "ba8bfe3d-27da-e911-8192-00155d4d070d", "textValue": "https://www.facebook.com/relaismontbellevue", "dateModified": "2026-07-10T17:14:31.1Z"}], "otherPictures": [{"picture": "287-eve-26-relais-mb.jpg", "order": 0}], "isHighlighted": false, "isOnline": true}]}}};</script></html>
\ No newline at end of file
added tests/fixtures/sitq_sample.json +1373 −0
@@ -0,0 +1,1373 @@
1 +{
2 + "value": [
3 + {
4 + "SyndicObjectID": "EVENMTOV500NIO",
5 + "Published": "2021-12-06T09:57:17.687",
6 + "Updated": "2026-07-02T16:13:56",
7 + "SyndicObjectName": "JACKALOPE",
8 + "SyndicStructureId": "9003b522-3a84-46a5-89b3-8494d7b33d4f",
9 + "ObjectTypeFix": "242A3256-953F-4B3D-8871-24A12638284C",
10 + "ObjectTypeName": "Événement",
11 + "SyndicObjectOrder": 1,
12 + "Structure": {
13 + "SyndicStructureId": "9003b522-3a84-46a5-89b3-8494d7b33d4f",
14 + "Name": "Tourisme Montréal",
15 + "Email": "Edition@mtl.org",
16 + "Url": "https://www.mtl.org/",
17 + "Address1": "800, Boul. René Lévesque Ouest",
18 + "Postcode": "H3B 1X9",
19 + "City": "Montréal (Québec)",
20 + "Country": "Canada",
21 + "Phone": "514 844-5400"
22 + },
23 + "RegionsDesserviess": [
24 + {
25 + "ID": 377422485000000059,
26 + "Ordre": 1,
27 + "Regionstouristiques": [
28 + {
29 + "ID": 378222090600000015,
30 + "Ordre": 1,
31 + "ThesID": "84895ced-9b61-4db8-9c73-a1592dc43fb5",
32 + "ThesLibelle": "Montréal"
33 + }
34 + ]
35 + }
36 + ],
37 + "SiteInternets": [
38 + {
39 + "ID": 351215170901051020,
40 + "Ordre": 1,
41 + "Coordonnees": "http://jackalopefest.ca",
42 + "Moyendecommunication": {
43 + "ThesID": "ac91b86b-e734-45ff-bf8f-9c95f22d4219",
44 + "ThesLibelle": "Site internet"
45 + }
46 + }
47 + ],
48 + "Themes": [
49 + {
50 + "ID": 352115125400003033,
51 + "Ordre": 1,
52 + "Theme": [
53 + {
54 + "ID": 377422485000003059,
55 + "Ordre": 1,
56 + "ThesID": "836d5d8d-7e09-4115-89b8-7e2319ccaf34",
57 + "ThesLibelle": "Chanson"
58 + },
59 + {
60 + "ID": 377422485000002059,
61 + "Ordre": 2,
62 + "ThesID": "52daf9e3-e6c5-4e14-886f-2223fb3bb6a1",
63 + "ThesLibelle": "Sports et plein air"
64 + }
65 + ]
66 + }
67 + ],
68 + "Geolocalisations": [
69 + {
70 + "ID": 351215170901049020,
71 + "Ordre": 1,
72 + "Longitude": "-73.55206729999",
73 + "Latitude": "45.507702500000"
74 + }
75 + ],
76 + "PeriodeOuvertures": [
77 + {
78 + "ID": 435721520100000017,
79 + "Ordre": 1,
80 + "Datedebut": "2026-09-11T00:00:00",
81 + "Datefin": "2026-09-13T00:00:00",
82 + "Datesestimees": false
83 + }
84 + ],
85 + "ClientelesAdmisess": [
86 + {
87 + "ID": 352115125400010033,
88 + "Ordre": 1,
89 + "Clientelesadmises": [
90 + {
91 + "ID": 456419331500010013,
92 + "Ordre": 1,
93 + "ThesID": "d497b12b-4645-4ee5-96f2-8ac057a2f45f",
94 + "ThesLibelle": "Adultes"
95 + },
96 + {
97 + "ID": 456419331500009013,
98 + "Ordre": 2,
99 + "ThesID": "da2520d7-fbe9-4f4e-905f-3e8c7bf4e974",
100 + "ThesLibelle": "Adolescents"
101 + },
102 + {
103 + "ID": 456419331500008013,
104 + "Ordre": 3,
105 + "ThesID": "f967daa6-3fa4-43e6-96db-2f9f01261556",
106 + "ThesLibelle": "Enfants"
107 + }
108 + ]
109 + }
110 + ],
111 + "ActivitesSportivesTerrestress": [
112 + {
113 + "ID": 352115125400007033,
114 + "Ordre": 1,
115 + "Activitessportivesterrestres": [
116 + {
117 + "ID": 456419331500000013,
118 + "Ordre": 1,
119 + "ThesID": "f674dab1-d1e7-4ef7-9680-7a59e7213a54",
120 + "ThesLibelle": "Planche à roulettes"
121 + }
122 + ]
123 + }
124 + ],
125 + "GroupesServicess": [
126 + {
127 + "ID": 332512034300001599,
128 + "Ordre": 1,
129 + "Tarifdegroupe": false,
130 + "Horaireparticuliersurdemande": false,
131 + "Aucunereservationacceptee": false,
132 + "Reservationobligatoire": false
133 + }
134 + ],
135 + "Adresses": [
136 + {
137 + "ID": 351215170901098020,
138 + "Ordre": 1,
139 + "Pays": {
140 + "ThesID": "38c90f3c-cc1c-4459-9936-9df0fb4b5b9a",
141 + "ThesLibelle": "CANADA"
142 + },
143 + "Typedadresse": {
144 + "ThesID": "2abe9900-fddf-4b6b-9e80-25f350696cdd",
145 + "ThesLibelle": "Lieu de l'événement"
146 + },
147 + "Nomdupointdeservice": "Quai Jacques-Cartier",
148 + "Codegeographique": "2466023",
149 + "ProvinceEtat": "Québec",
150 + "Municipalite": "Montréal",
151 + "CodePostal": "H2L 5C1",
152 + "Numerovoie": "Rue de la Commune Est"
153 + }
154 + ],
155 + "ActivitesRecreativesCulturelless": [
156 + {
157 + "ID": 300809545401933000,
158 + "Ordre": 1,
159 + "Activitesrecreativesculturelles": [
160 + {
161 + "ID": 351215170901085020,
162 + "Ordre": 1,
163 + "ThesID": "cef2c6f0-bcb5-461d-a746-d050e832d1ea",
164 + "ThesLibelle": "Animation pour enfants"
165 + },
166 + {
167 + "ID": 351215170901084020,
168 + "Ordre": 2,
169 + "ThesID": "ebd384dd-5749-4d14-9801-f9f585b330d5",
170 + "ThesLibelle": "Atelier"
171 + },
172 + {
173 + "ID": 351215170901081020,
174 + "Ordre": 3,
175 + "ThesID": "0caf1ea3-2a85-4b0c-8850-87f40c9d8ce6",
176 + "ThesLibelle": "Concert, spectacle"
177 + },
178 + {
179 + "ID": 351215170901082020,
180 + "Ordre": 4,
181 + "ThesID": "f53d8f69-bac1-4cc0-bac7-0884d223606c",
182 + "ThesLibelle": "Concours / tournoi"
183 + },
184 + {
185 + "ID": 351215170901086020,
186 + "Ordre": 5,
187 + "ThesID": "d49b6cd2-f576-452d-922a-284f1e4cb9a4",
188 + "ThesLibelle": "Dégustation"
189 + },
190 + {
191 + "ID": 351215170901083020,
192 + "Ordre": 6,
193 + "ThesID": "ff5c0b07-7fe5-4582-9a6a-6cdd73f33693",
194 + "ThesLibelle": "Démonstration"
195 + }
196 + ]
197 + }
198 + ],
199 + "ServicesAnimauxDeCompagnies": [
200 + {
201 + "ID": 455017325800001047,
202 + "Ordre": 1,
203 + "Animauxdecompagnie": [
204 + {
205 + "ID": 456419331500007013,
206 + "Ordre": 1,
207 + "ThesID": "f891ae59-f3b0-4ce0-afdd-1f3cea5a3b6c",
208 + "ThesLibelle": "Chiens (en laisse) admis"
209 + }
210 + ]
211 + }
212 + ],
213 + "ZonesGeographiquess": [
214 + {
215 + "ZoneId": 426,
216 + "ZoneLibelle": "Canada",
217 + "ID": 1,
218 + "Ordre": 3287,
219 + "AdresseOrdre": 1
220 + },
221 + {
222 + "ZoneId": 469,
223 + "ZoneLibelle": "Montréal",
224 + "ID": 2,
225 + "Ordre": 3288,
226 + "AdresseOrdre": 1
227 + },
228 + {
229 + "ZoneId": 487,
230 + "ZoneLibelle": "Montréal",
231 + "ID": 3,
232 + "Ordre": 3289,
233 + "AdresseOrdre": 1
234 + },
235 + {
236 + "ZoneId": 667,
237 + "ZoneLibelle": "Montréal",
238 + "ID": 4,
239 + "Ordre": 3290,
240 + "AdresseOrdre": 1
241 + },
242 + {
243 + "ZoneId": 699,
244 + "ZoneLibelle": "Québec",
245 + "ID": 5,
246 + "Ordre": 3291,
247 + "AdresseOrdre": 1
248 + },
249 + {
250 + "ZoneId": 708,
251 + "ZoneLibelle": "MTMD : Direction de la région métropolitaine de Montréal",
252 + "ID": 6,
253 + "Ordre": 3292,
254 + "AdresseOrdre": 1
255 + }
256 + ],
257 + "ZonesGeographiquesTypes": [
258 + {
259 + "ID": 1,
260 + "ZoneId": 426,
261 + "ZoneTypeId": 6,
262 + "ZoneTypeLibelle": "Zone géographique"
263 + },
264 + {
265 + "ID": 2,
266 + "ZoneId": 469,
267 + "ZoneTypeId": 7,
268 + "ZoneTypeLibelle": "Région touristique"
269 + },
270 + {
271 + "ID": 3,
272 + "ZoneId": 487,
273 + "ZoneTypeId": 14,
274 + "ZoneTypeLibelle": "Région administrative"
275 + },
276 + {
277 + "ID": 4,
278 + "ZoneId": 667,
279 + "ZoneTypeId": 13,
280 + "ZoneTypeLibelle": "MRC"
281 + },
282 + {
283 + "ID": 5,
284 + "ZoneId": 699,
285 + "ZoneTypeId": 17,
286 + "ZoneTypeLibelle": "Province"
287 + },
288 + {
289 + "ID": 6,
290 + "ZoneId": 708,
291 + "ZoneTypeId": 18,
292 + "ZoneTypeLibelle": "Direction territoriale - MTMD"
293 + }
294 + ],
295 + "TransportEnCommuns": [
296 + {
297 + "ID": 377422485000001059,
298 + "Ordre": 1,
299 + "Modedetransport": {
300 + "ThesID": "c6a21f6e-a1e6-463f-ae8a-dadb96f4c11b",
301 + "ThesLibelle": "Métro (ligne orange)"
302 + }
303 + }
304 + ],
305 + "ServicesRestaurations": [
306 + {
307 + "ID": 352115125400009033,
308 + "Ordre": 1,
309 + "Servicesderestauration": [
310 + {
311 + "ID": 456419331500006013,
312 + "Ordre": 1,
313 + "ThesID": "c16c01a2-0b78-4ef8-adfc-74764d84dcd8",
314 + "ThesLibelle": "Camion de cuisine de rue"
315 + }
316 + ]
317 + }
318 + ],
319 + "InstallationsDisponibless": [
320 + {
321 + "ID": 352115125400008033,
322 + "Ordre": 1,
323 + "Installationsdisponibles": [
324 + {
325 + "ID": 456419331500001013,
326 + "Ordre": 1,
327 + "ThesID": "bc44c861-970f-4e58-b2b4-06792bf85f76",
328 + "ThesLibelle": "Aire de dégustation"
329 + },
330 + {
331 + "ID": 456419331500002013,
332 + "Ordre": 2,
333 + "ThesID": "3c705952-cd0c-41a4-9884-96db5d02321a",
334 + "ThesLibelle": "Aire de jeux extérieure pour enfants"
335 + }
336 + ]
337 + }
338 + ],
339 + "Identifications": [
340 + {
341 + "ID": 300809545401969000,
342 + "Ordre": 1,
343 + "Typedetablissementoudequipement": {
344 + "ThesID": "d308143b-72b5-4885-9225-aa53ee5cc07a",
345 + "ThesLibelle": "Manifestation sportive"
346 + },
347 + "Nomdefiche": "JACKALOPE"
348 + }
349 + ],
350 + "Accessibilites": [
351 + {
352 + "ID": 300809545401930000,
353 + "Ordre": 1,
354 + "ServicesadaptespourdeficienceauditiveselonKeroul": false,
355 + "Locationdefauteuilroulant": false,
356 + "Activitesequipementsadaptesselonlexploitant": false,
357 + "ServicesadaptespourdeficiencevisuelleselonKeroul": false
358 + }
359 + ]
360 + },
361 + {
362 + "SyndicObjectID": "EVENMTOV500NJH",
363 + "Published": "2021-12-06T09:58:00.857",
364 + "Updated": "2026-05-23T18:11:39.53",
365 + "SyndicObjectName": "Salon du VR de Québec",
366 + "SyndicStructureId": "350560da-9d0f-4cb4-a5b3-1b48ef21f642",
367 + "ObjectTypeFix": "242A3256-953F-4B3D-8871-24A12638284C",
368 + "ObjectTypeName": "Événement",
369 + "SyndicObjectOrder": 2,
370 + "Structure": {
371 + "SyndicStructureId": "350560da-9d0f-4cb4-a5b3-1b48ef21f642",
372 + "Name": "Ministère du Tourisme",
373 + "Email": "pilote-sitquebec@tourisme.gouv.qc.ca",
374 + "EmailName": "Ministère du Tourisme",
375 + "Url": "https://www.bonjourquebec.com/fr-ca",
376 + "Address1": "900, boulevard René-Lévesque Est",
377 + "Postcode": "G1R 2B5",
378 + "City": "Québec (Québec)"
379 + },
380 + "RegionsDesserviess": [
381 + {
382 + "ID": 378720503800001025,
383 + "Ordre": 1,
384 + "Regionstouristiques": [
385 + {
386 + "ID": 381519463400001023,
387 + "Ordre": 1,
388 + "ThesID": "f7dd5b01-e69f-416c-a3c7-8ea6e9a6d02e",
389 + "ThesLibelle": "Québec"
390 + }
391 + ]
392 + }
393 + ],
394 + "SiteInternets": [
395 + {
396 + "ID": 351215170705897020,
397 + "Ordre": 1,
398 + "Coordonnees": "http://www.acvrq.com/salon-du-vr/salon-de-quebec/",
399 + "Moyendecommunication": {
400 + "ThesID": "ac91b86b-e734-45ff-bf8f-9c95f22d4219",
401 + "ThesLibelle": "Site internet"
402 + }
403 + }
404 + ],
405 + "Geolocalisations": [
406 + {
407 + "ID": 381519453400001024,
408 + "Ordre": 1,
409 + "Longitude": "-71.247364",
410 + "Latitude": "46.8266342"
411 + }
412 + ],
413 + "PeriodeOuvertures": [
414 + {
415 + "ID": 351215170705929020,
416 + "Ordre": 1,
417 + "Datedebut": "2026-04-09T00:00:00",
418 + "Datefin": "2026-04-12T00:00:00",
419 + "Datesestimees": false
420 + }
421 + ],
422 + "TelephonePrincipals": [
423 + {
424 + "ID": 351215170705898020,
425 + "Ordre": 1,
426 + "Coordonnees": "438 393-4747",
427 + "Moyendecommunication": {
428 + "ThesID": "815281e5-9e93-4e4c-8a1d-a1661d639bd7",
429 + "ThesLibelle": "Téléphone principal"
430 + }
431 + }
432 + ],
433 + "ClientelesAdmisess": [
434 + {
435 + "ID": 352814195000009063,
436 + "Ordre": 1,
437 + "Clientelesadmises": [
438 + {
439 + "ID": 360618064000012052,
440 + "Ordre": 1,
441 + "ThesID": "d497b12b-4645-4ee5-96f2-8ac057a2f45f",
442 + "ThesLibelle": "Adultes"
443 + },
444 + {
445 + "ID": 360618064000013052,
446 + "Ordre": 2,
447 + "ThesID": "da2520d7-fbe9-4f4e-905f-3e8c7bf4e974",
448 + "ThesLibelle": "Adolescents"
449 + }
450 + ]
451 + }
452 + ],
453 + "GroupesServicess": [
454 + {
455 + "ID": 332512034300001699,
456 + "Ordre": 1,
457 + "Tarifdegroupe": false,
458 + "Horaireparticuliersurdemande": false,
459 + "Aucunereservationacceptee": false,
460 + "Reservationobligatoire": false
461 + }
462 + ],
463 + "Adresses": [
464 + {
465 + "ID": 381519453400000024,
466 + "Ordre": 1,
467 + "Pays": {
468 + "ThesID": "38c90f3c-cc1c-4459-9936-9df0fb4b5b9a",
469 + "ThesLibelle": "CANADA"
470 + },
471 + "Typedadresse": {
472 + "ThesID": "2abe9900-fddf-4b6b-9e80-25f350696cdd",
473 + "ThesLibelle": "Lieu de l'événement"
474 + },
475 + "Nomdupointdeservice": "Centre de foires Expocité",
476 + "Codegeographique": "2423027",
477 + "ProvinceEtat": "Quebec",
478 + "Municipalite": "Québec",
479 + "CodePostal": "G1L 5A7",
480 + "Numerovoie": "250 boul. Wilfrid-Hamel "
481 + }
482 + ],
483 + "ActivitesRecreativesCulturelless": [
484 + {
485 + "ID": 300809545501351000,
486 + "Ordre": 1,
487 + "Activitesrecreativesculturelles": [
488 + {
489 + "ID": 360618064000004052,
490 + "Ordre": 1,
491 + "ThesID": "ebd384dd-5749-4d14-9801-f9f585b330d5",
492 + "ThesLibelle": "Atelier"
493 + },
494 + {
495 + "ID": 360618064000005052,
496 + "Ordre": 2,
497 + "ThesID": "f53d8f69-bac1-4cc0-bac7-0884d223606c",
498 + "ThesLibelle": "Concours / tournoi"
499 + },
500 + {
501 + "ID": 360618064000002052,
502 + "Ordre": 3,
503 + "ThesID": "28883b21-f4b4-44e5-ac0b-972f608b50c5",
504 + "ThesLibelle": "Conférence"
505 + },
506 + {
507 + "ID": 360618064000001052,
508 + "Ordre": 4,
509 + "ThesID": "ff5c0b07-7fe5-4582-9a6a-6cdd73f33693",
510 + "ThesLibelle": "Démonstration"
511 + },
512 + {
513 + "ID": 360618064000003052,
514 + "Ordre": 5,
515 + "ThesID": "7ccc8bc3-31da-429f-a524-da22b94c8393",
516 + "ThesLibelle": "Exposition"
517 + }
518 + ]
519 + }
520 + ],
521 + "Courriels": [
522 + {
523 + "ID": 351215170705896020,
524 + "Ordre": 1,
525 + "Moyendecommunication": {
526 + "ThesID": "a0ecac9e-1f82-46c1-a573-911b259b0b2f",
527 + "ThesLibelle": "Courriel"
528 + },
529 + "Coordonnees": "administration@acvrq.com"
530 + }
531 + ],
532 + "ZonesGeographiquess": [
533 + {
534 + "ZoneId": 426,
535 + "ZoneLibelle": "Canada",
536 + "ID": 7,
537 + "Ordre": 5820,
538 + "AdresseOrdre": 1
539 + },
540 + {
541 + "ZoneId": 472,
542 + "ZoneLibelle": "Québec",
543 + "ID": 8,
544 + "Ordre": 5821,
545 + "AdresseOrdre": 1
546 + },
547 + {
548 + "ZoneId": 476,
549 + "ZoneLibelle": "Capitale-Nationale",
550 + "ID": 9,
551 + "Ordre": 5822,
552 + "AdresseOrdre": 1
553 + },
554 + {
555 + "ZoneId": 554,
556 + "ZoneLibelle": "Québec",
557 + "ID": 10,
558 + "Ordre": 5823,
559 + "AdresseOrdre": 1
560 + },
561 + {
562 + "ZoneId": 674,
563 + "ZoneLibelle": "Québec",
564 + "ID": 11,
565 + "Ordre": 5824,
566 + "AdresseOrdre": 1
567 + },
568 + {
569 + "ZoneId": 699,
570 + "ZoneLibelle": "Québec",
571 + "ID": 12,
572 + "Ordre": 5825,
573 + "AdresseOrdre": 1
574 + },
575 + {
576 + "ZoneId": 702,
577 + "ZoneLibelle": "MTMD : Direction de la Capitale-Nationale",
578 + "ID": 13,
579 + "Ordre": 5826,
580 + "AdresseOrdre": 1
581 + }
582 + ],
583 + "ZonesGeographiquesTypes": [
584 + {
585 + "ID": 7,
586 + "ZoneId": 426,
587 + "ZoneTypeId": 6,
588 + "ZoneTypeLibelle": "Zone géographique"
589 + },
590 + {
591 + "ID": 8,
592 + "ZoneId": 472,
593 + "ZoneTypeId": 7,
594 + "ZoneTypeLibelle": "Région touristique"
595 + },
596 + {
597 + "ID": 9,
598 + "ZoneId": 476,
599 + "ZoneTypeId": 14,
600 + "ZoneTypeLibelle": "Région administrative"
601 + },
602 + {
603 + "ID": 10,
604 + "ZoneId": 554,
605 + "ZoneTypeId": 12,
606 + "ZoneTypeLibelle": "Secteur touristique"
607 + },
608 + {
609 + "ID": 11,
610 + "ZoneId": 674,
611 + "ZoneTypeId": 13,
612 + "ZoneTypeLibelle": "MRC"
613 + },
614 + {
615 + "ID": 12,
616 + "ZoneId": 699,
617 + "ZoneTypeId": 17,
618 + "ZoneTypeLibelle": "Province"
619 + },
620 + {
621 + "ID": 13,
622 + "ZoneId": 702,
623 + "ZoneTypeId": 18,
624 + "ZoneTypeLibelle": "Direction territoriale - MTMD"
625 + }
626 + ],
627 + "Stationnements": [
628 + {
629 + "ID": 352814195000001063,
630 + "Ordre": 1,
631 + "Stationnement": [
632 + {
633 + "ID": 360618064000000052,
634 + "Ordre": 1,
635 + "ThesID": "b6eee923-1fde-4d56-b63f-ced0220b8384",
636 + "ThesLibelle": "Stationnement sur place payant"
637 + }
638 + ]
639 + }
640 + ],
641 + "ServicesRestaurations": [
642 + {
643 + "ID": 352814195000008063,
644 + "Ordre": 1,
645 + "Servicesderestauration": [
646 + {
647 + "ID": 360618064000011052,
648 + "Ordre": 1,
649 + "ThesID": "1e44f13c-864a-4f7f-b6b5-b9c452a45534",
650 + "ThesLibelle": "Cafétéria"
651 + },
652 + {
653 + "ID": 360618064000010052,
654 + "Ordre": 2,
655 + "ThesID": "cba3a835-737f-44dd-8c0e-4777d8896c90",
656 + "ThesLibelle": "Casse-croûte"
657 + }
658 + ]
659 + }
660 + ],
661 + "InstallationsDisponibless": [
662 + {
663 + "ID": 352814195000007063,
664 + "Ordre": 1,
665 + "Installationsdisponibles": [
666 + {
667 + "ID": 360618064000006052,
668 + "Ordre": 1,
669 + "ThesID": "886800e8-7c0e-4055-97ee-78894d08026d",
670 + "ThesLibelle": "Toilettes"
671 + }
672 + ]
673 + }
674 + ],
675 + "Identifications": [
676 + {
677 + "ID": 300809545501384000,
678 + "Ordre": 1,
679 + "Typedetablissementoudequipement": {
680 + "ThesID": "4b1eca0c-a385-48e5-ae95-a6eb60188c5e",
681 + "ThesLibelle": "Salon / foire"
682 + },
683 + "Nomdefiche": "Salon du VR de Québec"
684 + }
685 + ],
686 + "Accessibilites": [
687 + {
688 + "ID": 300809545501348000,
689 + "Ordre": 1,
690 + "ServicesadaptespourdeficienceauditiveselonKeroul": false,
691 + "Locationdefauteuilroulant": false,
692 + "Activitesequipementsadaptesselonlexploitant": true,
693 + "ServicesadaptespourdeficiencevisuelleselonKeroul": false
694 + }
695 + ]
696 + },
697 + {
698 + "SyndicObjectID": "EVENMTOV500NW4",
699 + "Published": "2022-01-13T11:36:04.77",
700 + "Updated": "2026-07-29T14:01:41.843",
701 + "SyndicObjectName": "MONTRÉAL COMPLÈTEMENT CiRQUE",
702 + "SyndicStructureId": "9003b522-3a84-46a5-89b3-8494d7b33d4f",
703 + "ObjectTypeFix": "242A3256-953F-4B3D-8871-24A12638284C",
704 + "ObjectTypeName": "Événement",
705 + "SyndicObjectOrder": 3,
706 + "Structure": {
707 + "SyndicStructureId": "9003b522-3a84-46a5-89b3-8494d7b33d4f",
708 + "Name": "Tourisme Montréal",
709 + "Email": "Edition@mtl.org",
710 + "Url": "https://www.mtl.org/",
711 + "Address1": "800, Boul. René Lévesque Ouest",
712 + "Postcode": "H3B 1X9",
713 + "City": "Montréal (Québec)",
714 + "Country": "Canada",
715 + "Phone": "514 844-5400"
716 + },
717 + "RegionsDesserviess": [
718 + {
719 + "ID": 373419571300000038,
720 + "Ordre": 1,
721 + "Regionstouristiques": [
722 + {
723 + "ID": 378922281600000030,
724 + "Ordre": 1,
725 + "ThesID": "84895ced-9b61-4db8-9c73-a1592dc43fb5",
726 + "ThesLibelle": "Montréal"
727 + }
728 + ]
729 + }
730 + ],
731 + "SiteInternets": [
732 + {
733 + "ID": 351215170803597020,
734 + "Ordre": 1,
735 + "Coordonnees": "https://montrealcompletementcirque.com/fr/",
736 + "Moyendecommunication": {
737 + "ThesID": "ac91b86b-e734-45ff-bf8f-9c95f22d4219",
738 + "ThesLibelle": "Site internet"
739 + }
740 + }
741 + ],
742 + "Themes": [
743 + {
744 + "ID": 352014583500002027,
745 + "Ordre": 1,
746 + "Theme": [
747 + {
748 + "ID": 373721294300022499,
749 + "Ordre": 1,
750 + "ThesID": "f1bc0834-85c8-4a3d-93f6-fe0bd53b86f1",
751 + "ThesLibelle": "Cirque"
752 + }
753 + ]
754 + }
755 + ],
756 + "PeriodeOuvertures": [
757 + {
758 + "ID": 395821575400000022,
759 + "Ordre": 1,
760 + "Datedebut": "2026-07-02T00:00:00",
761 + "Datefin": "2026-07-12T00:00:00",
762 + "Datesestimees": false
763 + },
764 + {
765 + "ID": 465518165600000022,
766 + "Ordre": 2,
767 + "Datedebut": "2027-07-01T00:00:00",
768 + "Datefin": "2027-07-11T00:00:00",
769 + "Datesestimees": true
770 + }
771 + ],
772 + "TelephonePrincipals": [
773 + {
774 + "ID": 351215170803598020,
775 + "Ordre": 1,
776 + "Coordonnees": "514 376-8648",
777 + "Moyendecommunication": {
778 + "ThesID": "815281e5-9e93-4e4c-8a1d-a1661d639bd7",
779 + "ThesLibelle": "Téléphone principal"
780 + }
781 + }
782 + ],
783 + "ClientelesAdmisess": [
784 + {
785 + "ID": 352014583500008027,
786 + "Ordre": 1,
787 + "Clientelesadmises": [
788 + {
789 + "ID": 354714172700009033,
790 + "Ordre": 1,
791 + "ThesID": "d497b12b-4645-4ee5-96f2-8ac057a2f45f",
792 + "ThesLibelle": "Adultes"
793 + },
794 + {
795 + "ID": 354714172700010033,
796 + "Ordre": 2,
797 + "ThesID": "da2520d7-fbe9-4f4e-905f-3e8c7bf4e974",
798 + "ThesLibelle": "Adolescents"
799 + },
800 + {
801 + "ID": 354714172700012033,
802 + "Ordre": 3,
803 + "ThesID": "f967daa6-3fa4-43e6-96db-2f9f01261556",
804 + "ThesLibelle": "Enfants"
805 + },
806 + {
807 + "ID": 354714172700011033,
808 + "Ordre": 4,
809 + "ThesID": "1ab14347-d5b3-4fda-af19-ece26eb018be",
810 + "ThesLibelle": "Groupes"
811 + }
812 + ]
813 + }
814 + ],
815 + "GroupesServicess": [
816 + {
817 + "ID": 332512034300001799,
818 + "Ordre": 1,
819 + "Tarifdegroupe": true,
820 + "Horaireparticuliersurdemande": false,
821 + "Aucunereservationacceptee": false,
822 + "Reservationobligatoire": true
823 + }
824 + ],
825 + "Adresses": [
826 + {
827 + "ID": 351215170803634020,
828 + "Ordre": 1,
829 + "Pays": {
830 + "ThesID": "38c90f3c-cc1c-4459-9936-9df0fb4b5b9a",
831 + "ThesLibelle": "CANADA"
832 + },
833 + "Typedadresse": {
834 + "ThesID": "2abe9900-fddf-4b6b-9e80-25f350696cdd",
835 + "ThesLibelle": "Lieu de l'événement"
836 + },
837 + "Nomdupointdeservice": "À travers la ville",
838 + "Codegeographique": "2466023",
839 + "ProvinceEtat": "Quebec",
840 + "Municipalite": "Montréal"
841 + }
842 + ],
843 + "ActivitesRecreativesCulturelless": [
844 + {
845 + "ID": 304610592502390000,
846 + "Ordre": 1,
847 + "Activitesrecreativesculturelles": [
848 + {
849 + "ID": 351215170803618020,
850 + "Ordre": 1,
851 + "ThesID": "cef2c6f0-bcb5-461d-a746-d050e832d1ea",
852 + "ThesLibelle": "Animation pour enfants"
853 + },
854 + {
855 + "ID": 351215170803622020,
856 + "Ordre": 2,
857 + "ThesID": "ebd384dd-5749-4d14-9801-f9f585b330d5",
858 + "ThesLibelle": "Atelier"
859 + },
860 + {
861 + "ID": 351215170803621020,
862 + "Ordre": 3,
863 + "ThesID": "0caf1ea3-2a85-4b0c-8850-87f40c9d8ce6",
864 + "ThesLibelle": "Concert, spectacle"
865 + },
866 + {
867 + "ID": 351215170803619020,
868 + "Ordre": 4,
869 + "ThesID": "ff5c0b07-7fe5-4582-9a6a-6cdd73f33693",
870 + "ThesLibelle": "Démonstration"
871 + },
872 + {
873 + "ID": 351215170803620020,
874 + "Ordre": 5,
875 + "ThesID": "7ccc8bc3-31da-429f-a524-da22b94c8393",
876 + "ThesLibelle": "Exposition"
877 + }
878 + ]
879 + }
880 + ],
881 + "ServicesAutomobiless": [
882 + {
883 + "ID": 352014583500000027,
884 + "Ordre": 1,
885 + "Servicesautomobiles": [
886 + {
887 + "ID": 354714172700001033,
888 + "Ordre": 1,
889 + "ThesID": "5b72538a-f528-4f9e-8122-0dfa41a398a1",
890 + "ThesLibelle": "Borne de recharge électrique"
891 + },
892 + {
893 + "ID": 354714172700000033,
894 + "Ordre": 2,
895 + "ThesID": "7306b8ff-7f05-49bb-8bfc-baf614b057cc",
896 + "ThesLibelle": "Taxi disponible"
897 + }
898 + ]
899 + }
900 + ],
901 + "Courriels": [
902 + {
903 + "ID": 351215170803596020,
904 + "Ordre": 1,
905 + "Moyendecommunication": {
906 + "ThesID": "a0ecac9e-1f82-46c1-a573-911b259b0b2f",
907 + "ThesLibelle": "Courriel"
908 + },
909 + "Coordonnees": "montrealcompletementcirque@tohu.ca"
910 + }
911 + ],
912 + "ZonesGeographiquess": [
913 + {
914 + "ZoneId": 426,
915 + "ZoneLibelle": "Canada",
916 + "ID": 14,
917 + "Ordre": 2565,
918 + "AdresseOrdre": 1
919 + },
920 + {
921 + "ZoneId": 469,
922 + "ZoneLibelle": "Montréal",
923 + "ID": 15,
924 + "Ordre": 2566,
925 + "AdresseOrdre": 1
926 + },
927 + {
928 + "ZoneId": 487,
929 + "ZoneLibelle": "Montréal",
930 + "ID": 16,
931 + "Ordre": 2567,
932 + "AdresseOrdre": 1
933 + },
934 + {
935 + "ZoneId": 667,
936 + "ZoneLibelle": "Montréal",
937 + "ID": 17,
938 + "Ordre": 2568,
939 + "AdresseOrdre": 1
940 + },
941 + {
942 + "ZoneId": 699,
943 + "ZoneLibelle": "Québec",
944 + "ID": 18,
945 + "Ordre": 2569,
946 + "AdresseOrdre": 1
947 + },
948 + {
949 + "ZoneId": 708,
950 + "ZoneLibelle": "MTMD : Direction de la région métropolitaine de Montréal",
951 + "ID": 19,
952 + "Ordre": 2570,
953 + "AdresseOrdre": 1
954 + }
955 + ],
956 + "ZonesGeographiquesTypes": [
957 + {
958 + "ID": 14,
959 + "ZoneId": 426,
960 + "ZoneTypeId": 6,
961 + "ZoneTypeLibelle": "Zone géographique"
962 + },
963 + {
964 + "ID": 15,
965 + "ZoneId": 469,
966 + "ZoneTypeId": 7,
967 + "ZoneTypeLibelle": "Région touristique"
968 + },
969 + {
970 + "ID": 16,
971 + "ZoneId": 487,
972 + "ZoneTypeId": 14,
973 + "ZoneTypeLibelle": "Région administrative"
974 + },
975 + {
976 + "ID": 17,
977 + "ZoneId": 667,
978 + "ZoneTypeId": 13,
979 + "ZoneTypeLibelle": "MRC"
980 + },
981 + {
982 + "ID": 18,
983 + "ZoneId": 699,
984 + "ZoneTypeId": 17,
985 + "ZoneTypeLibelle": "Province"
986 + },
987 + {
988 + "ID": 19,
989 + "ZoneId": 708,
990 + "ZoneTypeId": 18,
991 + "ZoneTypeLibelle": "Direction territoriale - MTMD"
992 + }
993 + ],
994 + "Stationnements": [
995 + {
996 + "ID": 352014583500001027,
997 + "Ordre": 1,
998 + "Stationnement": [
999 + {
1000 + "ID": 354714172700002033,
1001 + "Ordre": 1,
1002 + "ThesID": "b6eee923-1fde-4d56-b63f-ced0220b8384",
1003 + "ThesLibelle": "Stationnement sur place payant"
1004 + }
1005 + ]
1006 + }
1007 + ],
1008 + "ServicesRestaurations": [
1009 + {
1010 + "ID": 304610592502401000,
1011 + "Ordre": 1,
1012 + "Servicesderestauration": [
1013 + {
1014 + "ID": 354714172700005033,
1015 + "Ordre": 1,
1016 + "ThesID": "95e4ab33-337d-46c5-a9aa-a5e15fa30369",
1017 + "ThesLibelle": "Aire de pique-nique"
1018 + },
1019 + {
1020 + "ID": 354714172700006033,
1021 + "Ordre": 2,
1022 + "ThesID": "685c3f92-055a-42bd-b236-9ddc1f449bbb",
1023 + "ThesLibelle": "Salle à manger"
1024 + }
1025 + ]
1026 + }
1027 + ],
1028 + "InstallationsDisponibless": [
1029 + {
1030 + "ID": 352014583500007027,
1031 + "Ordre": 1,
1032 + "Installationsdisponibles": [
1033 + {
1034 + "ID": 354714172700004033,
1035 + "Ordre": 1,
1036 + "ThesID": "3c705952-cd0c-41a4-9884-96db5d02321a",
1037 + "ThesLibelle": "Aire de jeux extérieure pour enfants"
1038 + },
1039 + {
1040 + "ID": 354714172700003033,
1041 + "Ordre": 2,
1042 + "ThesID": "886800e8-7c0e-4055-97ee-78894d08026d",
1043 + "ThesLibelle": "Toilettes"
1044 + }
1045 + ]
1046 + }
1047 + ],
1048 + "Identifications": [
1049 + {
1050 + "ID": 304610592502426000,
1051 + "Ordre": 1,
1052 + "Typedetablissementoudequipement": {
1053 + "ThesID": "437b272b-c46c-4fa0-a7b4-3210723c25d0",
1054 + "ThesLibelle": "Festival"
1055 + },
1056 + "Nomdefiche": "MONTRÉAL COMPLÈTEMENT CiRQUE"
1057 + }
1058 + ],
1059 + "Accessibilites": [
1060 + {
1061 + "ID": 304610592502385000,
1062 + "Ordre": 1,
1063 + "ServicesadaptespourdeficienceauditiveselonKeroul": false,
1064 + "Locationdefauteuilroulant": false,
1065 + "Activitesequipementsadaptesselonlexploitant": true,
1066 + "ServicesadaptespourdeficiencevisuelleselonKeroul": false
1067 + }
1068 + ]
1069 + },
1070 + {
1071 + "SyndicObjectID": "EVENMTOV500NW5",
1072 + "Published": "2022-01-13T11:36:06.033",
1073 + "Updated": "2026-06-15T15:24:42",
1074 + "SyndicObjectName": "Festival de la chanson de Saint-Ambroise",
1075 + "SyndicStructureId": "350560da-9d0f-4cb4-a5b3-1b48ef21f642",
1076 + "ObjectTypeFix": "242A3256-953F-4B3D-8871-24A12638284C",
1077 + "ObjectTypeName": "Événement",
1078 + "SyndicObjectOrder": 4,
1079 + "Structure": {
1080 + "SyndicStructureId": "350560da-9d0f-4cb4-a5b3-1b48ef21f642",
1081 + "Name": "Ministère du Tourisme",
1082 + "Email": "pilote-sitquebec@tourisme.gouv.qc.ca",
1083 + "EmailName": "Ministère du Tourisme",
1084 + "Url": "https://www.bonjourquebec.com/fr-ca",
1085 + "Address1": "900, boulevard René-Lévesque Est",
1086 + "Postcode": "G1R 2B5",
1087 + "City": "Québec (Québec)"
1088 + },
1089 + "RegionsDesserviess": [
1090 + {
1091 + "ID": 382820090400001050,
1092 + "Ordre": 1,
1093 + "Regionstouristiques": [
1094 + {
1095 + "ID": 390518350100000029,
1096 + "Ordre": 1,
1097 + "ThesID": "d6db3f01-7fdd-4140-8f83-7b5405f4c1dd",
1098 + "ThesLibelle": "Saguenay--Lac-Saint-Jean"
1099 + }
1100 + ]
1101 + }
1102 + ],
1103 + "SiteInternets": [
1104 + {
1105 + "ID": 351215170803545020,
1106 + "Ordre": 1,
1107 + "Coordonnees": "http://www.chansonsaintambroise.com",
1108 + "Moyendecommunication": {
1109 + "ThesID": "ac91b86b-e734-45ff-bf8f-9c95f22d4219",
1110 + "ThesLibelle": "Site internet"
1111 + }
1112 + }
1113 + ],
1114 + "Geolocalisations": [
1115 + {
1116 + "ID": 351215170803543020,
1117 + "Ordre": 1,
1118 + "Longitude": "-71.31458",
1119 + "Latitude": "48.556464"
1120 + }
1121 + ],
1122 + "PeriodeOuvertures": [
1123 + {
1124 + "ID": 351215170803572020,
1125 + "Ordre": 1,
1126 + "Datedebut": "2026-08-01T00:00:00",
1127 + "Datefin": "2026-08-08T00:00:00",
1128 + "Datesestimees": false
1129 + }
1130 + ],
1131 + "TelephonePrincipals": [
1132 + {
1133 + "ID": 351215170803546020,
1134 + "Ordre": 1,
1135 + "Coordonnees": "418 672-1144",
1136 + "Moyendecommunication": {
1137 + "ThesID": "815281e5-9e93-4e4c-8a1d-a1661d639bd7",
1138 + "ThesLibelle": "Téléphone principal"
1139 + }
1140 + }
1141 + ],
1142 + "ClientelesAdmisess": [
1143 + {
1144 + "ID": 352215254200009037,
1145 + "Ordre": 1,
1146 + "Clientelesadmises": [
1147 + {
1148 + "ID": 390518350100005029,
1149 + "Ordre": 1,
1150 + "ThesID": "d497b12b-4645-4ee5-96f2-8ac057a2f45f",
1151 + "ThesLibelle": "Adultes"
1152 + },
1153 + {
1154 + "ID": 390518350100003029,
1155 + "Ordre": 2,
1156 + "ThesID": "da2520d7-fbe9-4f4e-905f-3e8c7bf4e974",
1157 + "ThesLibelle": "Adolescents"
1158 + },
1159 + {
1160 + "ID": 390518350100002029,
1161 + "Ordre": 3,
1162 + "ThesID": "f967daa6-3fa4-43e6-96db-2f9f01261556",
1163 + "ThesLibelle": "Enfants"
1164 + },
1165 + {
1166 + "ID": 390518350100004029,
1167 + "Ordre": 4,
1168 + "ThesID": "1ab14347-d5b3-4fda-af19-ece26eb018be",
1169 + "ThesLibelle": "Groupes"
1170 + }
1171 + ]
1172 + }
1173 + ],
1174 + "GroupesServicess": [
1175 + {
1176 + "ID": 332512034300001899,
1177 + "Ordre": 1,
1178 + "Tarifdegroupe": false,
1179 + "Horaireparticuliersurdemande": false,
1180 + "Aucunereservationacceptee": false,
1181 + "Reservationobligatoire": false
1182 + }
1183 + ],
1184 + "Adresses": [
1185 + {
1186 + "ID": 351215170803578020,
1187 + "Ordre": 1,
1188 + "Pays": {
1189 + "ThesID": "38c90f3c-cc1c-4459-9936-9df0fb4b5b9a",
1190 + "ThesLibelle": "CANADA"
1191 + },
1192 + "Typedadresse": {
1193 + "ThesID": "2abe9900-fddf-4b6b-9e80-25f350696cdd",
1194 + "ThesLibelle": "Lieu de l'événement"
1195 + },
1196 + "Nomdupointdeservice": "Amphithéâtre Marcel-Claveau",
1197 + "Codegeographique": "2494255",
1198 + "ProvinceEtat": "Québec",
1199 + "Municipalite": "Saint-Ambroise",
1200 + "CodePostal": "G7P2J9",
1201 + "Numerovoie": "188, rue Gaudreault"
1202 + }
1203 + ],
1204 + "ActivitesRecreativesCulturelless": [
1205 + {
1206 + "ID": 304610592502441000,
1207 + "Ordre": 1,
1208 + "Activitesrecreativesculturelles": [
1209 + {
1210 + "ID": 351215170803565020,
1211 + "Ordre": 1,
1212 + "ThesID": "ebd384dd-5749-4d14-9801-f9f585b330d5",
1213 + "ThesLibelle": "Atelier"
1214 + },
1215 + {
1216 + "ID": 351215170803564020,
1217 + "Ordre": 2,
1218 + "ThesID": "0caf1ea3-2a85-4b0c-8850-87f40c9d8ce6",
1219 + "ThesLibelle": "Concert, spectacle"
1220 + },
1221 + {
1222 + "ID": 351215170803566020,
1223 + "Ordre": 3,
1224 + "ThesID": "f53d8f69-bac1-4cc0-bac7-0884d223606c",
1225 + "ThesLibelle": "Concours / tournoi"
1226 + }
1227 + ]
1228 + }
1229 + ],
1230 + "Courriels": [
1231 + {
1232 + "ID": 351215170803544020,
1233 + "Ordre": 1,
1234 + "Moyendecommunication": {
1235 + "ThesID": "a0ecac9e-1f82-46c1-a573-911b259b0b2f",
1236 + "ThesLibelle": "Courriel"
1237 + },
1238 + "Coordonnees": "festchansonsaintambroise@gmail.com"
1239 + }
1240 + ],
1241 + "ZonesGeographiquess": [
1242 + {
1243 + "ZoneId": 426,
1244 + "ZoneLibelle": "Canada",
1245 + "ID": 20,
1246 + "Ordre": 2558,
1247 + "AdresseOrdre": 1
1248 + },
1249 + {
1250 + "ZoneId": 473,
1251 + "ZoneLibelle": "Saguenay--Lac-Saint-Jean",
1252 + "ID": 21,
1253 + "Ordre": 2559,
1254 + "AdresseOrdre": 1
1255 + },
1256 + {
1257 + "ZoneId": 490,
1258 + "ZoneLibelle": "Saguenay--Lac-Saint-Jean",
1259 + "ID": 22,
1260 + "Ordre": 2560,
1261 + "AdresseOrdre": 1
1262 + },
1263 + {
1264 + "ZoneId": 569,
1265 + "ZoneLibelle": "Saguenay",
1266 + "ID": 23,
1267 + "Ordre": 2561,
1268 + "AdresseOrdre": 1
1269 + },
1270 + {
1271 + "ZoneId": 631,
1272 + "ZoneLibelle": "Le Fjord-du-Saguenay",
1273 + "ID": 24,
1274 + "Ordre": 2562,
1275 + "AdresseOrdre": 1
1276 + },
1277 + {
1278 + "ZoneId": 699,
1279 + "ZoneLibelle": "Québec",
1280 + "ID": 25,
1281 + "Ordre": 2563,
1282 + "AdresseOrdre": 1
1283 + },
1284 + {
1285 + "ZoneId": 717,
1286 + "ZoneLibelle": "MTMD : Direction du Saguenay--Lac-Saint-Jean",
1287 + "ID": 26,
1288 + "Ordre": 2564,
1289 + "AdresseOrdre": 1
1290 + }
1291 + ],
1292 + "ZonesGeographiquesTypes": [
1293 + {
1294 + "ID": 20,
1295 + "ZoneId": 426,
1296 + "ZoneTypeId": 6,
1297 + "ZoneTypeLibelle": "Zone géographique"
1298 + },
1299 + {
1300 + "ID": 21,
1301 + "ZoneId": 473,
1302 + "ZoneTypeId": 7,
1303 + "ZoneTypeLibelle": "Région touristique"
1304 + },
1305 + {
1306 + "ID": 22,
1307 + "ZoneId": 490,
1308 + "ZoneTypeId": 14,
1309 + "ZoneTypeLibelle": "Région administrative"
1310 + },
1311 + {
1312 + "ID": 23,
1313 + "ZoneId": 569,
1314 + "ZoneTypeId": 12,
1315 + "ZoneTypeLibelle": "Secteur touristique"
1316 + },
1317 + {
1318 + "ID": 24,
1319 + "ZoneId": 631,
1320 + "ZoneTypeId": 13,
1321 + "ZoneTypeLibelle": "MRC"
1322 + },
1323 + {
1324 + "ID": 25,
1325 + "ZoneId": 699,
1326 + "ZoneTypeId": 17,
1327 + "ZoneTypeLibelle": "Province"
1328 + },
1329 + {
1330 + "ID": 26,
1331 + "ZoneId": 717,
1332 + "ZoneTypeId": 18,
1333 + "ZoneTypeLibelle": "Direction territoriale - MTMD"
1334 + }
1335 + ],
1336 + "Stationnements": [
1337 + {
1338 + "ID": 352215254200001037,
1339 + "Ordre": 1,
1340 + "Stationnement": [
1341 + {
1342 + "ID": 390518350100001029,
1343 + "Ordre": 1,
1344 + "ThesID": "16c8d1ca-044a-4333-9ba5-f5f471bd01ad",
1345 + "ThesLibelle": "Stationnement sur place gratuit"
1346 + }
1347 + ]
1348 + }
1349 + ],
1350 + "Identifications": [
1351 + {
1352 + "ID": 304610592502477000,
1353 + "Ordre": 1,
1354 + "Typedetablissementoudequipement": {
1355 + "ThesID": "437b272b-c46c-4fa0-a7b4-3210723c25d0",
1356 + "ThesLibelle": "Festival"
1357 + },
1358 + "Nomdefiche": "Festival de la chanson de Saint-Ambroise"
1359 + }
1360 + ],
1361 + "Accessibilites": [
1362 + {
1363 + "ID": 304610592502437000,
1364 + "Ordre": 1,
1365 + "ServicesadaptespourdeficienceauditiveselonKeroul": false,
1366 + "Locationdefauteuilroulant": false,
1367 + "Activitesequipementsadaptesselonlexploitant": true,
1368 + "ServicesadaptespourdeficiencevisuelleselonKeroul": false
1369 + }
1370 + ]
1371 + }
1372 + ]
1373 +}
\ No newline at end of file
added tests/test_connectors.py +148 −0
@@ -0,0 +1,148 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: tests/test_connectors.py
4 +# Desc: Tests des connecteurs sur fixtures réelles (réponses API figées) —
5 +# aucun appel réseau (CLAUDE.md §18).
6 +# ==============================================================================
7 +from __future__ import annotations
8 +
9 +import json
10 +from pathlib import Path
11 +
12 +from sortika.connectors import CONNECTORS
13 +from sortika.connectors.montreal import MontrealConnector
14 +from sortika.connectors.sitq import SitqConnector
15 +
16 +FIXTURES = Path(__file__).parent / "fixtures"
17 +
18 +
19 +def _patch_json(monkeypatch, connector, payloads):
20 + """Remplace get_json par une file de réponses figées."""
21 + queue = list(payloads)
22 + monkeypatch.setattr(connector, "get_json",
23 + lambda *a, **kw: queue.pop(0) if queue else {"result": {}})
24 +
25 +
26 +def test_registry_autodiscovers():
27 + for sid in ("sitq", "montreal", "lepointdevente", "laval",
28 + "evenko", "atuvu", "lavitrine"):
29 + assert sid in CONNECTORS, sid
30 +
31 +
32 +def test_evenko_parses_fixture(monkeypatch):
33 + from sortika.connectors.evenko import EvenkoConnector
34 + c = EvenkoConnector()
35 + fixture = json.loads((FIXTURES / "evenko_sample.json").read_text(encoding="utf-8"))
36 + fixture["nbPages"] = 1
37 + monkeypatch.setattr(c, "_search_page", lambda page: fixture if page == 0 else {"hits": []})
38 + events = [e.finalize() for e in c.fetch()]
39 + assert len(events) == 3
40 + ev = events[0]
41 + assert ev.source == "evenko"
42 + assert ev.venue and ev.city and ev.region
43 + assert ev.start_date and ev.start_date.startswith("20")
44 + assert ev.url.startswith("http") # billets Ticketmaster / salle
45 + assert "musique" in ev.categories or ev.categories
46 +
47 +
48 +def test_atuvu_parses_fixture():
49 + from sortika.connectors.atuvu import AtuvuConnector
50 + html = (FIXTURES / "atuvu_fiche.html").read_text(encoding="utf-8")
51 + rows = AtuvuConnector()._parse_fiche("https://atuvu.ca/billets-spectacle/arkansas", html)
52 + assert rows and rows[0]["external_id"] == "arkansas-2026-08-20"
53 + assert rows[0]["venue"] == "Église Saint-Antoine-de-Lavaltrie"
54 + assert rows[0]["city"] == "Lavaltrie"
55 + assert rows[0]["price_min"] == 27.0
56 +
57 +
58 +def test_sherbrooke_parses_fixture(monkeypatch):
59 + from sortika.connectors.sherbrooke import SherbrookeConnector
60 + c = SherbrookeConnector()
61 + html = (FIXTURES / "sherbrooke_page.html").read_text(encoding="utf-8")
62 + monkeypatch.setattr(c, "get", lambda url: type("R", (), {"text": html})())
63 + events = [e.finalize() for e in c.fetch()]
64 + assert len(events) == 3
65 + ev = events[0]
66 + assert ev.source == "sherbrooke"
67 + assert ev.city == "Sherbrooke" and ev.region == "Estrie"
68 + assert ev.start_date and ev.start_date.startswith("2026")
69 + assert ev.url.startswith("https://www.sherbrooke.ca/")
70 + assert ev.is_free in (True, False) # booléen `payant` de la source
71 + assert any(e.image.startswith("https://www.sherbrooke.ca/Fichiers/")
72 + for e in events)
73 +
74 +
75 +def test_lavitrine_parses_fixture():
76 + from sortika.connectors.lavitrine import LaVitrineConnector
77 + html = (FIXTURES / "lavitrine_fiche.html").read_text(encoding="utf-8")
78 + rows = LaVitrineConnector()._parse_fiche(
79 + "https://www.lavitrine.com/fr/evenement/100-ans/23777", html)
80 + assert rows and rows[0]["start_date"] == "2026-11-22"
81 + assert rows[0]["venue"] == "Théâtre Petit Champlain"
82 + assert rows[0]["city"] == "Québec"
83 + assert rows[0]["external_id"].startswith("23777-")
84 +
85 +
86 +def test_lepointdevente_parses_fixture():
87 + from sortika.connectors.lepointdevente import LePointDeVenteConnector
88 + html = (FIXTURES / "lepointdevente_page1.html").read_text(encoding="utf-8")
89 + events = [e.finalize() for e in LePointDeVenteConnector()._parse_page(html)]
90 + assert len(events) >= 40
91 + ev = next(e for e in events if "JOAT" in e.title)
92 + assert ev.city == "Montréal" and ev.region == "Montréal"
93 + assert ev.venue == "Club Soda"
94 + assert ev.start_date == "2026-09-06"
95 + assert ev.url.startswith("https://lepointdevente.com/billets/")
96 + # dates FR parsées et régions rattachées hors Montréal aussi
97 + stq = next(e for e in events if e.city == "Saint-Agapit")
98 + assert stq.region == "Chaudière-Appalaches"
99 + # entités HTML décodées (pas de &#039; résiduel)
100 + assert not any("&#" in e.title for e in events)
101 +
102 +
103 +def test_laval_parses_fixture(monkeypatch):
104 + from sortika.connectors.laval import LavalConnector
105 + c = LavalConnector()
106 + _patch_json(monkeypatch, c, [json.loads(
107 + (FIXTURES / "laval_sample.json").read_text(encoding="utf-8"))])
108 + events = [e.finalize() for e in c.fetch()]
109 + assert len(events) == 5
110 + ev = events[0]
111 + assert ev.source == "laval" and ev.region == "Laval"
112 + assert ev.url.startswith("https://www.laval.ca/")
113 + assert ev.start_date and ev.start_date.startswith("20")
114 + assert "<" not in ev.description # HTML de l'Excerpt nettoyé
115 +
116 +
117 +def test_sitq_parses_fixture(monkeypatch):
118 + c = SitqConnector()
119 + _patch_json(monkeypatch, c, [json.loads(
120 + (FIXTURES / "sitq_sample.json").read_text(encoding="utf-8"))])
121 + events = [e.finalize() for e in c.fetch()]
122 + assert len(events) == 4
123 + jack = next(e for e in events if e.title == "JACKALOPE")
124 + assert jack.uid == "sitq:EVENMTOV500NIO"
125 + assert jack.city == "Montréal"
126 + assert jack.region == "Montréal"
127 + assert jack.start_date == "2026-09-11" and jack.end_date == "2026-09-13"
128 + assert jack.lat and 45 < jack.lat < 46
129 + assert jack.website.startswith("http")
130 + assert "musique" in jack.categories or "sport" in jack.categories
131 +
132 +
133 +def test_montreal_parses_fixture(monkeypatch):
134 + c = MontrealConnector()
135 + fixture = json.loads((FIXTURES / "montreal_sample.json").read_text(encoding="utf-8"))
136 + # une seule page : total = nb d'enregistrements de la fixture
137 + fixture["result"]["total"] = len(fixture["result"]["records"])
138 + _patch_json(monkeypatch, c, [fixture])
139 + events = [e.finalize() for e in c.fetch()]
140 + assert len(events) == 4
141 + ev = events[0]
142 + assert ev.source == "montreal"
143 + assert ev.city == "Montréal" and ev.region == "Montréal"
144 + assert ev.start_date and ev.start_date.startswith("20")
145 + assert ev.url.startswith("https://montreal.ca/")
146 + # « Gratuit » de la fixture → is_free True, jamais un prix inventé
147 + frees = [e for e in events if e.is_free]
148 + assert frees and all(e.price_min is None for e in frees)
added tests/test_db.py +61 −0
@@ -0,0 +1,61 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: tests/test_db.py
4 +# Desc: Tests de la persistance — upsert, détection de changements, délai de
5 +# grâce (2 syncs), dérive, clé de déduplication inter-sources.
6 +# ==============================================================================
7 +from __future__ import annotations
8 +
9 +from sortika import db
10 +from sortika.schema import Event
11 +
12 +
13 +def _ev(eid="1", title="Fête au parc", price="Gratuit", source="sitq"):
14 + return Event(source=source, external_id=eid, url=f"https://x/{eid}",
15 + title=title, city="Québec", tourist_region="Québec",
16 + start_date="2027-01-15", price_label=price).finalize()
17 +
18 +
19 +def test_sync_lifecycle(tmp_path):
20 + con = db.connect(tmp_path / "t.db")
21 +
22 + s = db.sync_source(con, "sitq", [_ev()])
23 + assert (s["added"], s["removed"]) == (1, 0)
24 +
25 + # inchangé
26 + s = db.sync_source(con, "sitq", [_ev()])
27 + assert (s["added"], s["updated"], s["unchanged"]) == (0, 0, 1)
28 +
29 + # changement de contenu détecté
30 + s = db.sync_source(con, "sitq", [_ev(price="Adulte 10 $")])
31 + assert s["updated"] == 1
32 + row = con.execute("SELECT is_free, price_min FROM events").fetchone()
33 + assert (row["is_free"], row["price_min"]) == (0, 10.0)
34 +
35 + # disparition : grâce au 1er raté, retrait au 2e
36 + s = db.sync_source(con, "sitq", [])
37 + assert s["removed"] == 0
38 + s = db.sync_source(con, "sitq", [])
39 + assert s["removed"] == 1
40 + assert con.execute("SELECT active FROM events").fetchone()[0] == 0
41 +
42 +
43 +def test_drift_suspends_removals(tmp_path):
44 + con = db.connect(tmp_path / "t.db")
45 + batch = [_ev(eid=str(i), title=f"Événement {i}") for i in range(20)]
46 + for _ in range(3):
47 + db.sync_source(con, "sitq", batch)
48 + # chute brutale à 2 événements → alerte, aucun retrait
49 + s = db.sync_source(con, "sitq", batch[:2])
50 + assert "alert" in s and s["removed"] == 0
51 + n = con.execute("SELECT COUNT(*) FROM events WHERE active=1").fetchone()[0]
52 + assert n == 20
53 +
54 +
55 +def test_dedup_key_cross_source():
56 + a = _ev(source="sitq", title="Grand marché de Noël")
57 + b = Event(source="montreal", external_id="99", url="https://y",
58 + title=" GRAND MARCHÉ de Noël ", city="Québec",
59 + start_date="2027-01-15").finalize()
60 + assert a.dedup_key() == b.dedup_key()
61 + assert a.dedup_key() != _ev(title="Autre événement").dedup_key()
added tests/test_normalize.py +73 −0
@@ -0,0 +1,73 @@
1 +# ==============================================================================
2 +# Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 +# File: tests/test_normalize.py
4 +# Desc: Tests de la couche de normalisation (dates, prix, catégories, régions)
5 +# ==============================================================================
6 +from __future__ import annotations
7 +
8 +from sortika.normalize import (clean_city, clean_text, map_categories,
9 + parse_date_iso, parse_latlng, parse_price)
10 +from sortika.regions import region_from_city, region_from_tourist
11 +from sortika.schema import Event
12 +
13 +
14 +def test_parse_date_iso():
15 + assert parse_date_iso("2026-09-11T00:00:00") == "2026-09-11"
16 + assert parse_date_iso("2026-09-11") == "2026-09-11"
17 + assert parse_date_iso("") is None
18 + assert parse_date_iso("demain") is None
19 + assert parse_date_iso("0001-99-99") is None
20 +
21 +
22 +def test_parse_price():
23 + assert parse_price("Gratuit") == (True, None, "Gratuit")
24 + assert parse_price("Entrée libre") == (True, None, "Entrée libre")
25 + free, amt, _ = parse_price("Adulte 25 $, enfant 12,50 $")
26 + assert free is False and amt == 12.50
27 + assert parse_price("Payant")[0] is False
28 + assert parse_price("")[0] is None
29 +
30 +
31 +def test_map_categories():
32 + assert map_categories(["Festival"]) == ["festival"]
33 + assert map_categories(["Manifestation sportive"]) == ["sport"]
34 + assert map_categories(["Chanson", "Sports et plein air"]) == ["musique", "sport"]
35 + assert map_categories(["Conseil et comité"]) == ["communautaire"]
36 + assert map_categories(["Zzz-inconnu"]) == ["autre"]
37 + assert map_categories([]) == ["autre"]
38 +
39 +
40 +def test_regions():
41 + assert region_from_tourist("Cantons-de-l'Est") == "Estrie"
42 + assert region_from_tourist("Charlevoix") == "Capitale-Nationale"
43 + assert region_from_tourist("Québec") == "Capitale-Nationale"
44 + assert region_from_city("Lévis") == "Chaudière-Appalaches"
45 + assert region_from_city("Gatineau") == "Outaouais"
46 + assert region_from_city("Ville-Inconnue") == ""
47 +
48 +
49 +def test_clean():
50 + assert clean_city("Montréal (Québec)") == "Montréal"
51 + assert clean_text("a <b>b</b>&nbsp;c") == "a b c"
52 +
53 +
54 +def test_parse_latlng():
55 + assert parse_latlng("45.5", "-73.5") == (45.5, -73.5)
56 + assert parse_latlng("0", "0") == (None, None)
57 + assert parse_latlng(None, None) == (None, None)
58 +
59 +
60 +def test_event_finalize_and_dedup():
61 + a = Event(source="sitq", external_id="X1", url="https://a",
62 + title=" Festival d'été ", city="Québec (Québec)",
63 + tourist_region="Québec", raw_categories=["Festival"],
64 + start_date="2026-07-01T00:00:00", price_label="Gratuit").finalize()
65 + b = Event(source="montreal", external_id="Y2", url="https://b",
66 + title="Festival d'été", city="Québec",
67 + start_date="2026-07-01").finalize()
68 + assert a.title == "Festival d'été"
69 + assert a.region == "Capitale-Nationale"
70 + assert a.categories == ["festival"]
71 + assert a.is_free is True
72 + assert a.end_date == "2026-07-01"
73 + assert a.dedup_key() == b.dedup_key()
74