- Alignement backend: seules 4 entités retournées (persons, companies, addresses, contractual) - Version API mise à jour à 1.0.1 dans /api/health - Interface onglets d entités: Personnes, Adresses, Entreprises, Contractuel - Correction erreurs TypeScript pour build stricte - Tests et documentation mis à jour - CHANGELOG.md mis à jour avec version 1.1.1
385 lines
12 KiB
JavaScript
385 lines
12 KiB
JavaScript
/**
|
|
* Extraction d'entités métier spécialisées pour les actes notariés
|
|
* Biens immobiliers, clauses contractuelles, signatures, héritiers, etc.
|
|
*/
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
/**
|
|
* Extraction des biens immobiliers
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Array} Liste des biens immobiliers
|
|
*/
|
|
function extractBiensImmobiliers(text) {
|
|
const biens = []
|
|
|
|
// Patterns pour les biens immobiliers
|
|
const patterns = [
|
|
// Maison, appartement, terrain
|
|
/(maison|appartement|terrain|villa|studio|loft|duplex|triplex|pavillon|chalet|château|manoir|hôtel particulier|immeuble|bâtiment|construction|édifice)\s+(?:situé[e]?\s+)?(?:au\s+)?(?:n°\s*)?(\d+[a-z]?)?\s*(?:rue|avenue|boulevard|place|chemin|route|impasse|allée|square|quai|cours|passage)\s+([^,]+)/gi,
|
|
|
|
// Adresse complète
|
|
/(?:situé[e]?\s+)?(?:au\s+)?(?:n°\s*)?(\d+[a-z]?)\s*(?:rue|avenue|boulevard|place|chemin|route|impasse|allée|square|quai|cours|passage)\s+([^,]+),\s*(\d{5})\s+([^,]+)/gi,
|
|
|
|
// Surface et caractéristiques
|
|
/(?:d'une\s+)?(?:surface\s+de\s+)?(\d+(?:\.\d+)?)\s*(?:m²|m2|mètres?\s+carrés?)/gi,
|
|
|
|
// Nombre de pièces
|
|
/(?:composé[e]?\s+de\s+)?(\d+)\s*(?:pièces?|chambres?|salles?)/gi,
|
|
|
|
// Type de bien
|
|
/(?:un\s+)?(maison|appartement|terrain|villa|studio|loft|duplex|triplex|pavillon|chalet|château|manoir|hôtel particulier|immeuble|bâtiment|construction|édifice)/gi
|
|
]
|
|
|
|
// Extraction des adresses
|
|
const adresseMatches = text.match(patterns[1]) || []
|
|
for (const match of adresseMatches) {
|
|
const parts = match.match(/(\d+[a-z]?)\s*(?:rue|avenue|boulevard|place|chemin|route|impasse|allée|square|quai|cours|passage)\s+([^,]+),\s*(\d{5})\s+([^,]+)/i)
|
|
if (parts) {
|
|
biens.push({
|
|
type: 'bien_immobilier',
|
|
adresse: {
|
|
numero: parts[1],
|
|
rue: parts[2].trim(),
|
|
codePostal: parts[3],
|
|
ville: parts[4].trim()
|
|
},
|
|
surface: null,
|
|
pieces: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
}
|
|
|
|
// Extraction des surfaces
|
|
const surfaceMatches = text.match(patterns[2]) || []
|
|
for (const match of surfaceMatches) {
|
|
const surface = parseFloat(match.match(/(\d+(?:\.\d+)?)/)[1])
|
|
if (biens.length > 0) {
|
|
biens[biens.length - 1].surface = surface
|
|
}
|
|
}
|
|
|
|
// Extraction du nombre de pièces
|
|
const piecesMatches = text.match(patterns[3]) || []
|
|
for (const match of piecesMatches) {
|
|
const pieces = parseInt(match.match(/(\d+)/)[1])
|
|
if (biens.length > 0) {
|
|
biens[biens.length - 1].pieces = pieces
|
|
}
|
|
}
|
|
|
|
return biens
|
|
}
|
|
|
|
/**
|
|
* Extraction des clauses contractuelles
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Array} Liste des clauses
|
|
*/
|
|
function extractClauses(text) {
|
|
const clauses = []
|
|
|
|
// Patterns pour les clauses
|
|
const patterns = [
|
|
// Clauses de prix
|
|
/(?:prix|montant|somme)\s+(?:de\s+)?(?:vente\s+)?(?:fixé[e]?\s+à\s+)?(\d+(?:\s+\d+)*(?:\.\d+)?)\s*(?:euros?|€|EUR)/gi,
|
|
|
|
// Clauses suspensives
|
|
/(?:clause\s+)?(?:suspensive|condition)\s+(?:de\s+)?([^.]{10,100})/gi,
|
|
|
|
// Clauses de garantie
|
|
/(?:garantie|garanties?)\s+(?:de\s+)?([^.]{10,100})/gi,
|
|
|
|
// Clauses de délai
|
|
/(?:délai|échéance|terme)\s+(?:de\s+)?(\d+)\s*(?:jours?|mois|années?)/gi,
|
|
|
|
// Clauses de résolution
|
|
/(?:résolution|annulation)\s+(?:du\s+)?(?:contrat|acte)\s+(?:en\s+cas\s+de\s+)?([^.]{10,100})/gi
|
|
]
|
|
|
|
// Extraction des prix
|
|
const prixMatches = text.match(patterns[0]) || []
|
|
for (const match of prixMatches) {
|
|
const prix = match.match(/(\d+(?:\s+\d+)*(?:\.\d+)?)/)[1].replace(/\s+/g, '')
|
|
clauses.push({
|
|
type: 'prix',
|
|
valeur: parseFloat(prix),
|
|
devise: 'EUR',
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des clauses suspensives
|
|
const suspensivesMatches = text.match(patterns[1]) || []
|
|
for (const match of suspensivesMatches) {
|
|
clauses.push({
|
|
type: 'clause_suspensive',
|
|
description: match.trim(),
|
|
condition: match.replace(/(?:clause\s+)?(?:suspensive|condition)\s+(?:de\s+)?/i, '').trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des garanties
|
|
const garantiesMatches = text.match(patterns[2]) || []
|
|
for (const match of garantiesMatches) {
|
|
clauses.push({
|
|
type: 'garantie',
|
|
description: match.trim(),
|
|
garantie: match.replace(/(?:garantie|garanties?)\s+(?:de\s+)?/i, '').trim()
|
|
})
|
|
}
|
|
|
|
return clauses
|
|
}
|
|
|
|
/**
|
|
* Extraction des signatures
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Array} Liste des signatures
|
|
*/
|
|
function extractSignatures(text) {
|
|
const signatures = []
|
|
|
|
// Patterns pour les signatures
|
|
const patterns = [
|
|
// Signature simple
|
|
/(?:signé|signature)\s+(?:par\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)/gi,
|
|
|
|
// Signature avec date
|
|
/(?:signé|signature)\s+(?:par\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:le\s+)?(\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4})/gi,
|
|
|
|
// Signature avec lieu
|
|
/(?:signé|signature)\s+(?:par\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:à\s+)?([A-Z][a-z]+)/gi,
|
|
|
|
// Signature notariale
|
|
/(?:notaire|maître)\s+([A-Z][a-z]+\s+[A-Z][a-z]+)/gi
|
|
]
|
|
|
|
// Extraction des signatures simples
|
|
const signatureMatches = text.match(patterns[0]) || []
|
|
for (const match of signatureMatches) {
|
|
const nom = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)/)[1]
|
|
signatures.push({
|
|
type: 'signature',
|
|
nom: nom,
|
|
date: null,
|
|
lieu: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des signatures avec date
|
|
const signatureDateMatches = text.match(patterns[1]) || []
|
|
for (const match of signatureDateMatches) {
|
|
const parts = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:le\s+)?(\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4})/)
|
|
if (parts) {
|
|
signatures.push({
|
|
type: 'signature',
|
|
nom: parts[1],
|
|
date: parts[2],
|
|
lieu: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
}
|
|
|
|
// Extraction des signatures notariales
|
|
const notaireMatches = text.match(patterns[3]) || []
|
|
for (const match of notaireMatches) {
|
|
const nom = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)/)[1]
|
|
signatures.push({
|
|
type: 'signature_notariale',
|
|
nom: nom,
|
|
date: null,
|
|
lieu: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
return signatures
|
|
}
|
|
|
|
/**
|
|
* Extraction des héritiers
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Array} Liste des héritiers
|
|
*/
|
|
function extractHeritiers(text) {
|
|
const heritiers = []
|
|
|
|
// Patterns pour les héritiers
|
|
const patterns = [
|
|
// Héritier simple
|
|
/(?:héritier|héritière|successeur|successeure|bénéficiaire)\s+(?:de\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)/gi,
|
|
|
|
// Héritier avec degré de parenté
|
|
/(?:fils|fille|père|mère|frère|sœur|époux|épouse|mari|femme|conjoint|conjointe|enfant|parent|grand-père|grand-mère|oncle|tante|neveu|nièce|cousin|cousine)\s+(?:de\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)/gi,
|
|
|
|
// Héritier avec part
|
|
/([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:hérite|bénéficie)\s+(?:de\s+)?(?:la\s+)?(?:part\s+de\s+)?(\d+(?:\/\d+)?|tout|totalité)/gi
|
|
]
|
|
|
|
// Extraction des héritiers simples
|
|
const heritierMatches = text.match(patterns[0]) || []
|
|
for (const match of heritierMatches) {
|
|
const nom = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)/)[1]
|
|
heritiers.push({
|
|
type: 'heritier',
|
|
nom: nom,
|
|
parente: null,
|
|
part: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des héritiers avec parenté
|
|
const parenteMatches = text.match(patterns[1]) || []
|
|
for (const match of parenteMatches) {
|
|
const parts = match.match(/(fils|fille|père|mère|frère|sœur|époux|épouse|mari|femme|conjoint|conjointe|enfant|parent|grand-père|grand-mère|oncle|tante|neveu|nièce|cousin|cousine)\s+(?:de\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)/i)
|
|
if (parts) {
|
|
heritiers.push({
|
|
type: 'heritier',
|
|
nom: parts[2],
|
|
parente: parts[1],
|
|
part: null,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
}
|
|
|
|
return heritiers
|
|
}
|
|
|
|
/**
|
|
* Extraction des vendeurs et acheteurs
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Object} Vendeurs et acheteurs
|
|
*/
|
|
function extractVendeursAcheteurs(text) {
|
|
const result = {
|
|
vendeurs: [],
|
|
acheteurs: []
|
|
}
|
|
|
|
// Patterns pour vendeurs et acheteurs
|
|
const patterns = [
|
|
// Vendeur
|
|
/(?:vendeur|vendeuse|vendant|vendant)\s+(?:M\.|Mme|Mademoiselle|Monsieur|Madame)?\s*([A-Z][a-z]+\s+[A-Z][a-z]+)/gi,
|
|
|
|
// Acheteur
|
|
/(?:acheteur|acheteuse|achetant|achetant)\s+(?:M\.|Mme|Mademoiselle|Monsieur|Madame)?\s*([A-Z][a-z]+\s+[A-Z][a-z]+)/gi,
|
|
|
|
// Vente entre
|
|
/(?:vente\s+)?(?:entre\s+)?(?:M\.|Mme|Mademoiselle|Monsieur|Madame)?\s*([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:et\s+)?(?:M\.|Mme|Mademoiselle|Monsieur|Madame)?\s*([A-Z][a-z]+\s+[A-Z][a-z]+)/gi
|
|
]
|
|
|
|
// Extraction des vendeurs
|
|
const vendeurMatches = text.match(patterns[0]) || []
|
|
for (const match of vendeurMatches) {
|
|
const nom = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)/)[1]
|
|
result.vendeurs.push({
|
|
type: 'vendeur',
|
|
nom: nom,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des acheteurs
|
|
const acheteurMatches = text.match(patterns[1]) || []
|
|
for (const match of acheteurMatches) {
|
|
const nom = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)/)[1]
|
|
result.acheteurs.push({
|
|
type: 'acheteur',
|
|
nom: nom,
|
|
description: match.trim()
|
|
})
|
|
}
|
|
|
|
// Extraction des ventes entre
|
|
const venteMatches = text.match(patterns[2]) || []
|
|
for (const match of venteMatches) {
|
|
const parts = match.match(/([A-Z][a-z]+\s+[A-Z][a-z]+)\s+(?:et\s+)?([A-Z][a-z]+\s+[A-Z][a-z]+)/)
|
|
if (parts) {
|
|
result.vendeurs.push({
|
|
type: 'vendeur',
|
|
nom: parts[1],
|
|
description: match.trim()
|
|
})
|
|
result.acheteurs.push({
|
|
type: 'acheteur',
|
|
nom: parts[2],
|
|
description: match.trim()
|
|
})
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Classification du type de document
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {string} Type de document
|
|
*/
|
|
function classifyDocumentType(text) {
|
|
const textLower = text.toLowerCase()
|
|
|
|
// Types de documents notariés
|
|
const types = {
|
|
'acte_vente': ['vente', 'achat', 'acquisition', 'cession', 'transfert'],
|
|
'acte_succession': ['succession', 'héritage', 'héritier', 'défunt', 'décès'],
|
|
'acte_donation': ['donation', 'donner', 'donné', 'donateur', 'donataire'],
|
|
'acte_mariage': ['mariage', 'époux', 'épouse', 'conjoint', 'conjointe'],
|
|
'acte_divorce': ['divorce', 'séparation', 'liquidation'],
|
|
'acte_pacs': ['pacs', 'pacte civil', 'solidarité'],
|
|
'acte_testament': ['testament', 'testateur', 'testatrice', 'legs', 'léguer'],
|
|
'acte_promesse': ['promesse', 'compromis', 'avant-contrat'],
|
|
'acte_authentification': ['authentification', 'authentifier', 'certifier'],
|
|
'acte_pouvoir': ['pouvoir', 'procuration', 'mandat'],
|
|
'acte_societe': ['société', 'entreprise', 'sarl', 'sas', 'eurl', 'snc']
|
|
}
|
|
|
|
// Calcul des scores
|
|
const scores = {}
|
|
for (const [type, keywords] of Object.entries(types)) {
|
|
scores[type] = keywords.reduce((score, keyword) => {
|
|
const matches = (textLower.match(new RegExp(keyword, 'g')) || []).length
|
|
return score + matches
|
|
}, 0)
|
|
}
|
|
|
|
// Retourner le type avec le score le plus élevé
|
|
const bestType = Object.entries(scores).reduce((a, b) => scores[a[0]] > scores[b[0]] ? a : b)
|
|
|
|
return bestType[1] > 0 ? bestType[0] : 'document_inconnu'
|
|
}
|
|
|
|
/**
|
|
* Extraction complète des entités métier
|
|
* @param {string} text - Texte à analyser
|
|
* @returns {Object} Toutes les entités extraites
|
|
*/
|
|
function extractMetierEntities(text) {
|
|
return {
|
|
biensImmobiliers: extractBiensImmobiliers(text),
|
|
clauses: extractClauses(text),
|
|
signatures: extractSignatures(text),
|
|
heritiers: extractHeritiers(text),
|
|
vendeursAcheteurs: extractVendeursAcheteurs(text),
|
|
documentType: classifyDocumentType(text),
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
extractBiensImmobiliers,
|
|
extractClauses,
|
|
extractSignatures,
|
|
extractHeritiers,
|
|
extractVendeursAcheteurs,
|
|
classifyDocumentType,
|
|
extractMetierEntities
|
|
}
|