Checkpoint: Ajout de nouveaux patterns de détection (fin de dépôt, fin des candidatures, remise, répondre avant le, ouvert jusqu'au, candidatures jusqu'au). Amélioration de parseFrDate : support JJ.MM.AAAA, ISO AAAA-MM-JJ, "1er mois". Normalisation du texte (retours à la ligne). Fonction findDateInStr pour extraire une date même quand du texte suit.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "4a018fb6",
|
||||
"timestamp": 1782828810514
|
||||
"version": "0f628f8c",
|
||||
"timestamp": 1782891184609
|
||||
}
|
||||
@@ -316,14 +316,15 @@ const MONTHS_FR: Record<string, number> = {
|
||||
};
|
||||
|
||||
function parseFrDate(dateStr: string): Date | null {
|
||||
// Format JJ/MM/AAAA ou JJ-MM-AAAA
|
||||
const numMatch = dateStr.match(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})/);
|
||||
const s = dateStr.trim();
|
||||
// Format JJ/MM/AAAA ou JJ-MM-AAAA ou JJ.MM.AAAA
|
||||
const numMatch = s.match(/(\d{1,2})[\/.\-](\d{1,2})[\/.\-](\d{4})/);
|
||||
if (numMatch) {
|
||||
const d = new Date(parseInt(numMatch[3]), parseInt(numMatch[2]) - 1, parseInt(numMatch[1]));
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
// Format JJ mois AAAA (ex: "30 septembre 2026")
|
||||
const textMatch = dateStr.match(/(\d{1,2})\s+([a-zéèêàù]+)\.?\s+(\d{4})/i);
|
||||
// Format JJ mois AAAA ou "1er mois AAAA"
|
||||
const textMatch = s.match(/(\d{1,2})(?:er|e)?\s+([a-z\u00e9\u00e8\u00ea\u00e0\u00f9\u00fb]+)\.?\s+(\d{4})/i);
|
||||
if (textMatch) {
|
||||
const month = MONTHS_FR[textMatch[2].toLowerCase().replace(/\.$/, "")];
|
||||
if (month !== undefined) {
|
||||
@@ -331,35 +332,65 @@ function parseFrDate(dateStr: string): Date | null {
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
}
|
||||
// Format AAAA-MM-JJ (ISO)
|
||||
const isoMatch = s.match(/(\d{4})-(\d{2})-(\d{2})/);
|
||||
if (isoMatch) {
|
||||
const d = new Date(parseInt(isoMatch[1]), parseInt(isoMatch[2]) - 1, parseInt(isoMatch[3]));
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extrait la première date valide trouvée dans une chaîne
|
||||
function findDateInStr(s: string): Date | null {
|
||||
// Essayer directement
|
||||
const d = parseFrDate(s);
|
||||
if (d) return d;
|
||||
// Chercher un sous-motif date dans la chaîne (utile quand du texte suit la date)
|
||||
const sub = s.match(/(\d{1,2}(?:er|e)?[\s.\/\-][\w\s.]+\d{4}|\d{4}-\d{2}-\d{2}|\d{1,2}[\/\.\-]\d{1,2}[\/\.\-]\d{4})/);
|
||||
if (sub) return parseFrDate(sub[1]);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function extractDateCloture(text: string): Date | null {
|
||||
const patterns = [
|
||||
// "date limite (de dépôt|de candidature|d'envoi|de soumission)? : ..."
|
||||
/date\s+limite(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// "clôture (des candidatures|des dossiers|le|au)? : ..."
|
||||
/cl[oô]ture(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// Normaliser : supprimer les retours à la ligne et les espaces multiples
|
||||
const t = text.replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ");
|
||||
|
||||
const patterns: RegExp[] = [
|
||||
// "date limite (de dépôt|de candidature|d'envoi|de soumission|de remise)? ..."
|
||||
/date\s+limite(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "fin (de dépôt|des candidatures|de soumission|d'inscription|de réception)?"
|
||||
/fin\s+(?:de\s+)?(?:d[eé]p[oô]t|des\s+candidatures|de\s+soumission|d['\u2019]inscription|de\s+r[eé]ception|des\s+dossiers)(?:[^:\d]{0,30})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "clôture (des candidatures|des dossiers|de l'appel|le|au|:)?"
|
||||
/cl[oô]ture(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "clôture le ..."
|
||||
/cl[oô]ture\s+le\s+([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
/cl[oô]ture\s+le\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "avant le ..."
|
||||
/avant\s+le\s+([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
/avant\s+le\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "jusqu'au ..."
|
||||
/jusqu['\u2019]au\s+([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// "dépôt des dossiers : ..."
|
||||
/d[eé]p[oô]t(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// "délai de réponse : ..."
|
||||
/d[eé]lai(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// "réception des dossiers : ..."
|
||||
/r[eé]ception(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
// "soumission (des candidatures)? : ..."
|
||||
/soumission(?:[^:]{0,40})?:\s*([\d]{1,2}[\s\/\-][\w\s.]+\d{4})/i,
|
||||
/jusqu['\u2019]au\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "dépôt (des dossiers|des candidatures|des projets)? ..."
|
||||
/d[eé]p[oô]t(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "délai (de réponse|de dépôt|de soumission)? ..."
|
||||
/d[eé]lai(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "réception (des dossiers|des candidatures)? ..."
|
||||
/r[eé]ception(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "soumission (des candidatures|des projets)? ..."
|
||||
/soumission(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "remise (des offres|des dossiers|des candidatures)? ..."
|
||||
/remise(?:[^:\d]{0,50})?[:\s]+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "candidatures (attendues|acceptées|ouvertes)? jusqu'au ..."
|
||||
/candidatures?(?:[^\d]{0,40})?jusqu['\u2019]au\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "répondre avant le ..."
|
||||
/r[eé]pondre\s+avant\s+le\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
// "ouvert jusqu'au ..."
|
||||
/ouvert(?:e)?\s+jusqu['\u2019]au\s+([\d][\w\s.\/\-]+\d{4})/i,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = text.match(pattern);
|
||||
const match = t.match(pattern);
|
||||
if (match?.[1]) {
|
||||
const d = parseFrDate(match[1].trim());
|
||||
const d = findDateInStr(match[1].trim());
|
||||
if (d && d.getFullYear() >= 2020 && d.getFullYear() <= 2035) return d;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user