From 74e56c956c0e44a451360b82ec10bfe35c77c89d Mon Sep 17 00:00:00 2001 From: nBiqoz Date: Sat, 9 Aug 2025 15:23:20 +0200 Subject: [PATCH] glisser --- app/components/AnonymizationLogic.tsx | 104 +++++++++++++------- app/components/FileUploadComponent.tsx | 128 +++++++++++++++++++++++-- app/utils/highlightEntities.tsx | 61 ++++-------- 3 files changed, 209 insertions(+), 84 deletions(-) diff --git a/app/components/AnonymizationLogic.tsx b/app/components/AnonymizationLogic.tsx index d4b61c5..e51692e 100644 --- a/app/components/AnonymizationLogic.tsx +++ b/app/components/AnonymizationLogic.tsx @@ -117,41 +117,79 @@ export const useAnonymization = ({ } }); - const seen = new Set(); + // 2. Créer un mapping basé sur l'ordre d'apparition dans le texte anonymisé const uniqueMappings: EntityMapping[] = []; - const addedCounts = new Map(); - - // 2. N'ajouter que les entités réellement anonymisées avec un compteur - data.analyzerResults - .sort((a, b) => a.start - b.start) // Trier par ordre d'apparition - .forEach((result) => { - const entityType = result.entity_type; - const maxCount = tagCounts.get(entityType) || 0; - const currentCount = addedCounts.get(entityType) || 0; - - if (currentCount < maxCount) { - const originalValue = textToProcess.substring( - result.start, - result.end - ); - const frenchLabel = entityTypeMap.get(entityType) || entityType; - const uniqueKey = `${frenchLabel}|${originalValue}`; - - if (!seen.has(uniqueKey)) { - const newCount = (addedCounts.get(entityType) || 0) + 1; - addedCounts.set(entityType, newCount); - - uniqueMappings.push({ - entityType: frenchLabel, - originalValue: originalValue, - anonymizedValue: `${frenchLabel} [${newCount}]`, - startIndex: result.start, - endIndex: result.end, - }); - seen.add(uniqueKey); - } + + // Vérifier que les données nécessaires sont disponibles + if (!data.analyzerResults || !data.anonymizedText) { + setEntityMappings([]); + return; + } + + const entityCounters = new Map(); + + // Parcourir le texte anonymisé pour trouver les tags dans l'ordre + const anonymizedText = data.anonymizedText; + const allMatches: Array<{ + match: RegExpMatchArray; + entityType: string; + position: number; + }> = []; + + // Trouver tous les tags dans le texte anonymisé + patterns.forEach(pattern => { + const entityTypeKey = pattern.regex.toString().match(/<([A-Z_]+)>/)?.[1]; + if (entityTypeKey) { + const regex = new RegExp(pattern.regex.source, 'g'); + let match; + while ((match = regex.exec(anonymizedText)) !== null) { + allMatches.push({ + match, + entityType: entityTypeKey, + position: match.index + }); } - }); + } + }); + + // Trier par position dans le texte anonymisé + allMatches.sort((a, b) => a.position - b.position); + + // Créer les mappings dans l'ordre d'apparition + const seen = new Set(); + allMatches.forEach(({ entityType }) => { + const frenchLabel = entityTypeMap.get(entityType) || entityType; + const currentCount = (entityCounters.get(entityType) || 0) + 1; + entityCounters.set(entityType, currentCount); + + // Trouver l'entité correspondante dans les résultats d'analyse + const correspondingResult = data.analyzerResults + ?.filter(result => result.entity_type === entityType) + .find(result => { + const originalValue = textToProcess.substring(result.start, result.end); + const uniqueKey = `${frenchLabel}|${originalValue}|${currentCount}`; + return !seen.has(uniqueKey); + }); + + if (correspondingResult) { + const originalValue = textToProcess.substring( + correspondingResult.start, + correspondingResult.end + ); + const uniqueKey = `${frenchLabel}|${originalValue}|${currentCount}`; + + if (!seen.has(uniqueKey)) { + uniqueMappings.push({ + entityType: frenchLabel, + originalValue: originalValue, + anonymizedValue: `${frenchLabel} [${currentCount}]`, + startIndex: correspondingResult.start, + endIndex: correspondingResult.end, + }); + seen.add(uniqueKey); + } + } + }); setEntityMappings(uniqueMappings); } else if (data.text) { diff --git a/app/components/FileUploadComponent.tsx b/app/components/FileUploadComponent.tsx index 524534c..9e37caa 100644 --- a/app/components/FileUploadComponent.tsx +++ b/app/components/FileUploadComponent.tsx @@ -10,6 +10,7 @@ import { SampleTextComponent } from "./SampleTextComponent"; import { SupportedDataTypes } from "./SupportedDataTypes"; import { AnonymizationInterface } from "./AnonymizationInterface"; import { highlightEntities } from "../utils/highlightEntities"; +import { useState } from "react"; interface EntityMapping { originalValue: string; @@ -57,6 +58,86 @@ export const FileUploadComponent = ({ setIsExampleLoaded, entityMappings, // Ajouter cette prop ici }: FileUploadComponentProps) => { + const [isDragOver, setIsDragOver] = useState(false); + + // Fonction pour valider le type de fichier + const isValidFileType = (file: File) => { + const allowedTypes = ["text/plain", "application/pdf"]; + const allowedExtensions = [".txt", ".pdf"]; + + return ( + allowedTypes.includes(file.type) || + allowedExtensions.some((ext) => file.name.toLowerCase().endsWith(ext)) + ); + }; + + // Gestionnaires de glisser-déposer + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const handleDragEnter = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(true); + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + // Vérifier si on quitte vraiment la zone de drop + if (!e.currentTarget.contains(e.relatedTarget as Node)) { + setIsDragOver(false); + } + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(false); + + const files = Array.from(e.dataTransfer.files); + if (files.length > 0) { + const file = files[0]; + + // Vérifier le type de fichier + if (!isValidFileType(file)) { + alert( + "Type de fichier non supporté. Veuillez sélectionner un fichier PDF ou TXT." + ); + return; + } + + // Vérifier la taille (5MB max) + if (file.size > 5 * 1024 * 1024) { + alert("Le fichier est trop volumineux. Taille maximale : 5MB."); + return; + } + + const syntheticEvent = { + target: { files: [file] }, + } as unknown as React.ChangeEvent; + + handleFileChange(syntheticEvent); + } + }; + + // Gestionnaire de changement de fichier modifié pour valider le type + const handleFileChangeWithValidation = ( + e: React.ChangeEvent + ) => { + const file = e.target.files?.[0]; + if (file && !isValidFileType(file)) { + alert( + "Type de fichier non supporté. Veuillez sélectionner un fichier PDF ou TXT." + ); + e.target.value = ""; // Reset l'input + return; + } + handleFileChange(e); + }; + // On passe en preview seulement si : // 1. Un fichier est uploadé OU // 2. On a un résultat d'anonymisation @@ -144,7 +225,7 @@ export const FileUploadComponent = ({
{highlightEntities( outputText || "Aucun contenu à afficher", - entityMappings // Ajouter les mappings ici + entityMappings )}
@@ -410,30 +491,59 @@ export const FileUploadComponent = ({ {/* Colonne droite - Zone upload */} -
+