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# connectors/troisrivieres.py : connecteur Ville de Trois-Rivières — activités5# Source : https://www.v3r.net/activites-et-loisirs/calendrier-des-activites6# (l'API REST WP /wp-json est verrouillée en 401 — consigné7# 2026-08-18 — mais le calendrier AngularJS interroge un8# endpoint AJAX public du thème).9# Extraction: POST JSON /ajax/wp-queries/archive-activites.php (même appel10# que le frontend, découvert dans ActivitesController.js) —11# 1 requête : title, startDate/endDate « dd/mm/yyyy »,12# description (= NOM DU LIEU, vérifié sur les 17 fiches live13# 2026-08-25), imageUrl, url de la fiche. Heure non publiée14# par l'endpoint → jamais inventée.15# Accès : endpoint public du site municipal (aucune auth) ; robots.txt16# n'interdit pas /ajax/. GET/POST directs, identification17# honnête, 1 requête par sync.18# Prix : non publié par l'endpoint → jamais inventé.19# -----------------------------------------------------------------------------20from __future__ import annotations2122import re23import time2425from ..normalize import clean_text26from ..schema import Event27from .base import BaseConnector2829API = "https://www.v3r.net/ajax/wp-queries/archive-activites.php"30QUERY = {31 "postType": "activites",32 "orderBy": "meta_value date",33 "order": "DESC",34 "filters": {"category": "", "startDate": "", "endDate": ""},35}36_DDMMYYYY_RE = re.compile(r"^(\d{1,2})/(\d{1,2})/(\d{4})$")373839def _iso(raw: str | None) -> str | None:40 """« 25/08/2026 » (format de l'endpoint) → « 2026-08-25 »."""41 m = _DDMMYYYY_RE.match(str(raw or "").strip())42 if not m:43 return None44 d, mo, y = int(m.group(1)), int(m.group(2)), int(m.group(3))45 if not (1 <= d <= 31 and 1 <= mo <= 12 and 1990 <= y <= 2100):46 return None47 return f"{y:04d}-{mo:02d}-{d:02d}"484950class TroisRivieresConnector(BaseConnector):51 source_id = "troisrivieres"52 request_delay = 0.85354 def _post_json(self) -> dict:55 self._throttle()56 resp = self.session.post(API, json={"query": QUERY},57 timeout=self.timeout)58 self._last_request = time.time()59 resp.raise_for_status()60 return resp.json()6162 def fetch(self) -> list[Event]:63 payload = self._post_json()64 events: list[Event] = []65 seen: set[str] = set()66 for rec in payload.get("data") or []:67 title = clean_text(rec.get("title") or "")68 url = rec.get("url") or ""69 start = _iso(rec.get("startDate"))70 if not title or not url or not start:71 continue72 slug = url.rstrip("/").rsplit("/", 1)[-1]73 ext = f"{slug}-{start}"74 if ext in seen:75 continue76 seen.add(ext)77 events.append(Event(78 source=self.source_id,79 external_id=ext,80 url=url,81 title=title,82 raw_categories=[title],83 venue=clean_text(rec.get("description") or ""),84 city="Trois-Rivières",85 region="Mauricie",86 start_date=start,87 end_date=_iso(rec.get("endDate")) or start,88 organizer="Ville de Trois-Rivières",89 image=rec.get("imageUrl") or "",90 ))91 return events92