SPB Git

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.

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 (Aug 17, 2026) Browse files

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
Large diff (586 lines) is not loaded by default to keep the page fast.Load diff
added requirements.txt +9 −0
Large diff (9 lines) is not loaded by default to keep the page fast.Load diff
added run.py +51 −0
Large diff (51 lines) is not loaded by default to keep the page fast.Load diff
added sortika/__init__.py +9 −0
Large diff (9 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/__init__.py +30 −0
Large diff (30 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/atuvu.py +127 −0
Large diff (127 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/base.py +142 −0
Large diff (142 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/evenko.py +113 −0
Large diff (113 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/laval.py +59 −0
Large diff (59 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/lavitrine.py +162 −0
Large diff (162 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/lepointdevente.py +96 −0
Large diff (96 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/montreal.py +114 −0
Large diff (114 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/sherbrooke.py +103 −0
Large diff (103 lines) is not loaded by default to keep the page fast.Load diff
added sortika/connectors/sitq.py +152 −0
Large diff (152 lines) is not loaded by default to keep the page fast.Load diff
added sortika/db.py +205 −0
Large diff (205 lines) is not loaded by default to keep the page fast.Load diff
added sortika/ingest.py +67 −0
Large diff (67 lines) is not loaded by default to keep the page fast.Load diff
added sortika/normalize.py +201 −0
Large diff (201 lines) is not loaded by default to keep the page fast.Load diff
added sortika/regions.py +173 −0
Large diff (173 lines) is not loaded by default to keep the page fast.Load diff
added sortika/schema.py +116 −0
Large diff (116 lines) is not loaded by default to keep the page fast.Load diff
added sortika/seo.py +200 −0
Large diff (200 lines) is not loaded by default to keep the page fast.Load diff
added sortika/web.py +207 −0
Large diff (207 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/atuvu_fiche.html +1426 −0
Large diff (1,426 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/evenko_sample.json +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/laval_sample.json +449 −0
Large diff (450 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/lavitrine_fiche.html +736 −0
Large diff (737 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/lepointdevente_page1.html +2284 −0
Large diff (2,284 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/montreal_sample.json +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/sherbrooke_page.html +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added tests/fixtures/sitq_sample.json +1373 −0
Large diff (1,374 lines) is not loaded by default to keep the page fast.Load diff
added tests/test_connectors.py +148 −0
Large diff (148 lines) is not loaded by default to keep the page fast.Load diff
added tests/test_db.py +61 −0
Large diff (61 lines) is not loaded by default to keep the page fast.Load diff
added tests/test_normalize.py +73 −0
Large diff (74 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme