Toutes les sorties et tous les événements du Québec, un seul endroit — 7 connecteurs, fiches SSR, design Groupe KA.
HTML 82.9%
Python 15.2%
TypeScript 0.9%
JavaScript 0.7%
1# -----------------------------------------------------------------------------2# Sorti-Ka — Agrégateur de sorties & événements (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# regions.py : les 17 régions administratives du Québec + correspondances5# région touristique → région administrative et ville → région6# (patron Lou-Ka : rattacher chaque fiche à sa région via l'adresse)7# -----------------------------------------------------------------------------8from __future__ import annotations910import re11import unicodedata1213# Les 17 régions administratives officielles (ordre des numéros 01-17).14REGIONS: list[str] = [15 "Bas-Saint-Laurent",16 "Saguenay–Lac-Saint-Jean",17 "Capitale-Nationale",18 "Mauricie",19 "Estrie",20 "Montréal",21 "Outaouais",22 "Abitibi-Témiscamingue",23 "Côte-Nord",24 "Nord-du-Québec",25 "Gaspésie–Îles-de-la-Madeleine",26 "Chaudière-Appalaches",27 "Laval",28 "Lanaudière",29 "Laurentides",30 "Montérégie",31 "Centre-du-Québec",32]333435def _k(s: str) -> str:36 """Clé de comparaison : minuscules, sans accents, tirets/points/apostrophes unifiés."""37 s = unicodedata.normalize("NFD", s or "")38 s = "".join(c for c in s if unicodedata.category(c) != "Mn")39 s = s.lower().replace("’", "'")40 return re.sub(r"[\s\-–—_'.]+", " ", s).strip()414243# Région touristique (SIT Québec / ATR, 22 régions) → région administrative.44TOURIST_TO_ADMIN: dict[str, str] = {45 _k("Abitibi-Témiscamingue"): "Abitibi-Témiscamingue",46 _k("Baie-James"): "Nord-du-Québec",47 _k("Eeyou Istchee Baie-James"): "Nord-du-Québec",48 _k("Nunavik"): "Nord-du-Québec",49 _k("Bas-Saint-Laurent"): "Bas-Saint-Laurent",50 _k("Cantons-de-l'Est"): "Estrie",51 _k("Centre-du-Québec"): "Centre-du-Québec",52 _k("Charlevoix"): "Capitale-Nationale",53 _k("Chaudière-Appalaches"): "Chaudière-Appalaches",54 _k("Côte-Nord"): "Côte-Nord",55 _k("Duplessis"): "Côte-Nord",56 _k("Manicouagan"): "Côte-Nord",57 _k("Gaspésie"): "Gaspésie–Îles-de-la-Madeleine",58 _k("Îles-de-la-Madeleine"): "Gaspésie–Îles-de-la-Madeleine",59 _k("Lanaudière"): "Lanaudière",60 _k("Laurentides"): "Laurentides",61 _k("Laval"): "Laval",62 _k("Mauricie"): "Mauricie",63 _k("Montérégie"): "Montérégie",64 _k("Montréal"): "Montréal",65 _k("Outaouais"): "Outaouais",66 _k("Québec"): "Capitale-Nationale",67 _k("Région de Québec"): "Capitale-Nationale",68 _k("Saguenay–Lac-Saint-Jean"): "Saguenay–Lac-Saint-Jean",69}7071# Villes → région administrative. Table PRINCIPALE : data/villes_regions.json,72# générée depuis le Répertoire des municipalités du Québec (MAMH, données73# ouvertes, 1 193 municipalités sans ambiguïté). La table manuelle ci-dessous74# sert de repli et couvre quelques graphies fréquentes des sources.75_VILLES_JSON = None767778def _villes_table() -> dict[str, str]:79 global _VILLES_JSON80 if _VILLES_JSON is None:81 import json82 from pathlib import Path83 p = Path(__file__).resolve().parent.parent / "data" / "villes_regions.json"84 try:85 _VILLES_JSON = json.loads(p.read_text(encoding="utf-8"))["villes"]86 except Exception:87 _VILLES_JSON = {}88 return _VILLES_JSON899091CITY_TO_ADMIN: dict[str, str] = {92 _k(city): region93 for city, region in {94 "Montréal": "Montréal",95 "Québec": "Capitale-Nationale",96 "Lévis": "Chaudière-Appalaches",97 "Laval": "Laval",98 "Gatineau": "Outaouais",99 "Longueuil": "Montérégie",100 "Brossard": "Montérégie",101 "Saint-Jean-sur-Richelieu": "Montérégie",102 "Granby": "Montérégie",103 "Salaberry-de-Valleyfield": "Montérégie",104 "Sorel-Tracy": "Montérégie",105 "Vaudreuil-Dorion": "Montérégie",106 "Sherbrooke": "Estrie",107 "Magog": "Estrie",108 "Bromont": "Estrie",109 "Trois-Rivières": "Mauricie",110 "Shawinigan": "Mauricie",111 "La Tuque": "Mauricie",112 "Saguenay": "Saguenay–Lac-Saint-Jean",113 "Alma": "Saguenay–Lac-Saint-Jean",114 "Roberval": "Saguenay–Lac-Saint-Jean",115 "Dolbeau-Mistassini": "Saguenay–Lac-Saint-Jean",116 "Rimouski": "Bas-Saint-Laurent",117 "Rivière-du-Loup": "Bas-Saint-Laurent",118 "Matane": "Bas-Saint-Laurent",119 "Gaspé": "Gaspésie–Îles-de-la-Madeleine",120 "Percé": "Gaspésie–Îles-de-la-Madeleine",121 "Les Îles-de-la-Madeleine": "Gaspésie–Îles-de-la-Madeleine",122 "Baie-Comeau": "Côte-Nord",123 "Sept-Îles": "Côte-Nord",124 "Rouyn-Noranda": "Abitibi-Témiscamingue",125 "Val-d'Or": "Abitibi-Témiscamingue",126 "Amos": "Abitibi-Témiscamingue",127 "Chibougamau": "Nord-du-Québec",128 "Drummondville": "Centre-du-Québec",129 "Victoriaville": "Centre-du-Québec",130 "Bécancour": "Centre-du-Québec",131 "Joliette": "Lanaudière",132 "Terrebonne": "Lanaudière",133 "Repentigny": "Lanaudière",134 "Mascouche": "Lanaudière",135 "Saint-Jérôme": "Laurentides",136 "Mont-Tremblant": "Laurentides",137 "Blainville": "Laurentides",138 "Saint-Sauveur": "Laurentides",139 "Mirabel": "Laurentides",140 "Saint-Georges": "Chaudière-Appalaches",141 "Thetford Mines": "Chaudière-Appalaches",142 "Montmagny": "Chaudière-Appalaches",143 "Baie-Saint-Paul": "Capitale-Nationale",144 "La Malbaie": "Capitale-Nationale",145 "Wendake": "Capitale-Nationale",146 "L'Ancienne-Lorette": "Capitale-Nationale",147 }.items()148}149150151def region_from_tourist(label: str) -> str:152 """Région administrative à partir d'une région touristique (SITQ/ATR)."""153 return TOURIST_TO_ADMIN.get(_k(label), "")154155156def region_from_city(city: str) -> str:157 """Région administrative à partir d'une ville (MAMH d'abord, repli manuel)."""158 key = _k(city)159 for candidate in (160 key,161 # « St-Damase » → « saint damase », « Ste-Anne » → « sainte anne »162 re.sub(r"\bste\b", "sainte", re.sub(r"\bst\b", "saint", key)),163 # « Sainte-Adèle (secteur X) » / « Québec, arr. de … »164 key.split("(")[0].split(",")[0].strip(),165 ):166 hit = _villes_table().get(candidate) or CITY_TO_ADMIN.get(candidate, "")167 if hit:168 return hit169 return ""170171172def is_valid_region(region: str) -> bool:173 return region in REGIONS174