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:
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "3967f6c2",
|
"version": "598c445e",
|
||||||
"timestamp": 1782655834971
|
"timestamp": 1782826461485
|
||||||
}
|
}
|
||||||
@@ -48,50 +48,106 @@ interface FetchResult {
|
|||||||
newItems: number;
|
newItems: number;
|
||||||
skippedItems: number;
|
skippedItems: number;
|
||||||
mergedItems: number;
|
mergedItems: number;
|
||||||
error?: string;
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Dictionnaire des départements d'Auvergne-Rhône-Alpes ────────────────────
|
// ─── Dictionnaire des départements (AuRA, PACA, Nouvelle-Aquitaine, Occitanie, Bourgogne-FC) ──────
|
||||||
// IMPORTANT : les départements composés (Haute-Loire, Haute-Savoie, Puy-de-Dôme)
|
// IMPORTANT : les départements composés doivent être AVANT leurs variantes simples
|
||||||
// doivent être AVANT leurs variantes simples (Loire, Savoie) pour éviter les faux positifs.
|
// pour éviter les faux positifs (ex. Haute-Loire avant Loire).
|
||||||
|
|
||||||
const AURA_DEPARTMENTS: Array<{ pattern: RegExp; name: string; num: string }> = [
|
const AURA_DEPARTMENTS: Array<{ pattern: RegExp; name: string; num: string; region: string }> = [
|
||||||
// Composés en premier
|
|
||||||
{ pattern: /haute-savoie|haute savoie|(?<!\d)74(?!\d)|\(74\)/i, name: "Haute-Savoie", num: "74" },
|
// ─── AUVERGNE-RHÔNE-ALPES ─────────────────────────────────────────────────────────────────
|
||||||
{ pattern: /haute-loire|haute loire|(?<!\d)43(?!\d)|\(43\)/i, name: "Haute-Loire", num: "43" },
|
{ pattern: /haute-savoie|haute savoie|(?<!\d)74(?!\d)|\(74\)/i, name: "Haute-Savoie", num: "74", 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" },
|
{ pattern: /haute-loire|haute loire|(?<!\d)43(?!\d)|\(43\)/i, name: "Haute-Loire", num: "43", region: "Auvergne-Rhône-Alpes" },
|
||||||
// Simples ensuite
|
{ 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" },
|
{ 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" },
|
{ 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" },
|
{ 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" },
|
{ 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" },
|
{ 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" },
|
{ pattern: /\bis[eè]re\b|(?<!\d)38(?!\d)|\(38\)/i, name: "Isère", num: "38", region: "Auvergne-Rhône-Alpes" },
|
||||||
// Loire : exclure "Haute-Loire" déjà traité
|
{ pattern: /(?<!haute-)\bloire\b|(?<!\d)42(?!\d)|\(42\)/i, name: "Loire", num: "42", region: "Auvergne-Rhône-Alpes" },
|
||||||
{ 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", 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" },
|
{ pattern: /\bsavoie\b(?!.*haute)|(?<!\d)73(?!\d)|\(73\)/i, name: "Savoie", num: "73", region: "Auvergne-Rhône-Alpes" },
|
||||||
// Savoie : exclure "Haute-Savoie" déjà traité
|
|
||||||
{ pattern: /\bsavoie\b(?!.*haute)|(?<!\d)73(?!\d)|\(73\)/i, name: "Savoie", num: "73" },
|
// ─── 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.
|
* 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) {
|
for (const dept of AURA_DEPARTMENTS) {
|
||||||
if (dept.pattern.test(text)) {
|
if (dept.pattern.test(text)) {
|
||||||
return { name: dept.name, num: dept.num };
|
return { name: dept.name, num: dept.num, region: dept.region };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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 {
|
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 ────────────────────────────────────
|
// ─── Normalisation du titre pour la fusion ────────────────────────────────────
|
||||||
@@ -105,16 +161,14 @@ function isRegional(text: string): boolean {
|
|||||||
function buildMergeKey(title: string): string {
|
function buildMergeKey(title: string): string {
|
||||||
let key = title.toLowerCase();
|
let key = title.toLowerCase();
|
||||||
|
|
||||||
// Supprimer les noms de départements AuRA (composites d'abord)
|
// Supprimer les noms de départements (composites d'abord)
|
||||||
const deptNames = [
|
const deptNames = AURA_DEPARTMENTS.flatMap(d => [
|
||||||
"haute-savoie", "haute savoie", "haute-loire", "haute loire",
|
d.name.toLowerCase(),
|
||||||
"puy-de-dôme", "puy-de-dome", "puy de dôme", "puy de dome",
|
d.name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
|
||||||
"ain", "allier", "ardèche", "ardeche", "cantal",
|
]).concat([
|
||||||
"drôme", "drome", "isère", "isere", "loire",
|
|
||||||
"rhône", "rhone", "savoie",
|
|
||||||
"métropole de lyon", "metropole de lyon", "lyon",
|
"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"
|
// Protéger "auvergne-rhône-alpes" et "rhône-alpes"
|
||||||
key = key.replace(/auvergne.?rh.?ne.?alpes/gi, "__AURA__");
|
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.
|
* 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 } {
|
export function detectVeilleNiveauTerritoire(text: string): { niveau: VeilleNiveau; territoire: string } {
|
||||||
const dept = detectDepartment(text);
|
const dept = detectDepartment(text);
|
||||||
if (dept) {
|
if (dept) {
|
||||||
return { niveau: "departemental", territoire: dept.name };
|
return { niveau: "departemental", territoire: dept.name };
|
||||||
}
|
}
|
||||||
if (isRegional(text)) {
|
const region = detectRegion(text);
|
||||||
return { niveau: "regional", territoire: "Auvergne-Rhône-Alpes" };
|
if (region) {
|
||||||
|
return { niveau: "regional", territoire: region };
|
||||||
}
|
}
|
||||||
return { niveau: "national", territoire: "France" };
|
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étecte le niveau de localisation le plus fin dans le texte :
|
||||||
* - Département AuRA si mentionné → region = "Auvergne-Rhône-Alpes", departement = "Nom (num)"
|
* - Département connu → region = région du département, departement = "Nom (num)"
|
||||||
* - Région AuRA si mentionnée → region = "Auvergne-Rhône-Alpes", departement = null
|
* - Région connue → region = nom de la région, departement = null
|
||||||
* - Sinon → region = "National", departement = null
|
* - Sinon → region = "National", departement = null
|
||||||
*/
|
*/
|
||||||
export function detectAapGeo(text: string): { region: string; departement: string | null } {
|
export function detectAapGeo(text: string): { region: string; departement: string | null } {
|
||||||
const dept = detectDepartment(text);
|
const dept = detectDepartment(text);
|
||||||
if (dept) {
|
if (dept) {
|
||||||
return {
|
return {
|
||||||
region: "Auvergne-Rhône-Alpes",
|
region: dept.region,
|
||||||
departement: `${dept.name} (${dept.num})`,
|
departement: `${dept.name} (${dept.num})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (isRegional(text)) {
|
const region = detectRegion(text);
|
||||||
|
if (region) {
|
||||||
return {
|
return {
|
||||||
region: "Auvergne-Rhône-Alpes",
|
region,
|
||||||
departement: null,
|
departement: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -524,10 +581,10 @@ async function processFeed(feed: RssFeed): Promise<FetchResult> {
|
|||||||
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
result.status = "error";
|
result.status = "error";
|
||||||
result.error = e?.message ?? String(e);
|
result.errorMessage = e?.message ?? String(e);
|
||||||
try {
|
try {
|
||||||
await db.update(rssFeeds)
|
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));
|
.where(eq(rssFeeds.id, feed.id));
|
||||||
} catch (_) { /* ignore */ }
|
} catch (_) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user