feat: Traitement automatique des fichiers non traités

- Modification de listFolderResults pour détecter les fichiers non traités
- Mise en pending automatique des fichiers sans résultats d'extraction
- Fonction processDocument pour traiter les fichiers en arrière-plan
- Fonction processFileInBackground pour le traitement asynchrone
- Fonction removePendingFlag pour nettoyer les flags après traitement
- Les fichiers non traités sont maintenant automatiquement traités

Fixes: Fichiers non traités mis en pending et traités automatiquement
Fixes: Traitement en arrière-plan des fichiers uploadés sans extraction
This commit is contained in:
Nicolas Cantu 2025-09-16 06:07:45 +02:00
parent 736637c5cd
commit 328d2584de

View File

@ -267,81 +267,34 @@ function listFolderResults(folderHash) {
// Vérifier si ce fichier n'a pas déjà un résultat en cache
const hasCacheResult = results.some(result => result.fileHash === fileHash)
// Vérifier si ce fichier n'est pas déjà en pending
const isAlreadyPending = pending.some(p => p.fileHash === fileHash)
if (!hasCacheResult) {
// Créer un résultat minimal pour les fichiers non traités
if (!hasCacheResult && !isAlreadyPending) {
// Mettre le fichier en pending et le traiter automatiquement
const filePath = path.join(uploadsPath, file)
const stats = fs.statSync(filePath)
results.push({
fileHash,
document: {
id: `doc-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
fileName: file,
mimeType: getMimeTypeFromExtension(path.extname(file)),
fileSize: stats.size,
uploadTimestamp: stats.mtime.toISOString()
},
classification: {
documentType: 'Document',
confidence: 0.0,
subType: 'Non traité',
language: 'fr',
pageCount: 1
},
extraction: {
text: {
raw: '',
processed: '',
wordCount: 0,
characterCount: 0,
confidence: 0.0
},
entities: {
persons: [],
companies: [],
addresses: [],
financial: {
amounts: [],
totals: {},
payment: {}
},
dates: [],
contractual: {
clauses: [],
signatures: []
},
references: []
}
},
metadata: {
processing: {
engine: '4NK_IA_Backend',
version: '1.0.0',
processingTime: '0ms',
ocrEngine: 'none',
nerEngine: 'none',
preprocessing: {
applied: false,
reason: 'Fichier non traité'
}
},
quality: {
globalConfidence: 0.0,
textExtractionConfidence: 0.0,
entityExtractionConfidence: 0.0,
classificationConfidence: 0.0
}
},
status: {
success: false,
errors: ['Fichier non traité'],
warnings: ['Aucune extraction effectuée'],
timestamp: stats.mtime.toISOString()
}
})
console.log(`[FOLDER] Fichier non traité détecté, mise en pending: ${file}`)
console.log(`[FOLDER] Fichier non traité ajouté: ${file}`)
// Créer le flag pending
const pendingData = {
fileHash,
fileName: file,
folderHash,
timestamp: new Date().toISOString(),
status: 'processing'
}
// Sauvegarder le flag pending
createPendingFlag(folderHash, fileHash, pendingData)
// Ajouter à la liste des pending
pending.push(pendingData)
hasPending = true
// Traiter le fichier en arrière-plan
processFileInBackground(filePath, fileHash, folderHash)
}
}
}
@ -349,6 +302,103 @@ function listFolderResults(folderHash) {
return { results, pending, hasPending }
}
// Fonction pour traiter un document (extraction de la logique de /api/extract)
async function processDocument(filePath, fileHash) {
const startTime = Date.now()
try {
console.log(`[PROCESS] Début du traitement: ${filePath}`)
// Obtenir les informations du fichier
const stats = fs.statSync(filePath)
const ext = path.extname(filePath)
const mimeType = getMimeTypeFromExtension(ext)
// Créer un objet file similaire à celui de multer
const file = {
path: filePath,
originalname: path.basename(filePath),
mimetype: mimeType,
size: stats.size
}
let ocrResult
let result
// Si c'est un PDF, extraire le texte directement
if (mimeType === 'application/pdf') {
console.log(`[PROCESS] Extraction de texte depuis PDF...`)
try {
ocrResult = await extractTextFromPdf(filePath)
console.log(`[PROCESS] Texte extrait du PDF: ${ocrResult.text.length} caractères`)
} catch (error) {
console.error(`[PROCESS] Erreur lors de l'extraction PDF:`, error.message)
throw new Error(`Erreur lors de l'extraction PDF: ${error.message}`)
}
} else {
// Pour les images, utiliser l'OCR avec préprocessing
ocrResult = await extractTextFromImage(filePath)
}
// Extraction NER
const entities = extractEntitiesFromText(ocrResult.text)
// Mesure du temps de traitement
const processingTime = Date.now() - startTime
// Génération du format JSON standard
result = generateStandardJSON(file, ocrResult, entities, processingTime)
console.log(`[PROCESS] Traitement terminé en ${processingTime}ms`)
return result
} catch (error) {
console.error(`[PROCESS] Erreur lors du traitement:`, error)
throw error
}
}
// Fonction pour traiter un fichier en arrière-plan
async function processFileInBackground(filePath, fileHash, folderHash) {
try {
console.log(`[BACKGROUND] Début du traitement en arrière-plan: ${filePath}`)
// Traiter le document
const result = await processDocument(filePath, fileHash)
// Sauvegarder le résultat dans le cache du dossier
const success = saveJsonCacheInFolder(folderHash, fileHash, result)
if (success) {
// Supprimer le flag pending
removePendingFlag(folderHash, fileHash)
console.log(`[BACKGROUND] Traitement terminé avec succès: ${fileHash}`)
} else {
console.error(`[BACKGROUND] Erreur lors de la sauvegarde du résultat: ${fileHash}`)
}
} catch (error) {
console.error(`[BACKGROUND] Erreur lors du traitement en arrière-plan:`, error)
// Supprimer le flag pending même en cas d'erreur
removePendingFlag(folderHash, fileHash)
}
}
// Fonction pour supprimer un flag pending
function removePendingFlag(folderHash, fileHash) {
try {
const cachePath = path.join('cache', folderHash)
const pendingFile = path.join(cachePath, `${fileHash}.pending`)
if (fs.existsSync(pendingFile)) {
fs.unlinkSync(pendingFile)
console.log(`[PENDING] Flag pending supprimé: ${fileHash}`)
}
} catch (error) {
console.error(`[PENDING] Erreur lors de la suppression du flag pending:`, error)
}
}
// Fonction pour vérifier si un fichier existe déjà par hash dans un dossier
function findExistingFileByHash(hash, folderHash) {
const folderPath = path.join('uploads', folderHash)
@ -1138,8 +1188,8 @@ app.post('/api/extract', upload.single('document'), async (req, res) => {
ocrResult = await extractTextFromImage(req.file.path)
}
// Extraction NER
const entities = extractEntitiesFromText(ocrResult.text)
// Extraction NER
const entities = extractEntitiesFromText(ocrResult.text)
// Mesure du temps de traitement
const processingTime = Date.now() - startTime
@ -1169,7 +1219,7 @@ app.post('/api/extract', upload.single('document'), async (req, res) => {
details: error.message
})
} finally {
// Nettoyage du fichier temporaire
// Nettoyage du fichier temporaire
if (isDuplicate) {
// Supprimer le doublon uploadé
fs.unlinkSync(duplicatePath)