Pure matching (date locale Cugnaux + titre normalisé) and paginated transport, both fully tested. HTML extraction via DOMParser stays impure and untested (absent from Bun), like formulaire-mairie.js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.5 KiB
JavaScript
67 lines
2.5 KiB
JavaScript
// Matching événement Nextcloud ↔ carte de l'agenda mairie. Fonctions PURES,
|
|
// sans DOM (testables) : c'est le cœur du risque produit (précision du match,
|
|
// fuseau, normalisation du titre). Biais PRÉCISION v1 : dans le doute, on
|
|
// n'affirme pas « publié » (faux négatif bénin, cf. plan / DECISIONS.md).
|
|
|
|
const FUSEAU = "Europe/Paris";
|
|
|
|
// en-CA rend directement "YYYY-MM-DD" : même forme que le <time datetime> de
|
|
// l'agenda, comparable sans reformatage.
|
|
const FORMAT_JOUR_PARIS = new Intl.DateTimeFormat("en-CA", {
|
|
timeZone: FUSEAU,
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
});
|
|
|
|
// Forme stable d'un titre : ce qui doit matcher malgré les écarts cosmétiques
|
|
// entre le calendrier et la mairie. On RETIRE la ponctuation (sans la remplacer
|
|
// par une espace) pour que « Atelier B.D. » == « Atelier BD ».
|
|
export function normaliserTitre(titre) {
|
|
return titre
|
|
.normalize("NFD")
|
|
.replace(/[̀-ͯ]/gu, "") // diacritiques isolés par NFD
|
|
.replace(/[^\p{Letter}\p{Number}\s]/gu, "") // ponctuation ET emojis
|
|
.toLowerCase()
|
|
.replace(/\s+/gu, " ")
|
|
.trim();
|
|
}
|
|
|
|
// Le <time datetime> de l'agenda est une date NUE (pas d'heure) : on la compare
|
|
// à la date locale de Cugnaux du début, jamais à une conversion de fuseau.
|
|
export function dateLocaleCugnaux(evenement) {
|
|
return evenement.journeeEntiere
|
|
? formaterDateFlottante(evenement.debut)
|
|
: FORMAT_JOUR_PARIS.format(evenement.debut);
|
|
}
|
|
|
|
// Journée entière = date flottante (RFC 5545) : composantes LOCALES, jamais un
|
|
// fuseau, sinon la date glisse d'un jour (cf. presentation.js / DECISIONS.md).
|
|
function formaterDateFlottante(date) {
|
|
const annee = date.getFullYear();
|
|
const mois = String(date.getMonth() + 1).padStart(2, "0");
|
|
const jour = String(date.getDate()).padStart(2, "0");
|
|
return `${annee}-${mois}-${jour}`;
|
|
}
|
|
|
|
// Index des cartes par `date|titre normalisé` → lien. Deux cartes de même clé
|
|
// (même jour + même titre normalisé, rare) : la DERNIÈRE indexée l'emporte.
|
|
export function indexerCartes(cartes) {
|
|
const index = new Map();
|
|
for (const carte of cartes) {
|
|
index.set(cle(carte.date, carte.titre), carte.lien);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Match STRICT : même date locale ET titre normalisé identique. Rien d'autre ne
|
|
// vaut « publié » en v1.
|
|
export function trouverPublication(evenement, index) {
|
|
const lien = index.get(cle(dateLocaleCugnaux(evenement), evenement.titre));
|
|
return lien ? { publie: true, lien } : { publie: false };
|
|
}
|
|
|
|
function cle(date, titre) {
|
|
return `${date}|${normaliserTitre(titre)}`;
|
|
}
|