diff --git a/backend/server.js b/backend/server.js index 47a102b..9dc1101 100644 --- a/backend/server.js +++ b/backend/server.js @@ -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)