version nice
This commit is contained in:
178
app/components/AnonymizationLogic.tsx
Normal file
178
app/components/AnonymizationLogic.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import { useState } from "react";
|
||||
|
||||
interface EntityMapping {
|
||||
originalValue: string;
|
||||
anonymizedValue: string;
|
||||
entityType: string;
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
}
|
||||
|
||||
interface AnonymizationLogicProps {
|
||||
sourceText: string;
|
||||
fileContent: string;
|
||||
uploadedFile: File | null;
|
||||
setOutputText: (text: string) => void;
|
||||
setError: (error: string | null) => void;
|
||||
setEntityMappings: (mappings: EntityMapping[]) => void;
|
||||
}
|
||||
|
||||
export const useAnonymization = ({
|
||||
sourceText,
|
||||
fileContent,
|
||||
uploadedFile,
|
||||
setOutputText,
|
||||
setError,
|
||||
setEntityMappings,
|
||||
}: AnonymizationLogicProps) => {
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
const anonymizeData = async () => {
|
||||
const textToProcess = sourceText || fileContent || "";
|
||||
|
||||
if (!textToProcess.trim()) {
|
||||
setError(
|
||||
"Veuillez saisir du texte à anonymiser ou télécharger un fichier"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsProcessing(true);
|
||||
setError(null);
|
||||
setOutputText("");
|
||||
setEntityMappings([]);
|
||||
|
||||
try {
|
||||
if (
|
||||
uploadedFile &&
|
||||
uploadedFile.type === "application/pdf" &&
|
||||
!fileContent
|
||||
) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", uploadedFile);
|
||||
|
||||
const response = await fetch("/api/process-document", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Erreur lors du traitement du PDF");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
if (data.anonymizedText) {
|
||||
setOutputText(data.anonymizedText);
|
||||
// TODO: Extraire les mappings depuis les résultats Presidio
|
||||
setIsProcessing(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
// Simulation des mappings pour le fallback
|
||||
const mappings: EntityMapping[] = [];
|
||||
let anonymized = textToProcess;
|
||||
|
||||
// Noms
|
||||
const nameMatches = textToProcess.matchAll(/\b[A-Z][a-z]+ [A-Z][a-z]+\b/g);
|
||||
let nameCounter = 1;
|
||||
for (const match of nameMatches) {
|
||||
const replacement = `[Nom${nameCounter}]`;
|
||||
mappings.push({
|
||||
originalValue: match[0],
|
||||
anonymizedValue: replacement,
|
||||
entityType: "PERSON",
|
||||
startIndex: match.index!,
|
||||
endIndex: match.index! + match[0].length
|
||||
});
|
||||
anonymized = anonymized.replace(match[0], replacement);
|
||||
nameCounter++;
|
||||
}
|
||||
|
||||
// Téléphones
|
||||
const phoneMatches = textToProcess.matchAll(/\b0[1-9](?:[\s.-]?\d{2}){4}\b/g);
|
||||
let phoneCounter = 1;
|
||||
for (const match of phoneMatches) {
|
||||
const replacement = `[Téléphone${phoneCounter}]`;
|
||||
mappings.push({
|
||||
originalValue: match[0],
|
||||
anonymizedValue: replacement,
|
||||
entityType: "PHONE_NUMBER",
|
||||
startIndex: match.index!,
|
||||
endIndex: match.index! + match[0].length
|
||||
});
|
||||
anonymized = anonymized.replace(match[0], replacement);
|
||||
phoneCounter++;
|
||||
}
|
||||
|
||||
// Emails
|
||||
const emailMatches = textToProcess.matchAll(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g);
|
||||
let emailCounter = 1;
|
||||
for (const match of emailMatches) {
|
||||
const replacement = `[Email${emailCounter}]`;
|
||||
mappings.push({
|
||||
originalValue: match[0],
|
||||
anonymizedValue: replacement,
|
||||
entityType: "EMAIL_ADDRESS",
|
||||
startIndex: match.index!,
|
||||
endIndex: match.index! + match[0].length
|
||||
});
|
||||
anonymized = anonymized.replace(match[0], replacement);
|
||||
emailCounter++;
|
||||
}
|
||||
|
||||
// Adresses
|
||||
const addressMatches = textToProcess.matchAll(/\b\d{1,3}\s+[a-zA-Z\s]+,\s*\d{5}\s+[a-zA-Z\s]+\b/g);
|
||||
let addressCounter = 1;
|
||||
for (const match of addressMatches) {
|
||||
const replacement = `[Adresse${addressCounter}]`;
|
||||
mappings.push({
|
||||
originalValue: match[0],
|
||||
anonymizedValue: replacement,
|
||||
entityType: "LOCATION",
|
||||
startIndex: match.index!,
|
||||
endIndex: match.index! + match[0].length
|
||||
});
|
||||
anonymized = anonymized.replace(match[0], replacement);
|
||||
addressCounter++;
|
||||
}
|
||||
|
||||
// Numéros de sécurité sociale
|
||||
const ssnMatches = textToProcess.matchAll(/\b\d\s\d{2}\s\d{2}\s\d{2}\s\d{3}\s\d{3}\s\d{2}\b/g);
|
||||
let ssnCounter = 1;
|
||||
for (const match of ssnMatches) {
|
||||
const replacement = `[NuméroSS${ssnCounter}]`;
|
||||
mappings.push({
|
||||
originalValue: match[0],
|
||||
anonymizedValue: replacement,
|
||||
entityType: "FR_NIR",
|
||||
startIndex: match.index!,
|
||||
endIndex: match.index! + match[0].length
|
||||
});
|
||||
anonymized = anonymized.replace(match[0], replacement);
|
||||
ssnCounter++;
|
||||
}
|
||||
|
||||
setOutputText(anonymized);
|
||||
setEntityMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("Erreur anonymisation:", error);
|
||||
setError(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Erreur lors de l'anonymisation"
|
||||
);
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { anonymizeData, isProcessing };
|
||||
};
|
||||
Reference in New Issue
Block a user