new full
This commit is contained in:
126
app.py
126
app.py
@@ -28,6 +28,7 @@ try:
|
||||
config = config_loader.load_config("main.yaml")
|
||||
logger.info("✅ Configuration modulaire chargée avec succès")
|
||||
|
||||
# Normalisation douce de l'allow_list (préserve la structure des mots)
|
||||
allow_list_terms = set(term.lower().strip() for term in config.get('allow_list', []))
|
||||
logger.info(f"✅ Allow list chargée avec {len(allow_list_terms)} termes")
|
||||
|
||||
@@ -69,9 +70,114 @@ except Exception as e:
|
||||
|
||||
|
||||
def normalize_label(text: str) -> str:
|
||||
# Règles générales de normalisation pour gérer tous les cas
|
||||
text = text.strip().lower()
|
||||
|
||||
# 1. Supprimer parenthèses et leur contenu
|
||||
text = re.sub(r'\([^)]*\)', '', text)
|
||||
|
||||
# 2. Supprimer virgules et points suivis d'un espace
|
||||
text = re.sub(r'[,.] ', ' ', text)
|
||||
|
||||
# 3. Supprimer points collés (ex: "Dr.Marie" -> "Dr Marie")
|
||||
text = re.sub(r'\.(\w)', r' \1', text)
|
||||
|
||||
# 4. Supprimer tirets collés aux espaces SEULEMENT (garder les tirets dans les mots composés)
|
||||
text = re.sub(r'- ', ' ', text) # "expert- comptable" -> "expert comptable"
|
||||
text = re.sub(r' -', ' ', text) # "expert -comptable" -> "expert comptable"
|
||||
|
||||
# 5. Supprimer deux-points et ce qui suit (ex: "n° IEC: 567890" -> "n° IEC")
|
||||
text = re.sub(r':.*$', '', text)
|
||||
|
||||
# 6. Normaliser les espaces multiples
|
||||
text = re.sub(r'\s+', ' ', text)
|
||||
|
||||
# 7. Normalisation finale : garder lettres, chiffres, espaces ET tirets pour mots composés
|
||||
cleaned = re.sub(r'[^\w\s-]', '', text)
|
||||
|
||||
# 8. Nettoyer les espaces en début/fin
|
||||
return cleaned.strip()
|
||||
|
||||
cleaned = re.sub(r'[^\w\s]', '', text.strip().lower())
|
||||
return cleaned
|
||||
|
||||
def filter_by_category(results, mode):
|
||||
"""Filtre les résultats selon la catégorie sélectionnée"""
|
||||
if mode == "pii_business":
|
||||
return results # Tout
|
||||
|
||||
# Définir les entités PII (Données personnelles)
|
||||
pii_entities = {
|
||||
# Données personnelles de base
|
||||
'PERSONNE', 'PERSON', 'DATE', 'DATE_TIME',
|
||||
'EMAIL_ADDRESS', 'ADRESSE_EMAIL', 'PHONE_NUMBER', 'TELEPHONE',
|
||||
'CREDIT_CARD', 'IBAN', 'ADRESSE_IP',
|
||||
|
||||
# Adresses personnelles
|
||||
'ADRESSE', 'ADRESSE_FRANCAISE', 'ADRESSE_BELGE', 'LOCATION',
|
||||
|
||||
# Téléphones personnels
|
||||
'TELEPHONE_FRANCAIS', 'TELEPHONE_BELGE',
|
||||
|
||||
# Documents d'identité personnels
|
||||
'NUMERO_SECURITE_SOCIALE_FRANCAIS', 'REGISTRE_NATIONAL_BELGE',
|
||||
'CARTE_IDENTITE_FRANCAISE', 'CARTE_IDENTITE_BELGE',
|
||||
'PASSEPORT_FRANCAIS', 'PASSEPORT_BELGE',
|
||||
'PERMIS_CONDUIRE_FRANCAIS',
|
||||
|
||||
# Données financières personnelles
|
||||
'COMPTE_BANCAIRE_FRANCAIS',
|
||||
|
||||
# Données sensibles RGPD
|
||||
'HEALTH_DATA', 'DONNEES_SANTE',
|
||||
'SEXUAL_ORIENTATION', 'ORIENTATION_SEXUELLE',
|
||||
'POLITICAL_OPINIONS', 'OPINIONS_POLITIQUES',
|
||||
'BIOMETRIC_DATA', 'DONNEES_BIOMETRIQUES',
|
||||
'RGPD_FINANCIAL_DATA', 'DONNEES_FINANCIERES_RGPD',
|
||||
|
||||
# Identifiants personnels
|
||||
'IDENTIFIANT_PERSONNEL'
|
||||
}
|
||||
|
||||
# Définir les entités Business (Données d'entreprise)
|
||||
business_entities = {
|
||||
# Organisations et sociétés
|
||||
'ORGANISATION', 'ORGANIZATION',
|
||||
'SOCIETE_FRANCAISE', 'SOCIETE_BELGE',
|
||||
|
||||
# Identifiants fiscaux et d'entreprise
|
||||
'TVA_FRANCAISE', 'TVA_BELGE',
|
||||
'NUMERO_FISCAL_FRANCAIS', 'SIRET_SIREN_FRANCAIS',
|
||||
'NUMERO_ENTREPRISE_BELGE',
|
||||
|
||||
# Identifiants professionnels
|
||||
'ID_PROFESSIONNEL_BELGE',
|
||||
|
||||
# Données commerciales
|
||||
'MARKET_SHARE', 'SECRET_COMMERCIAL',
|
||||
'REFERENCE_CONTRAT', 'MONTANT_FINANCIER',
|
||||
|
||||
# Données techniques d'entreprise
|
||||
'CLE_API_SECRETE'
|
||||
}
|
||||
|
||||
# Définir les entités mixtes (PII + Business)
|
||||
mixed_entities = {
|
||||
# Données pouvant être personnelles ou professionnelles
|
||||
'TITRE_CIVILITE', 'DONNEES_PROFESSIONNELLES',
|
||||
'LOCALISATION_GPS', 'URL_IDENTIFIANT'
|
||||
}
|
||||
|
||||
if mode == "pii":
|
||||
# Inclure PII + mixtes
|
||||
allowed_entities = pii_entities | mixed_entities
|
||||
return [r for r in results if r.entity_type in allowed_entities]
|
||||
|
||||
elif mode == "business":
|
||||
# Inclure Business + mixtes
|
||||
allowed_entities = business_entities | mixed_entities
|
||||
return [r for r in results if r.entity_type in allowed_entities]
|
||||
|
||||
# Par défaut, retourner tous les résultats
|
||||
return results
|
||||
|
||||
|
||||
# Remplacer ligne 18
|
||||
@@ -87,6 +193,7 @@ def analyze_text():
|
||||
data = request.get_json(force=True)
|
||||
text_to_analyze = data.get("text", "")
|
||||
language = data.get("language", "fr")
|
||||
mode = data.get("mode", "pii_business") # Nouveau paramètre
|
||||
|
||||
if not text_to_analyze:
|
||||
return jsonify({"error": "text field is missing or empty"}), 400
|
||||
@@ -94,8 +201,11 @@ def analyze_text():
|
||||
# Analyse brute
|
||||
raw_results = analyzer.analyze(text=text_to_analyze, language=language)
|
||||
|
||||
# Filtrer selon la catégorie
|
||||
filtered_results = filter_by_category(raw_results, mode)
|
||||
|
||||
# Pipeline modulaire complet
|
||||
final_results = pipeline.process(text_to_analyze, raw_results, allow_list_terms)
|
||||
final_results = pipeline.process(text_to_analyze, filtered_results, allow_list_terms)
|
||||
|
||||
response_data = [res.to_dict() for res in final_results]
|
||||
return make_response(jsonify(response_data), 200)
|
||||
@@ -216,12 +326,12 @@ def anonymize_text():
|
||||
logger.info(f"🔍 Traitement entité: {res.entity_type} = '{ent_text}' (score: {res.score})")
|
||||
logger.info(f"🔍 Allow list terms: {allow_list_terms}")
|
||||
|
||||
# Vérification améliorée de la allow list
|
||||
ent_text_clean = re.sub(r'[^\w]', '', ent_text.strip().lower())
|
||||
logger.info(f"🔍 Texte nettoyé: '{ent_text_clean}'")
|
||||
# Normalisation douce du texte de l'entité (cohérente avec l'allow_list)
|
||||
ent_text_normalized = ent_text.lower().strip()
|
||||
logger.info(f"🔍 Texte normalisé: '{ent_text_normalized}'")
|
||||
|
||||
# Vérifier si le texte correspond exactement ou commence par un terme de la allow list
|
||||
is_allowed = any(ent_text_clean == term or ent_text_clean.startswith(term) for term in allow_list_terms)
|
||||
# Vérifier si l'entité est dans l'allow-list (correspondance exacte)
|
||||
is_allowed = ent_text_normalized in allow_list_terms
|
||||
|
||||
if is_allowed:
|
||||
logger.info(f"✅ Entité '{ent_text}' ignorée (dans allow list)")
|
||||
|
||||
Reference in New Issue
Block a user