Checkpoint: Extension du dictionnaire des départements à 5 régions (47 départements). Ajout de la fonction detectRegion pour détecter la région par son nom. Les fonctions detectVeilleNiveauTerritoire et detectAapGeo retournent désormais la région réelle du département détecté (plus de valeur codée en dur "Auvergne-Rhône-Alpes"). buildMergeKey mis à jour pour couvrir tous les noms de départements.
This commit is contained in:
@@ -48,50 +48,106 @@ interface FetchResult {
|
||||
newItems: number;
|
||||
skippedItems: number;
|
||||
mergedItems: number;
|
||||
error?: string;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
// ─── Dictionnaire des départements d'Auvergne-Rhône-Alpes ────────────────────
|
||||
// IMPORTANT : les départements composés (Haute-Loire, Haute-Savoie, Puy-de-Dôme)
|
||||
// doivent être AVANT leurs variantes simples (Loire, Savoie) pour éviter les faux positifs.
|
||||
// ─── Dictionnaire des départements (AuRA, PACA, Nouvelle-Aquitaine, Occitanie, Bourgogne-FC) ──────
|
||||
// IMPORTANT : les départements composés doivent être AVANT leurs variantes simples
|
||||
// pour éviter les faux positifs (ex. Haute-Loire avant Loire).
|
||||
|
||||
const AURA_DEPARTMENTS: Array<{ pattern: RegExp; name: string; num: string }> = [
|
||||
// Composés en premier
|
||||
{ pattern: /haute-savoie|haute savoie|(?<!\d)74(?!\d)|\(74\)/i, name: "Haute-Savoie", num: "74" },
|
||||
{ pattern: /haute-loire|haute loire|(?<!\d)43(?!\d)|\(43\)/i, name: "Haute-Loire", num: "43" },
|
||||
{ pattern: /puy-de-d[oô]me|puy de d[oô]me|(?<!\d)63(?!\d)|\(63\)/i, name: "Puy-de-Dôme", num: "63" },
|
||||
// Simples ensuite
|
||||
{ pattern: /\bain\b|(?<!\d)01(?!\d)|\(01\)/i, name: "Ain", num: "01" },
|
||||
{ pattern: /\ballier\b|(?<!\d)03(?!\d)|\(03\)/i, name: "Allier", num: "03" },
|
||||
{ pattern: /\bard[eè]che\b|(?<!\d)07(?!\d)|\(07\)/i, name: "Ardèche", num: "07" },
|
||||
{ pattern: /\bcantal\b|(?<!\d)15(?!\d)|\(15\)/i, name: "Cantal", num: "15" },
|
||||
{ pattern: /\bdr[oô]me\b|(?<!\d)26(?!\d)|\(26\)/i, name: "Drôme", num: "26" },
|
||||
{ pattern: /\bis[eè]re\b|(?<!\d)38(?!\d)|\(38\)/i, name: "Isère", num: "38" },
|
||||
// Loire : exclure "Haute-Loire" déjà traité
|
||||
{ pattern: /(?<!haute-)\bloire\b|(?<!\d)42(?!\d)|\(42\)/i, name: "Loire", num: "42" },
|
||||
{ pattern: /\brhone\b|\brhône\b|(?<!\d)69(?!\d)|\(69\)|m[eé]tropole\s+de\s+lyon/i, name: "Rhône", num: "69" },
|
||||
// Savoie : exclure "Haute-Savoie" déjà traité
|
||||
{ pattern: /\bsavoie\b(?!.*haute)|(?<!\d)73(?!\d)|\(73\)/i, name: "Savoie", num: "73" },
|
||||
const AURA_DEPARTMENTS: Array<{ pattern: RegExp; name: string; num: string; region: string }> = [
|
||||
|
||||
// ─── AUVERGNE-RHÔNE-ALPES ─────────────────────────────────────────────────────────────────
|
||||
{ pattern: /haute-savoie|haute savoie|(?<!\d)74(?!\d)|\(74\)/i, name: "Haute-Savoie", num: "74", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /haute-loire|haute loire|(?<!\d)43(?!\d)|\(43\)/i, name: "Haute-Loire", num: "43", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /puy-de-d[oô]me|puy de d[oô]me|(?<!\d)63(?!\d)|\(63\)/i, name: "Puy-de-Dôme", num: "63", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bain\b|(?<!\d)01(?!\d)|\(01\)/i, name: "Ain", num: "01", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\ballier\b|(?<!\d)03(?!\d)|\(03\)/i, name: "Allier", num: "03", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bard[eè]che\b|(?<!\d)07(?!\d)|\(07\)/i, name: "Ardèche", num: "07", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bcantal\b|(?<!\d)15(?!\d)|\(15\)/i, name: "Cantal", num: "15", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bdr[oô]me\b|(?<!\d)26(?!\d)|\(26\)/i, name: "Drôme", num: "26", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bis[eè]re\b|(?<!\d)38(?!\d)|\(38\)/i, name: "Isère", num: "38", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /(?<!haute-)\bloire\b|(?<!\d)42(?!\d)|\(42\)/i, name: "Loire", num: "42", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\brhone\b|\brhône\b|(?<!\d)69(?!\d)|\(69\)|m[eé]tropole\s+de\s+lyon/i, name: "Rhône", num: "69", region: "Auvergne-Rhône-Alpes" },
|
||||
{ pattern: /\bsavoie\b(?!.*haute)|(?<!\d)73(?!\d)|\(73\)/i, name: "Savoie", num: "73", region: "Auvergne-Rhône-Alpes" },
|
||||
|
||||
// ─── PROVENCE-ALPES-CÔTE D'AZUR (PACA) ────────────────────────────────────────────────────
|
||||
{ pattern: /alpes-de-haute-provence|alpes de haute provence|(?<!\d)04(?!\d)|\(04\)/i, name: "Alpes-de-Haute-Provence", num: "04", region: "Provence-Alpes-Côte d'Azur" },
|
||||
{ pattern: /hautes-alpes|hautes alpes|(?<!\d)05(?!\d)|\(05\)/i, name: "Hautes-Alpes", num: "05", region: "Provence-Alpes-Côte d'Azur" },
|
||||
{ pattern: /alpes-maritimes|alpes maritimes|(?<!\d)06(?!\d)|\(06\)/i, name: "Alpes-Maritimes", num: "06", region: "Provence-Alpes-Côte d'Azur" },
|
||||
{ pattern: /bouches-du-rh[oô]ne|bouches du rh[oô]ne|(?<!\d)13(?!\d)|\(13\)|marseille/i, name: "Bouches-du-Rhône", num: "13", region: "Provence-Alpes-Côte d'Azur" },
|
||||
{ pattern: /\bvar\b|(?<!\d)83(?!\d)|\(83\)/i, name: "Var", num: "83", region: "Provence-Alpes-Côte d'Azur" },
|
||||
{ pattern: /\bvaucluse\b|(?<!\d)84(?!\d)|\(84\)/i, name: "Vaucluse", num: "84", region: "Provence-Alpes-Côte d'Azur" },
|
||||
|
||||
// ─── NOUVELLE-AQUITAINE ───────────────────────────────────────────────────────────────────────────
|
||||
{ pattern: /charente-maritime|charente maritime|(?<!\d)17(?!\d)|\(17\)/i, name: "Charente-Maritime", num: "17", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bcharente\b|(?<!\d)16(?!\d)|\(16\)/i, name: "Charente", num: "16", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bcorr[eè]ze\b|(?<!\d)19(?!\d)|\(19\)/i, name: "Corrèze", num: "19", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bcreuse\b|(?<!\d)23(?!\d)|\(23\)/i, name: "Creuse", num: "23", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bdordogne\b|(?<!\d)24(?!\d)|\(24\)/i, name: "Dordogne", num: "24", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bgironde\b|(?<!\d)33(?!\d)|\(33\)|bordeaux/i, name: "Gironde", num: "33", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\blandes\b|(?<!\d)40(?!\d)|\(40\)/i, name: "Landes", num: "40", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /lot-et-garonne|lot et garonne|(?<!\d)47(?!\d)|\(47\)/i, name: "Lot-et-Garonne", num: "47", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /pyr[eé]n[eé]es-atlantiques|pyr[eé]n[eé]es atlantiques|(?<!\d)64(?!\d)|\(64\)/i, name: "Pyrénées-Atlantiques", num: "64", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /deux-s[eè]vres|deux s[eè]vres|(?<!\d)79(?!\d)|\(79\)/i, name: "Deux-Sèvres", num: "79", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /haute-vienne|haute vienne|(?<!\d)87(?!\d)|\(87\)/i, name: "Haute-Vienne", num: "87", region: "Nouvelle-Aquitaine" },
|
||||
{ pattern: /\bvienne\b|(?<!\d)86(?!\d)|\(86\)/i, name: "Vienne", num: "86", region: "Nouvelle-Aquitaine" },
|
||||
|
||||
// ─── OCCITANIE ───────────────────────────────────────────────────────────────────────────────────
|
||||
{ pattern: /\bari[eè]ge\b|(?<!\d)09(?!\d)|\(09\)/i, name: "Ariège", num: "09", region: "Occitanie" },
|
||||
{ pattern: /\baude\b|(?<!\d)11(?!\d)|\(11\)/i, name: "Aude", num: "11", region: "Occitanie" },
|
||||
{ pattern: /\baveyron\b|(?<!\d)12(?!\d)|\(12\)/i, name: "Aveyron", num: "12", region: "Occitanie" },
|
||||
{ pattern: /\bgard\b|(?<!\d)30(?!\d)|\(30\)/i, name: "Gard", num: "30", region: "Occitanie" },
|
||||
{ pattern: /haute-garonne|haute garonne|(?<!\d)31(?!\d)|\(31\)|toulouse/i, name: "Haute-Garonne", num: "31", region: "Occitanie" },
|
||||
{ pattern: /\bgers\b|(?<!\d)32(?!\d)|\(32\)/i, name: "Gers", num: "32", region: "Occitanie" },
|
||||
{ pattern: /\bh[eé]rault\b|(?<!\d)34(?!\d)|\(34\)|montpellier/i, name: "Hérault", num: "34", region: "Occitanie" },
|
||||
{ pattern: /\blot\b|(?<!\d)46(?!\d)|\(46\)/i, name: "Lot", num: "46", region: "Occitanie" },
|
||||
{ pattern: /\bloz[eè]re\b|(?<!\d)48(?!\d)|\(48\)/i, name: "Lozère", num: "48", region: "Occitanie" },
|
||||
{ pattern: /hautes-pyr[eé]n[eé]es|hautes pyr[eé]n[eé]es|(?<!\d)65(?!\d)|\(65\)/i, name: "Hautes-Pyrénées", num: "65", region: "Occitanie" },
|
||||
{ pattern: /pyr[eé]n[eé]es-orientales|pyr[eé]n[eé]es orientales|(?<!\d)66(?!\d)|\(66\)/i, name: "Pyrénées-Orientales", num: "66", region: "Occitanie" },
|
||||
{ pattern: /tarn-et-garonne|tarn et garonne|(?<!\d)82(?!\d)|\(82\)/i, name: "Tarn-et-Garonne", num: "82", region: "Occitanie" },
|
||||
{ pattern: /\btarn\b|(?<!\d)81(?!\d)|\(81\)/i, name: "Tarn", num: "81", region: "Occitanie" },
|
||||
|
||||
// ─── BOURGOGNE-FRANCHE-COMTÉ ────────────────────────────────────────────────────────────────────
|
||||
{ pattern: /c[oô]te-d.or|c[oô]te d.or|(?<!\d)21(?!\d)|\(21\)|dijon/i, name: "Côte-d'Or", num: "21", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /\bdoubs\b|(?<!\d)25(?!\d)|\(25\)|besan[cç]on/i, name: "Doubs", num: "25", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /\bjura\b|(?<!\d)39(?!\d)|\(39\)/i, name: "Jura", num: "39", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /\bni[eè]vre\b|(?<!\d)58(?!\d)|\(58\)/i, name: "Nièvre", num: "58", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /haute-sa[oô]ne|haute sa[oô]ne|(?<!\d)70(?!\d)|\(70\)/i, name: "Haute-Saône", num: "70", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /sa[oô]ne-et-loire|sa[oô]ne et loire|(?<!\d)71(?!\d)|\(71\)/i, name: "Saône-et-Loire", num: "71", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /\byonne\b|(?<!\d)89(?!\d)|\(89\)/i, name: "Yonne", num: "89", region: "Bourgogne-Franche-Comté" },
|
||||
{ pattern: /territoire-de-belfort|territoire de belfort|belfort|(?<!\d)90(?!\d)|\(90\)/i, name: "Territoire de Belfort", num: "90", region: "Bourgogne-Franche-Comté" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Détecte le département dans un texte.
|
||||
* Retourne { name, num } ou null si non trouvé.
|
||||
* Retourne { name, num, region } ou null si non trouvé.
|
||||
*/
|
||||
export function detectDepartment(text: string): { name: string; num: string } | null {
|
||||
export function detectDepartment(text: string): { name: string; num: string; region: string } | null {
|
||||
for (const dept of AURA_DEPARTMENTS) {
|
||||
if (dept.pattern.test(text)) {
|
||||
return { name: dept.name, num: dept.num };
|
||||
return { name: dept.name, num: dept.num, region: dept.region };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détecte si le texte mentionne la région Auvergne-Rhône-Alpes.
|
||||
* Détecte si le texte mentionne une des régions couvertes.
|
||||
* Retourne le nom de la région ou null.
|
||||
*/
|
||||
function detectRegion(text: string): string | null {
|
||||
if (/auvergne.?rh.?ne.?alpes|\baura\b|a\.r\.a\.|ars\s+auvergne|rh.?ne.?alpes/i.test(text)) return "Auvergne-Rhône-Alpes";
|
||||
if (/provence.?alpes.?c.?te.?d.?azur|\bpaca\b|ars\s+paca|ars\s+provence/i.test(text)) return "Provence-Alpes-Côte d'Azur";
|
||||
if (/nouvelle.?aquitaine|ars\s+nouvelle.?aquitaine/i.test(text)) return "Nouvelle-Aquitaine";
|
||||
if (/\boccitanie\b|ars\s+occitanie/i.test(text)) return "Occitanie";
|
||||
if (/bourgogne.?franche.?comt[eé]|ars\s+bourgogne/i.test(text)) return "Bourgogne-Franche-Comté";
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @deprecated Utiliser detectRegion à la place */
|
||||
function isRegional(text: string): boolean {
|
||||
return /auvergne.?rh.?ne.?alpes|aura\b|a\.r\.a\.|ars\s+auvergne/i.test(text);
|
||||
return detectRegion(text) !== null;
|
||||
}
|
||||
|
||||
// ─── Normalisation du titre pour la fusion ────────────────────────────────────
|
||||
@@ -105,16 +161,14 @@ function isRegional(text: string): boolean {
|
||||
function buildMergeKey(title: string): string {
|
||||
let key = title.toLowerCase();
|
||||
|
||||
// Supprimer les noms de départements AuRA (composites d'abord)
|
||||
const deptNames = [
|
||||
"haute-savoie", "haute savoie", "haute-loire", "haute loire",
|
||||
"puy-de-dôme", "puy-de-dome", "puy de dôme", "puy de dome",
|
||||
"ain", "allier", "ardèche", "ardeche", "cantal",
|
||||
"drôme", "drome", "isère", "isere", "loire",
|
||||
"rhône", "rhone", "savoie",
|
||||
// Supprimer les noms de départements (composites d'abord)
|
||||
const deptNames = AURA_DEPARTMENTS.flatMap(d => [
|
||||
d.name.toLowerCase(),
|
||||
d.name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
|
||||
]).concat([
|
||||
"métropole de lyon", "metropole de lyon", "lyon",
|
||||
"allier",
|
||||
];
|
||||
"marseille", "toulouse", "bordeaux", "montpellier", "nice", "dijon", "besançon", "besancon",
|
||||
]);
|
||||
|
||||
// Protéger "auvergne-rhône-alpes" et "rhône-alpes"
|
||||
key = key.replace(/auvergne.?rh.?ne.?alpes/gi, "__AURA__");
|
||||
@@ -189,14 +243,16 @@ export function detectVeilleCategorie(text: string): VeilleCategorie {
|
||||
|
||||
/**
|
||||
* Déduit le niveau et le territoire d'un article de veille.
|
||||
* Retourne le niveau le plus fin détecté : départemental > régional > national.
|
||||
*/
|
||||
export function detectVeilleNiveauTerritoire(text: string): { niveau: VeilleNiveau; territoire: string } {
|
||||
const dept = detectDepartment(text);
|
||||
if (dept) {
|
||||
return { niveau: "departemental", territoire: dept.name };
|
||||
}
|
||||
if (isRegional(text)) {
|
||||
return { niveau: "regional", territoire: "Auvergne-Rhône-Alpes" };
|
||||
const region = detectRegion(text);
|
||||
if (region) {
|
||||
return { niveau: "regional", territoire: region };
|
||||
}
|
||||
return { niveau: "national", territoire: "France" };
|
||||
}
|
||||
@@ -205,21 +261,22 @@ export function detectVeilleNiveauTerritoire(text: string): { niveau: VeilleNive
|
||||
|
||||
/**
|
||||
* Détecte le niveau de localisation le plus fin dans le texte :
|
||||
* - Département AuRA si mentionné → region = "Auvergne-Rhône-Alpes", departement = "Nom (num)"
|
||||
* - Région AuRA si mentionnée → region = "Auvergne-Rhône-Alpes", departement = null
|
||||
* - Département connu → region = région du département, departement = "Nom (num)"
|
||||
* - Région connue → region = nom de la région, departement = null
|
||||
* - Sinon → region = "National", departement = null
|
||||
*/
|
||||
export function detectAapGeo(text: string): { region: string; departement: string | null } {
|
||||
const dept = detectDepartment(text);
|
||||
if (dept) {
|
||||
return {
|
||||
region: "Auvergne-Rhône-Alpes",
|
||||
region: dept.region,
|
||||
departement: `${dept.name} (${dept.num})`,
|
||||
};
|
||||
}
|
||||
if (isRegional(text)) {
|
||||
const region = detectRegion(text);
|
||||
if (region) {
|
||||
return {
|
||||
region: "Auvergne-Rhône-Alpes",
|
||||
region,
|
||||
departement: null,
|
||||
};
|
||||
}
|
||||
@@ -524,10 +581,10 @@ async function processFeed(feed: RssFeed): Promise<FetchResult> {
|
||||
|
||||
} catch (e: any) {
|
||||
result.status = "error";
|
||||
result.error = e?.message ?? String(e);
|
||||
result.errorMessage = e?.message ?? String(e);
|
||||
try {
|
||||
await db.update(rssFeeds)
|
||||
.set({ lastFetchedAt: new Date(), lastFetchStatus: "error", lastFetchError: result.error })
|
||||
.set({ lastFetchedAt: new Date(), lastFetchStatus: "error", lastFetchError: result.errorMessage })
|
||||
.where(eq(rssFeeds.id, feed.id));
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user