Spaces:
Running
Running
# ========================================================= | |
# CELLA 1: IMPORT E SETUP INIZIALE | |
# ========================================================= | |
import os | |
import re | |
import gradio as gr | |
import pandas as pd | |
import json | |
from typing import List, Dict, Tuple, Any | |
import spacy | |
import torch | |
from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline | |
# Import Presidio | |
from presidio_analyzer import AnalyzerEngine, RecognizerRegistry, PatternRecognizer | |
from presidio_analyzer.pattern_recognizer import Pattern | |
from presidio_analyzer.nlp_engine import NlpEngine, NlpEngineProvider | |
from presidio_analyzer.context_aware_enhancers import LemmaContextAwareEnhancer | |
from presidio_anonymizer import AnonymizerEngine | |
from presidio_anonymizer.entities import OperatorConfig | |
# Configurazione base | |
print("✅ Import completati!") | |
# ========================================================= | |
# CELLA 2: CONFIGURAZIONE RICONOSCITORI PERSONALIZZATI (CORRETTA) | |
# ========================================================= | |
def create_italian_recognizers(): | |
""" | |
Crea riconoscitori personalizzati per il contesto italiano | |
""" | |
recognizers = [] | |
# CODICE FISCALE | |
cf_patterns = [Pattern(name="codice fiscale", | |
regex=r"\b[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]\b", | |
score=0.9)] | |
cf_recognizer = PatternRecognizer( | |
supported_entity="CODICE_FISCALE", | |
patterns=cf_patterns, | |
context=["codice", "fiscale", "cf", "c.f.", "cod.fisc.", "codice fiscale"], | |
supported_language="en" # Aggiungiamo il supporto per l'inglese | |
) | |
recognizers.append(cf_recognizer) | |
# PARTITA IVA | |
piva_patterns = [Pattern(name="partita iva", | |
regex=r"\b(IT)?[0-9]{11}\b", | |
score=0.85)] | |
piva_recognizer = PatternRecognizer( | |
supported_entity="PARTITA_IVA", | |
patterns=piva_patterns, | |
context=["partita", "iva", "p.iva", "p. iva", "piva", "partita iva"], | |
supported_language="en" | |
) | |
recognizers.append(piva_recognizer) | |
# IBAN ITALIANO | |
iban_patterns = [Pattern(name="iban", | |
regex=r"\b[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}\b", | |
score=0.9)] | |
iban_recognizer = PatternRecognizer( | |
supported_entity="IBAN_CODE", | |
patterns=iban_patterns, | |
context=["iban", "bonifico", "bancario", "conto", "pagamento", "IBAN"], | |
supported_language="en" | |
) | |
recognizers.append(iban_recognizer) | |
# TARGA ITALIANA | |
targa_patterns = [Pattern(name="targa", | |
regex=r"\b[A-Z]{2}[0-9]{3}[A-Z]{2}\b", | |
score=0.85)] | |
targa_recognizer = PatternRecognizer( | |
supported_entity="TARGA", | |
patterns=targa_patterns, | |
context=["targa", "auto", "veicolo", "automobile", "macchina"], | |
supported_language="en" | |
) | |
recognizers.append(targa_recognizer) | |
# TELEFONO ITALIANO | |
telefono_patterns = [ | |
Pattern(name="telefono (con prefisso)", regex=r"\b\+39\s?[0-9]{10}\b", score=0.9), | |
Pattern(name="telefono (cellulare)", regex=r"\b[3][0-9]{9}\b", score=0.8), | |
Pattern(name="telefono (fisso)", regex=r"\b0[0-9]{1,3}[-\s]?[0-9]{7}\b", score=0.7), | |
Pattern(name="telefono (generico)", regex=r"\b[0-9]{10}\b", score=0.6) | |
] | |
telefono_recognizer = PatternRecognizer( | |
supported_entity="PHONE_NUMBER", | |
patterns=telefono_patterns, | |
context=["telefono", "cellulare", "tel", "chiamare", "contattare", "mobile"], | |
supported_language="en" | |
) | |
recognizers.append(telefono_recognizer) | |
# DATA ITALIANA | |
data_patterns = [ | |
Pattern(name="data (dd/mm/yyyy)", regex=r"\b[0-3][0-9]/[0-1][0-9]/[1-2][0-9]{3}\b", score=0.9), | |
Pattern(name="data (dd-mm-yyyy)", regex=r"\b[0-3][0-9]-[0-1][0-9]-[1-2][0-9]{3}\b", score=0.9), | |
Pattern(name="data (d/m/yyyy)", regex=r"\b[1-9]/[1-9]/[1-2][0-9]{3}\b", score=0.8), | |
Pattern(name="data (dd/mm/yy)", regex=r"\b[0-3][0-9]/[0-1][0-9]/[0-9]{2}\b", score=0.8) | |
] | |
data_recognizer = PatternRecognizer( | |
supported_entity="DATE_TIME", | |
patterns=data_patterns, | |
context=["nato", "nata", "data di nascita", "nasce", "data", "nascita"], | |
supported_language="en" | |
) | |
recognizers.append(data_recognizer) | |
print(f"✅ Creati {len(recognizers)} riconoscitori personalizzati") | |
return recognizers | |
# Crea i riconoscitori | |
italian_recognizers = create_italian_recognizers() | |
# ========================================================= | |
# CELLA 3: STANFORD COME RECOGNIZER SEPARATO | |
# ========================================================= | |
from presidio_analyzer import EntityRecognizer, RecognizerResult | |
from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline | |
import torch | |
class StanfordRecognizer(EntityRecognizer): | |
def __init__(self): | |
self.supported_entities = ["PERSON", "ORGANIZATION", "LOCATION", "DATE_TIME", "AGE", "PHONE_NUMBER", "EMAIL"] | |
self.supported_language = "en" | |
# Carica il modello Stanford | |
try: | |
self.tokenizer = AutoTokenizer.from_pretrained("StanfordAIMI/stanford-deidentifier-base") | |
self.model = AutoModelForTokenClassification.from_pretrained("StanfordAIMI/stanford-deidentifier-base") | |
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
self.model.to(self.device) | |
self.model.eval() | |
# Crea una pipeline per gestire più facilmente il modello | |
self.pipeline = pipeline( | |
"token-classification", | |
model=self.model, | |
tokenizer=self.tokenizer, | |
device=0 if torch.cuda.is_available() else -1, | |
aggregation_strategy="max" | |
) | |
print("✅ Modello Stanford caricato con successo!") | |
except Exception as e: | |
print(f"⚠️ Errore nel caricamento del modello Stanford: {e}") | |
self.pipeline = None | |
super().__init__(supported_entities=self.supported_entities, supported_language=self.supported_language) | |
def analyze(self, text, entities, nlp_artifacts): | |
""" | |
Analizza il testo e restituisce RecognizerResult | |
""" | |
results = [] | |
if self.pipeline is None: | |
return results | |
try: | |
# Usa la pipeline per processare il testo | |
outputs = self.pipeline(text) | |
for output in outputs: | |
# Mappa le etichette del modello Stanford a quelle di Presidio | |
stanford_to_presidio = { | |
"PATIENT": "PERSON", | |
"STAFF": "PERSON", | |
"HOSP": "ORGANIZATION", | |
"HOSPITAL": "ORGANIZATION", | |
"AGE": "AGE", | |
"DATE": "DATE_TIME", | |
"PHONE": "PHONE_NUMBER", | |
"PER": "PERSON", | |
"LOC": "LOCATION", | |
"ORG": "ORGANIZATION", | |
"PERSON": "PERSON", | |
"LOCATION": "LOCATION", | |
"ORGANIZATION": "ORGANIZATION" | |
} | |
entity_type = output.get("entity_group", "") | |
# Rimuovi prefissi B-, I- se presenti | |
if entity_type.startswith(("B-", "I-")): | |
entity_type = entity_type[2:] | |
# Mappa all'entità Presidio | |
presidio_entity = stanford_to_presidio.get(entity_type, entity_type) | |
# Crea RecognizerResult se l'entità è supportata | |
if presidio_entity in self.supported_entities: | |
result = RecognizerResult( | |
entity_type=presidio_entity, | |
start=output["start"], | |
end=output["end"], | |
score=output["score"] | |
) | |
results.append(result) | |
except Exception as e: | |
print(f"Errore nell'analisi Stanford: {e}") | |
return results | |
def load(self): | |
pass # Il caricamento è fatto nel costruttore | |
# Crea un'istanza del recognizer Stanford | |
stanford_recognizer = StanfordRecognizer() | |
# Se l'analyzer è già stato creato, aggiungi il recognizer Stanford | |
if 'analyzer' in globals(): | |
try: | |
analyzer.registry.add_recognizer(stanford_recognizer) | |
print("✅ Stanford recognizer aggiunto a Presidio") | |
except Exception as e: | |
print(f"⚠️ Errore nell'aggiunta di Stanford recognizer: {e}") | |
# ========================================================= | |
# CELLA 4: SISTEMA DI REGEX FALLBACK | |
# ========================================================= | |
class RegexFallbackEngine: | |
def __init__(self): | |
self.patterns = { | |
"PERSON": [ | |
r"\b[A-Z][a-z]+\s+[A-Z][a-z]+\b", # Nome Cognome | |
r"\b(?:Sig\.|Dott\.|Dr\.|Ing\.)\s+[A-Z][a-z]+\s+[A-Z][a-z]+\b", # Titolo + Nome Cognome | |
], | |
"CODICE_FISCALE": [ | |
r"\b[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]\b", | |
], | |
"PARTITA_IVA": [ | |
r"\b(?:IT)?\d{11}\b", | |
], | |
"DATE_TIME": [ | |
r"\b\d{1,2}[/\-\.]\d{1,2}[/\-\.]\d{2,4}\b", | |
r"\b\d{1,2}\s+(?:gennaio|febbraio|marzo|aprile|maggio|giugno|luglio|agosto|settembre|ottobre|novembre|dicembre)\s+\d{4}\b", | |
], | |
"PHONE_NUMBER": [ | |
r"\b\+39\s?\d{10}\b", | |
r"\b\d{10}\b", | |
r"\b0\d{1,3}[-\.\s]?\d{7}\b", | |
], | |
"EMAIL": [ | |
r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", | |
], | |
"IBAN_CODE": [ | |
r"\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}[A-Z0-9]{12}\b", | |
], | |
"TARGA": [ | |
r"\b[A-Z]{2}\d{3}[A-Z]{2}\b", | |
] | |
} | |
def analyze(self, text): | |
""" | |
Analizza il testo utilizzando regex | |
""" | |
results = [] | |
for entity_type, patterns in self.patterns.items(): | |
for pattern in patterns: | |
for match in re.finditer(pattern, text): | |
results.append({ | |
"entity_type": entity_type, | |
"start": match.start(), | |
"end": match.end(), | |
"text": match.group(), | |
"score": 0.9 # Assegna un punteggio fisso per regex | |
}) | |
return results | |
# Inizializza il sistema di regex fallback | |
regex_engine = RegexFallbackEngine() | |
# ========================================================= | |
# CELLA 5: CONFIGURAZIONE PRESIDIO SEMPLIFICATA | |
# ========================================================= | |
# Per ora, creiamo una configurazione base senza Stanford, che possiamo aggiungere come recognizer separato | |
from presidio_analyzer import AnalyzerEngine | |
from presidio_analyzer.predefined_recognizers import ( | |
PhoneRecognizer, EmailRecognizer, CreditCardRecognizer, IbanRecognizer | |
) | |
def setup_presidio_simple(): | |
""" | |
Configura Presidio con setup semplificato | |
""" | |
try: | |
# Crea l'analyzer engine con configurazione di base | |
analyzer = AnalyzerEngine() | |
# Aggiungi riconoscitori predefiniti | |
try: | |
analyzer.registry.add_recognizer(PhoneRecognizer()) | |
except: | |
pass | |
try: | |
analyzer.registry.add_recognizer(EmailRecognizer()) | |
except: | |
pass | |
try: | |
analyzer.registry.add_recognizer(CreditCardRecognizer()) | |
except: | |
pass | |
try: | |
analyzer.registry.add_recognizer(IbanRecognizer()) | |
except: | |
pass | |
# Aggiungi riconoscitori personalizzati se definiti | |
if 'italian_recognizers' in globals(): | |
for recognizer in italian_recognizers: | |
try: | |
analyzer.registry.add_recognizer(recognizer) | |
except Exception as e: | |
print(f"Errore aggiungendo recognizer: {e}") | |
# Crea l'anonymizer engine | |
anonymizer = AnonymizerEngine() | |
print("✅ Presidio configurato con successo!") | |
return analyzer, anonymizer | |
except Exception as e: | |
print(f"❌ Errore nella configurazione di Presidio: {e}") | |
# Fallback a configurazione minima | |
analyzer = AnalyzerEngine() | |
anonymizer = AnonymizerEngine() | |
print("⚠️ Usando configurazione di default") | |
return analyzer, anonymizer | |
# Inizializza Presidio | |
analyzer, anonymizer = setup_presidio_simple() | |
# ========================================================= | |
# CELLA 6: SISTEMA DI ANONIMIZZAZIONE IBRIDO (CORRETTO) | |
# ========================================================= | |
class HybridAnonymizer: | |
def __init__(self, presidio_analyzer, regex_engine, anonymizer): | |
self.presidio_analyzer = presidio_analyzer | |
self.regex_engine = regex_engine | |
self.anonymizer = anonymizer | |
def analyze_text(self, text, enable_stanford=True, enable_regex=True): | |
""" | |
Analizza il testo usando tutti i metodi disponibili | |
""" | |
all_entities = [] | |
# Presidio ora include Stanford tramite il recognizer aggiunto | |
presidio_results = self.presidio_analyzer.analyze( | |
text=text, | |
language="en", | |
entities=None, # Usa tutti i recognizer disponibili | |
allow_list=None | |
) | |
# Converti risultati Presidio | |
for result in presidio_results: | |
all_entities.append({ | |
"entity_type": result.entity_type, | |
"start": result.start, | |
"end": result.end, | |
"text": text[result.start:result.end], | |
"score": result.score, | |
"source": "presidio" | |
}) | |
# Aggiungi regex se abilitato | |
if enable_regex: | |
try: | |
regex_entities = self.regex_engine.analyze(text) | |
for entity in regex_entities: | |
all_entities.append({ | |
"entity_type": entity["entity_type"], | |
"start": entity["start"], | |
"end": entity["end"], | |
"text": entity["text"], | |
"score": entity["score"], | |
"source": "regex" | |
}) | |
except Exception as e: | |
print(f"Errore in Regex: {e}") | |
return self._merge_overlapping_entities(all_entities) | |
def _merge_overlapping_entities(self, entities): | |
if not entities: | |
return [] | |
entities.sort(key=lambda x: (x["start"], -x["score"])) | |
merged = [] | |
for entity in entities: | |
if not merged or merged[-1]["end"] <= entity["start"]: | |
merged.append(entity) | |
elif entity["score"] > merged[-1]["score"]: | |
merged[-1] = entity | |
return merged | |
def anonymize_text(self, text, entities, anonymization_type="replace"): | |
""" | |
Anonimizza il testo basandosi sulle entità trovate con diversi metodi | |
Tipi di anonimizzazione: | |
- replace: sostituisce con tag (es. <PERSON>) | |
- redact: oscura con asterischi (es. ******) | |
- pseudonymize: sostituisce con valori fittizi (es. Persona1) | |
""" | |
if not entities: | |
return text | |
if anonymization_type == "replace": | |
# Usa Presidio per sostituire le entità con tag | |
presidio_results = [] | |
for entity in entities: | |
from presidio_analyzer import RecognizerResult | |
presidio_results.append( | |
RecognizerResult( | |
entity_type=entity["entity_type"], | |
start=entity["start"], | |
end=entity["end"], | |
score=entity["score"] | |
) | |
) | |
# Configura l'anonimizzazione con tag | |
operators = { | |
"PERSON": OperatorConfig("replace", {"new_value": "<PERSON>"}), | |
"LOCATION": OperatorConfig("replace", {"new_value": "<LOCATION>"}), | |
"ORGANIZATION": OperatorConfig("replace", {"new_value": "<ORGANIZATION>"}), | |
"DATE_TIME": OperatorConfig("replace", {"new_value": "<DATE>"}), | |
"PHONE_NUMBER": OperatorConfig("replace", {"new_value": "<PHONE>"}), | |
"EMAIL_ADDRESS": OperatorConfig("replace", {"new_value": "<EMAIL>"}), | |
"IBAN_CODE": OperatorConfig("replace", {"new_value": "<IBAN>"}), | |
"CODICE_FISCALE": OperatorConfig("replace", {"new_value": "<CF>"}), | |
"PARTITA_IVA": OperatorConfig("replace", {"new_value": "<PIVA>"}), | |
"TARGA": OperatorConfig("replace", {"new_value": "<TARGA>"}), | |
"AGE": OperatorConfig("replace", {"new_value": "<AGE>"}) | |
} | |
anonymized_result = self.anonymizer.anonymize( | |
text=text, | |
analyzer_results=presidio_results, | |
operators=operators | |
) | |
return anonymized_result.text | |
elif anonymization_type == "redact": | |
# Sostituisce ogni entità con asterischi | |
anonymized = text | |
# Ordina le entità per posizione (dall'ultima alla prima) per non alterare gli indici | |
sorted_entities = sorted(entities, key=lambda x: x["start"], reverse=True) | |
for entity in sorted_entities: | |
# Genera asterischi della stessa lunghezza dell'entità | |
asterisks = "*" * (entity["end"] - entity["start"]) | |
# Sostituisci il testo | |
anonymized = anonymized[:entity["start"]] + asterisks + anonymized[entity["end"]:] | |
return anonymized | |
elif anonymization_type == "pseudonymize": | |
# Sostituisce ogni entità con un valore fittizio | |
anonymized = text | |
# Dizionario per tenere traccia dei valori fittizi generati | |
pseudonyms = {} | |
type_counts = {} | |
# Ordina le entità per posizione (dall'ultima alla prima) per non alterare gli indici | |
sorted_entities = sorted(entities, key=lambda x: x["start"], reverse=True) | |
for entity in sorted_entities: | |
entity_type = entity["entity_type"] | |
entity_text = entity["text"] | |
# Se questa entità è già stata sostituita in precedenza, usa lo stesso valore | |
if entity_text in pseudonyms: | |
new_value = pseudonyms[entity_text] | |
else: | |
# Inizializza il contatore se non esiste | |
if entity_type not in type_counts: | |
type_counts[entity_type] = 0 | |
# Incrementa il contatore | |
type_counts[entity_type] += 1 | |
# Genera un valore fittizio basato sul tipo di entità | |
if entity_type == "PERSON": | |
new_value = f"Persona{type_counts[entity_type]}" | |
elif entity_type == "LOCATION": | |
new_value = f"Luogo{type_counts[entity_type]}" | |
elif entity_type == "ORGANIZATION": | |
new_value = f"Organizzazione{type_counts[entity_type]}" | |
elif entity_type == "DATE_TIME": | |
new_value = f"Data{type_counts[entity_type]}" | |
elif entity_type == "PHONE_NUMBER": | |
new_value = f"+39-XXX-XXX-{1000+type_counts[entity_type]}" | |
elif entity_type == "EMAIL_ADDRESS" or entity_type == "EMAIL": | |
new_value = f"email{type_counts[entity_type]}@esempio.com" | |
elif entity_type == "IBAN_CODE": | |
new_value = f"IT00X0000000000000{type_counts[entity_type]}" | |
elif entity_type == "CODICE_FISCALE" or entity_type == "CF": | |
new_value = f"ABCDEF00G00H000{type_counts[entity_type]}" | |
elif entity_type == "PARTITA_IVA" or entity_type == "PIVA": | |
new_value = f"IT0000000000{type_counts[entity_type]}" | |
elif entity_type == "TARGA": | |
new_value = f"XX000{type_counts[entity_type]}" | |
elif entity_type == "AGE": | |
new_value = f"XX" | |
else: | |
new_value = f"{entity_type}{type_counts[entity_type]}" | |
# Memorizza il valore generato per riusi futuri | |
pseudonyms[entity_text] = new_value | |
# Sostituisci il testo | |
anonymized = anonymized[:entity["start"]] + new_value + anonymized[entity["end"]:] | |
return anonymized | |
else: | |
# Tipo di anonimizzazione non supportato, usa replace come fallback | |
print(f"Tipo di anonimizzazione non supportato: {anonymization_type}, usando 'replace'") | |
return self.anonymize_text(text, entities, "replace") | |
# Inizializza il sistema ibrido | |
hybrid_anonymizer = HybridAnonymizer(analyzer, regex_engine, anonymizer) | |
# ========================================================= | |
# CELLA 7: UTILITÀ DI VISUALIZZAZIONE | |
# ========================================================= | |
# Colori per i diversi tipi di entità | |
ENTITY_COLORS = { | |
"PERSON": "#ff7f50", # Corallo | |
"LOCATION": "#6495ed", # Azzurro | |
"ORGANIZATION": "#9acd32", # Verde | |
"DATE_TIME": "#ffa500", # Arancione | |
"PHONE_NUMBER": "#da70d6", # Orchidea | |
"EMAIL_ADDRESS": "#dda0dd", # Plum | |
"IBAN_CODE": "#1e90ff", # Blu | |
"CODICE_FISCALE": "#ff69b4", # Rosa | |
"PARTITA_IVA": "#ff69b4", # Rosa | |
"TARGA": "#bdb76b" # Kaki | |
} | |
def highlight_entities_html(text, entities): | |
""" | |
Evidenzia le entità trovate nel testo con colori | |
""" | |
if not entities: | |
return text | |
# Prepara HTML con span colorati | |
chars = list(text) | |
spans = [] | |
for entity in entities: | |
entity_type = entity["entity_type"] | |
source = entity.get("source", "unknown") | |
color = ENTITY_COLORS.get(entity_type, "#cccccc") | |
score = int(entity["score"] * 100) | |
# Tooltip con informazioni dettagliate | |
tooltip = f"{entity_type} ({score}%) - detected by {source}" | |
spans.append({ | |
"index": entity["start"], | |
"content": f'<span style="background-color: {color}; padding: 2px; border-radius: 3px;" title="{tooltip}">', | |
"is_opening": True | |
}) | |
spans.append({ | |
"index": entity["end"], | |
"content": '</span>', | |
"is_opening": False | |
}) | |
# Ordina i span (chiusura prima dell'apertura se stesso indice) | |
spans.sort(key=lambda x: (x["index"], not x["is_opening"])) | |
# Inserisce i tag span nel testo | |
offset = 0 | |
for span in spans: | |
adjusted_index = span["index"] + offset | |
chars.insert(adjusted_index, span["content"]) | |
offset += 1 | |
return "".join(chars) | |
def generate_statistics(entities): | |
""" | |
Genera statistiche sulle entità rilevate | |
""" | |
stats = { | |
"total_entities": len(entities), | |
"by_type": {}, | |
"by_source": {}, | |
"avg_confidence": 0, | |
"all_detected_types": set() | |
} | |
for entity in entities: | |
entity_type = entity["entity_type"] | |
source = entity.get("source", "unknown") | |
score = entity["score"] | |
# Count by type | |
stats["by_type"][entity_type] = stats["by_type"].get(entity_type, 0) + 1 | |
# Count by source | |
stats["by_source"][source] = stats["by_source"].get(source, 0) + 1 | |
# Track all detected types | |
stats["all_detected_types"].add(entity_type) | |
# Update average confidence | |
stats["avg_confidence"] += score | |
if entities: | |
stats["avg_confidence"] /= len(entities) | |
stats["all_detected_types"] = list(stats["all_detected_types"]) | |
return stats | |
# ========================================================= | |
# CELLA 8: INTERFACCIA GRADIO (MODIFICHE) | |
# ========================================================= | |
def process_text_gradio(text, anonymization_type, use_stanford, use_regex, confidence_threshold): | |
""" | |
Processa il testo con l'interfaccia Gradio | |
""" | |
# Verifica che il testo sia una stringa | |
if not isinstance(text, str): | |
return "Errore: input deve essere una stringa", "", "Errore: tipo di input non valido" | |
if not text.strip(): | |
return "", "", "Nessun testo fornito" | |
try: | |
# Analizza il testo | |
entities = hybrid_anonymizer.analyze_text( | |
text, | |
enable_stanford=use_stanford, | |
enable_regex=use_regex | |
) | |
# Filtra per confidenza | |
filtered_entities = [e for e in entities if e["score"] >= confidence_threshold] | |
# Genera HTML evidenziato | |
highlighted_html = highlight_entities_html(text, filtered_entities) | |
# Anonimizza il testo | |
anonymized_text = hybrid_anonymizer.anonymize_text(text, filtered_entities, anonymization_type) | |
# Genera statistiche | |
stats = generate_statistics(filtered_entities) | |
# Formatta le statistiche per Gradio | |
stats_str = f""" | |
**Statistiche rilevamento:** | |
- Entità totali trovate: {stats['total_entities']} | |
- Confidenza media: {stats['avg_confidence']:.2%} | |
- Tipi di entità rilevati: {', '.join(sorted(stats['all_detected_types']))} | |
**Per tipo:** | |
{chr(10).join([f"- {k}: {v}" for k, v in stats['by_type'].items()])} | |
**Per sorgente:** | |
{chr(10).join([f"- {k}: {v}" for k, v in stats['by_source'].items()])} | |
""" | |
return highlighted_html, anonymized_text, stats_str | |
except Exception as e: | |
import traceback | |
error_msg = f"Errore: {str(e)}\n{traceback.format_exc()}" | |
return error_msg, "", error_msg | |
# ========================================================= | |
# CELLA 9: INTERFACCIA DI CONTROLLO ENTITÀ (VERSIONE COMPLETA) | |
# ========================================================= | |
def process_text_with_entity_control( | |
text, | |
anonymization_type, | |
use_stanford, | |
use_regex, | |
confidence_threshold, | |
person_enabled, | |
location_enabled, | |
organization_enabled, | |
date_time_enabled, | |
phone_number_enabled, | |
email_enabled, | |
iban_enabled, | |
codice_fiscale_enabled, | |
partita_iva_enabled, | |
targa_enabled, | |
# Nuovi parametri di anonimizzazione | |
tag_format="<TAG>", | |
redact_char="*", | |
preserve_length=False, | |
# Anonimizzazione per tipo specifico | |
person_anon_method=None, | |
location_anon_method=None, | |
organization_anon_method=None, | |
date_time_anon_method=None, | |
phone_anon_method=None, | |
email_anon_method=None, | |
iban_anon_method=None, | |
cf_anon_method=None, | |
piva_anon_method=None, | |
targa_anon_method=None, | |
# Soglie di confidenza specifiche per tipo | |
person_threshold=None, | |
location_threshold=None, | |
organization_threshold=None, | |
date_time_threshold=None, | |
phone_threshold=None, | |
email_threshold=None, | |
iban_threshold=None, | |
cf_threshold=None, | |
piva_threshold=None, | |
targa_threshold=None, | |
# Formati dei pseudonimi | |
person_pseudo_format="Persona{num}", | |
location_pseudo_format="Luogo{num}", | |
organization_pseudo_format="Organizzazione{num}", | |
date_pseudo_format="Data{num}", | |
phone_pseudo_format="+39-XXX-XXX-{num}", | |
email_pseudo_format="email{num}@esempio.com", | |
iban_pseudo_format="IT00X0000000000000{num}", | |
cf_pseudo_format="ABCDEF00G00H000{num}", | |
piva_pseudo_format="IT0000000000{num}", | |
targa_pseudo_format="XX000{num}" | |
): | |
""" | |
Processa il testo con controllo sulle entità da estrarre/anonimizzare | |
e con parametri avanzati di anonimizzazione | |
""" | |
# Verifica che il testo sia una stringa | |
if not isinstance(text, str): | |
return "Errore: input deve essere una stringa", "", "Errore: tipo di input non valido" | |
if not text.strip(): | |
return "", "", "Nessun testo fornito" | |
try: | |
# Crea una lista di entità abilitate e mappa dei metodi per tipo | |
enabled_entities = [] | |
entity_anon_methods = {} | |
entity_thresholds = {} | |
entity_pseudo_formats = {} | |
# Mappa degli entity types, abilitazione, metodi, soglie e formati | |
entity_config = [ | |
("PERSON", person_enabled, person_anon_method, person_threshold, person_pseudo_format), | |
("LOCATION", location_enabled, location_anon_method, location_threshold, location_pseudo_format), | |
("ORGANIZATION", organization_enabled, organization_anon_method, organization_threshold, organization_pseudo_format), | |
("DATE_TIME", date_time_enabled, date_time_anon_method, date_time_threshold, date_pseudo_format), | |
("PHONE_NUMBER", phone_number_enabled, phone_anon_method, phone_threshold, phone_pseudo_format), | |
("EMAIL", email_enabled, email_anon_method, email_threshold, email_pseudo_format), | |
("EMAIL_ADDRESS", email_enabled, email_anon_method, email_threshold, email_pseudo_format), | |
("IBAN_CODE", iban_enabled, iban_anon_method, iban_threshold, iban_pseudo_format), | |
("CODICE_FISCALE", codice_fiscale_enabled, cf_anon_method, cf_threshold, cf_pseudo_format), | |
("PARTITA_IVA", partita_iva_enabled, piva_anon_method, piva_threshold, piva_pseudo_format), | |
("TARGA", targa_enabled, targa_anon_method, targa_threshold, targa_pseudo_format) | |
] | |
# Popola gli array basandosi sulla configurazione | |
for entity_type, is_enabled, anon_method, threshold, pseudo_format in entity_config: | |
if is_enabled: | |
enabled_entities.append(entity_type) | |
# Se è specificato un metodo specifico per questo tipo, usalo | |
if anon_method: | |
entity_anon_methods[entity_type] = anon_method | |
# Se è specificata una soglia specifica per questo tipo, usala | |
if threshold is not None: | |
entity_thresholds[entity_type] = threshold | |
# Salva il formato del pseudonimo per questo tipo | |
entity_pseudo_formats[entity_type] = pseudo_format | |
# Se nessuna entità è abilitata, mostra il testo originale | |
if not enabled_entities: | |
return text, text, "Nessuna entità selezionata per l'anonimizzazione" | |
# Analizza il testo | |
entities = hybrid_anonymizer.analyze_text( | |
text, | |
enable_stanford=use_stanford, | |
enable_regex=use_regex | |
) | |
# Filtra per confidenza e per tipo di entità abilitato, usando soglie specifiche per tipo se disponibili | |
filtered_entities = [] | |
for e in entities: | |
if e["entity_type"] in enabled_entities: | |
# Determina la soglia da usare | |
entity_threshold = entity_thresholds.get(e["entity_type"], confidence_threshold) | |
if e["score"] >= entity_threshold: | |
filtered_entities.append(e) | |
# Genera HTML evidenziato | |
highlighted_html = highlight_entities_html(text, filtered_entities) | |
# Anonimizza il testo con i parametri avanzati | |
anonymized_text = advanced_anonymize_text( | |
text, | |
filtered_entities, | |
anonymization_type, | |
tag_format=tag_format, | |
redact_char=redact_char, | |
preserve_length=preserve_length, | |
entity_anon_methods=entity_anon_methods, | |
entity_pseudo_formats=entity_pseudo_formats | |
) | |
# Genera statistiche | |
stats = generate_statistics(filtered_entities) | |
# Formatta le statistiche per Gradio | |
stats_str = f""" | |
**Statistiche rilevamento:** | |
- Entità totali trovate: {stats['total_entities']} | |
- Confidenza media: {stats['avg_confidence']:.2%} | |
- Tipi di entità rilevati: {', '.join(sorted(stats['all_detected_types']))} | |
**Per tipo:** | |
{chr(10).join([f"- {k}: {v}" for k, v in stats['by_type'].items()])} | |
**Per sorgente:** | |
{chr(10).join([f"- {k}: {v}" for k, v in stats['by_source'].items()])} | |
**Parametri di anonimizzazione:** | |
- Metodo globale: {anonymization_type} | |
- Formato tag: {tag_format} | |
- Preserva lunghezza: {"Sì" if preserve_length else "No"} | |
""" | |
# Aggiungi informazioni sui metodi specifici | |
if entity_anon_methods: | |
stats_str += "\n**Metodi specifici per tipo:**\n" | |
stats_str += chr(10).join([f"- {k}: {v}" for k, v in entity_anon_methods.items()]) | |
# Aggiungi informazioni sulle soglie specifiche | |
if entity_thresholds: | |
stats_str += "\n\n**Soglie di confidenza specifiche:**\n" | |
stats_str += chr(10).join([f"- {k}: {v}" for k, v in entity_thresholds.items()]) | |
return highlighted_html, anonymized_text, stats_str | |
except Exception as e: | |
import traceback | |
error_msg = f"Errore: {str(e)}\n{traceback.format_exc()}" | |
return error_msg, "", error_msg | |
def advanced_anonymize_text(text, entities, global_anon_type, tag_format="<TAG>", redact_char="*", | |
preserve_length=False, entity_anon_methods={}, entity_pseudo_formats={}): | |
""" | |
Versione avanzata dell'anonimizzazione che supporta più parametri | |
""" | |
if not entities: | |
return text | |
# Ordina le entità per posizione (dall'ultima alla prima) per non alterare gli indici | |
sorted_entities = sorted(entities, key=lambda x: x["start"], reverse=True) | |
anonymized = text | |
# Dizionario per tenere traccia dei valori sostituiti | |
pseudonyms = {} | |
type_counts = {} | |
for entity in sorted_entities: | |
entity_type = entity["entity_type"] | |
entity_text = entity["text"] | |
entity_start = entity["start"] | |
entity_end = entity["end"] | |
# Determina il metodo di anonimizzazione per questa entità specifica | |
anon_type = entity_anon_methods.get(entity_type, global_anon_type) | |
if anon_type == "replace": | |
# Formatta il tag in base al formato scelto | |
if tag_format == "<TAG>": | |
new_value = f"<{entity_type}>" | |
elif tag_format == "[TAG]": | |
new_value = f"[{entity_type}]" | |
elif tag_format == "{TAG}": | |
new_value = f"{{{entity_type}}}" | |
elif tag_format == "TAG_": | |
new_value = f"{entity_type}_" | |
else: | |
new_value = f"<{entity_type}>" | |
elif anon_type == "redact": | |
# Redact con il carattere scelto, mantenendo o meno la lunghezza originale | |
if preserve_length: | |
new_value = redact_char * (entity_end - entity_start) | |
else: | |
new_value = redact_char * 5 # Lunghezza fissa | |
elif anon_type == "pseudonymize": | |
# Pseudonimizzazione con nomi fittizi | |
if entity_text in pseudonyms: | |
new_value = pseudonyms[entity_text] | |
else: | |
# Inizializza il contatore se non esiste | |
if entity_type not in type_counts: | |
type_counts[entity_type] = 0 | |
# Incrementa il contatore | |
type_counts[entity_type] += 1 | |
# Ottieni il formato del pseudonimo per questo tipo di entità | |
pseudo_format = entity_pseudo_formats.get(entity_type, "") | |
# Genera un valore fittizio basato sul tipo e formato | |
if pseudo_format: | |
try: | |
# Prova a formattare usando il formato specificato | |
new_value = pseudo_format.format( | |
num=type_counts[entity_type], | |
type=entity_type, | |
orig=entity_text[:1] if entity_text else "X" | |
) | |
except Exception: | |
# Fallback in caso di errore di formattazione | |
new_value = f"{entity_type}{type_counts[entity_type]}" | |
else: | |
# Formati predefiniti per ogni tipo se non specificato | |
if entity_type == "PERSON": | |
new_value = f"Persona{type_counts[entity_type]}" | |
elif entity_type == "LOCATION": | |
new_value = f"Luogo{type_counts[entity_type]}" | |
elif entity_type == "ORGANIZATION": | |
new_value = f"Organizzazione{type_counts[entity_type]}" | |
elif entity_type == "DATE_TIME": | |
new_value = f"Data{type_counts[entity_type]}" | |
elif entity_type == "PHONE_NUMBER": | |
new_value = f"+39-XXX-XXX-{1000+type_counts[entity_type]}" | |
elif entity_type == "EMAIL_ADDRESS" or entity_type == "EMAIL": | |
new_value = f"email{type_counts[entity_type]}@esempio.com" | |
elif entity_type == "IBAN_CODE": | |
new_value = f"IT00X0000000000000{type_counts[entity_type]}" | |
elif entity_type == "CODICE_FISCALE" or entity_type == "CF": | |
new_value = f"ABCDEF00G00H000{type_counts[entity_type]}" | |
elif entity_type == "PARTITA_IVA" or entity_type == "PIVA": | |
new_value = f"IT0000000000{type_counts[entity_type]}" | |
elif entity_type == "TARGA": | |
new_value = f"XX000{type_counts[entity_type]}" | |
else: | |
new_value = f"{entity_type}{type_counts[entity_type]}" | |
# Memorizza il valore per riusi futuri | |
pseudonyms[entity_text] = new_value | |
# Adatta la lunghezza se necessario | |
if preserve_length and len(new_value) < (entity_end - entity_start): | |
new_value = new_value.ljust(entity_end - entity_start) | |
elif preserve_length and len(new_value) > (entity_end - entity_start): | |
# Troncamento con ellipsis | |
new_value = new_value[:entity_end - entity_start - 1] + "…" | |
else: | |
# Tipo sconosciuto, usa il metodo replace come fallback | |
new_value = f"<{entity_type}>" | |
# Sostituisci il testo | |
anonymized = anonymized[:entity_start] + new_value + anonymized[entity_end:] | |
return anonymized | |
# Esempi per la nuova interfaccia | |
entity_control_examples = [ | |
[ | |
"Il signor Mario Rossi, nato il 15/04/1980, CF: RSSMRC80D15H501V, residente in Via Roma 123, Milano, possiede la partita IVA IT12345678901.", | |
"replace", | |
True, | |
False, | |
0.5, | |
True, # person_enabled | |
True, # location_enabled | |
True, # organization_enabled | |
True, # date_time_enabled | |
True, # phone_number_enabled | |
True, # email_enabled | |
True, # iban_enabled | |
True, # codice_fiscale_enabled | |
True, # partita_iva_enabled | |
True, # targa_enabled | |
], | |
[ | |
"Per contattare il cliente Giovanni Bianchi utilizzare l'email [email protected] o il numero +39 333-123-4567.", | |
"replace", | |
False, | |
True, | |
0.6, | |
True, # person_enabled | |
False, # location_enabled | |
False, # organization_enabled | |
False, # date_time_enabled | |
True, # phone_number_enabled | |
True, # email_enabled | |
False, # iban_enabled | |
False, # codice_fiscale_enabled | |
False, # partita_iva_enabled | |
False, # targa_enabled | |
], | |
[ | |
"Il veicolo targato AB123CD appartiene a Maria Verdi, titolare del conto bancario IT12K1234567890123456789012.", | |
"replace", | |
True, | |
True, | |
0.7, | |
True, # person_enabled | |
False, # location_enabled | |
False, # organization_enabled | |
False, # date_time_enabled | |
False, # phone_number_enabled | |
False, # email_enabled | |
True, # iban_enabled | |
False, # codice_fiscale_enabled | |
False, # partita_iva_enabled | |
True, # targa_enabled | |
] | |
] | |
def process_text_with_entity_control_wrapper( | |
text, | |
anonymization_type, | |
use_stanford, | |
use_regex, | |
confidence_threshold, | |
person_enabled, | |
location_enabled, | |
organization_enabled, | |
date_time_enabled, | |
phone_number_enabled, | |
email_enabled, | |
iban_enabled, | |
codice_fiscale_enabled, | |
partita_iva_enabled, | |
targa_enabled | |
): | |
""" | |
Funzione wrapper che passa i parametri predefiniti ai nuovi parametri della funzione originale | |
""" | |
return process_text_with_entity_control( | |
text=text, | |
anonymization_type=anonymization_type, | |
use_stanford=use_stanford, | |
use_regex=use_regex, | |
confidence_threshold=confidence_threshold, | |
person_enabled=person_enabled, | |
location_enabled=location_enabled, | |
organization_enabled=organization_enabled, | |
date_time_enabled=date_time_enabled, | |
phone_number_enabled=phone_number_enabled, | |
email_enabled=email_enabled, | |
iban_enabled=iban_enabled, | |
codice_fiscale_enabled=codice_fiscale_enabled, | |
partita_iva_enabled=partita_iva_enabled, | |
targa_enabled=targa_enabled, | |
# Valori predefiniti per i nuovi parametri | |
tag_format="<TAG>", | |
redact_char="*", | |
preserve_length=False, | |
# Metodi specifici per tipo (tutti None = usa metodo globale) | |
person_anon_method=None, | |
location_anon_method=None, | |
organization_anon_method=None, | |
date_time_anon_method=None, | |
phone_anon_method=None, | |
email_anon_method=None, | |
iban_anon_method=None, | |
cf_anon_method=None, | |
piva_anon_method=None, | |
targa_anon_method=None, | |
# Soglie specifiche (tutte None = usa soglia globale) | |
person_threshold=None, | |
location_threshold=None, | |
organization_threshold=None, | |
date_time_threshold=None, | |
phone_threshold=None, | |
email_threshold=None, | |
iban_threshold=None, | |
cf_threshold=None, | |
piva_threshold=None, | |
targa_threshold=None, | |
# Formati predefiniti per i pseudonimi | |
person_pseudo_format="Persona{num}", | |
location_pseudo_format="Luogo{num}", | |
organization_pseudo_format="Organizzazione{num}", | |
date_pseudo_format="Data{num}", | |
phone_pseudo_format="+39-XXX-XXX-{num}", | |
email_pseudo_format="email{num}@esempio.com", | |
iban_pseudo_format="IT00X0000000000000{num}", | |
cf_pseudo_format="ABCDEF00G00H000{num}", | |
piva_pseudo_format="IT0000000000{num}", | |
targa_pseudo_format="XX000{num}" | |
) | |
# Crea l'interfaccia Gradio con controllo entità | |
demo_advanced = gr.Interface( | |
fn=process_text_with_entity_control_wrapper, # Usa la funzione wrapper | |
inputs=[ | |
gr.Textbox( | |
label="Testo da analizzare", | |
lines=5, | |
placeholder="Inserisci il testo contenente dati sensibili...", | |
value="Il signor Marco Rossi, nato il 15/04/1978, CF: RSSMRC78D15H501T, può essere contattato al numero +39 333-1234567 o all'email [email protected]." | |
), | |
gr.Radio( | |
["replace", "redact", "pseudonymize"], | |
label="Tipo di anonimizzazione", | |
value="replace" | |
), | |
gr.Checkbox( | |
label="Usa modello Stanford", | |
value=True | |
), | |
gr.Checkbox( | |
label="Usa Regex Fallback", | |
value=True | |
), | |
gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
value=0.5, | |
step=0.05, | |
label="Soglia di confidenza minima" | |
), | |
# Controlli per i tipi di entità | |
gr.Checkbox(label="Persone (PERSON)", value=True), | |
gr.Checkbox(label="Luoghi (LOCATION)", value=True), | |
gr.Checkbox(label="Organizzazioni (ORGANIZATION)", value=True), | |
gr.Checkbox(label="Date (DATE_TIME)", value=True), | |
gr.Checkbox(label="Numeri di telefono (PHONE_NUMBER)", value=True), | |
gr.Checkbox(label="Email (EMAIL)", value=True), | |
gr.Checkbox(label="IBAN (IBAN_CODE)", value=True), | |
gr.Checkbox(label="Codici Fiscali (CODICE_FISCALE)", value=True), | |
gr.Checkbox(label="Partite IVA (PARTITA_IVA)", value=True), | |
gr.Checkbox(label="Targhe (TARGA)", value=True) | |
], | |
outputs=[ | |
gr.HTML(label="Testo con entità evidenziate"), | |
gr.Textbox(label="Testo anonimizzato", lines=5), | |
gr.Markdown(label="Statistiche di rilevamento") | |
], | |
title="🔒 Sistema Ibrido di Anonimizzazione Dati - Controllo Entità", | |
description="Analizza e anonimizza testi selezionando i tipi di entità da processare.\n" | |
"I diversi colori indicano i tipi di entità rilevate.", | |
examples=entity_control_examples, | |
theme=gr.themes.Soft(), | |
allow_flagging="never" | |
) | |
# Avvia l'interfaccia migliorata | |
# demo_advanced.launch(share=True, debug=True) | |
# ========================================================= | |
# CELLA 10: INTERFACCIA AVANZATA CON PARAMETRI DI ANONIMIZZAZIONE | |
# ========================================================= | |
with gr.Blocks(theme=gr.themes.Soft()) as demo_blocks: | |
gr.Markdown("# 🔒 Sistema Ibrido di Anonimizzazione Dati") | |
gr.Markdown("Analizza e anonimizza testi in italiano con controllo avanzato dei parametri.") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
text_input = gr.Textbox( | |
label="Testo da analizzare", | |
lines=6, | |
placeholder="Inserisci il testo contenente dati sensibili...", | |
value="Il signor Marco Rossi, nato il 15/04/1978, CF: RSSMRC78D15H501T, può essere contattato al numero +39 333-1234567 o all'email [email protected]." | |
) | |
with gr.Tabs(): | |
with gr.TabItem("Impostazioni Base"): | |
with gr.Row(): | |
with gr.Column(): | |
anon_type = gr.Radio( | |
["replace", "redact", "pseudonymize"], | |
label="Tipo di anonimizzazione globale", | |
value="replace" | |
) | |
confidence = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
value=0.5, | |
step=0.05, | |
label="Soglia di confidenza globale" | |
) | |
with gr.Column(): | |
use_stanford = gr.Checkbox(label="Usa modello Stanford", value=True) | |
use_regex = gr.Checkbox(label="Usa Regex Fallback", value=True) | |
with gr.TabItem("Parametri di Anonimizzazione"): | |
with gr.Row(): | |
with gr.Column(): | |
tag_format = gr.Radio( | |
["<TAG>", "[TAG]", "{TAG}", "TAG_"], | |
label="Formato dei tag di sostituzione", | |
value="<TAG>" | |
) | |
redact_char = gr.Radio( | |
["*", "X", "#", "_"], | |
label="Carattere di oscuramento (per redact)", | |
value="*" | |
) | |
with gr.Column(): | |
preserve_length = gr.Checkbox( | |
label="Preserva lunghezza originale nelle sostituzioni", | |
value=False | |
) | |
gr.Markdown("### Anteprima formati di tag") | |
anteprima_html = gr.HTML(value="<div style='padding: 10px; background-color: #f0f0f0; border-radius: 5px;'><p><b>Replace:</b> <PERSON>, [PERSON], {PERSON}, PERSON_</p><p><b>Redact:</b> *****, XXXXX, #####, _____</p><p><b>Pseudonymize:</b> Persona1, Luogo1, Data1...</p></div>") | |
with gr.Accordion("Metodi specifici per tipo di entità", open=False): | |
gr.Markdown("Seleziona un metodo specifico per ogni tipo di entità, o lascia 'Globale' per usare il metodo globale") | |
with gr.Row(): | |
with gr.Column(): | |
person_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per PERSON", | |
value=None | |
) | |
location_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per LOCATION", | |
value=None | |
) | |
organization_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per ORGANIZATION", | |
value=None | |
) | |
date_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per DATE_TIME", | |
value=None | |
) | |
phone_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per PHONE_NUMBER", | |
value=None | |
) | |
with gr.Column(): | |
email_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per EMAIL", | |
value=None | |
) | |
iban_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per IBAN_CODE", | |
value=None | |
) | |
cf_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per CODICE_FISCALE", | |
value=None | |
) | |
piva_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per PARTITA_IVA", | |
value=None | |
) | |
targa_method = gr.Dropdown( | |
[None, "replace", "redact", "pseudonymize"], | |
label="Metodo per TARGA", | |
value=None | |
) | |
with gr.TabItem("Soglie di Confidenza"): | |
gr.Markdown("### Imposta soglie di confidenza specifiche per tipo di entità") | |
gr.Markdown("Lascia vuoto per usare la soglia globale") | |
with gr.Row(): | |
with gr.Column(): | |
person_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per PERSON", | |
value=None | |
) | |
location_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per LOCATION", | |
value=None | |
) | |
organization_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per ORGANIZATION", | |
value=None | |
) | |
date_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per DATE_TIME", | |
value=None | |
) | |
phone_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per PHONE_NUMBER", | |
value=None | |
) | |
with gr.Column(): | |
email_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per EMAIL", | |
value=None | |
) | |
iban_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per IBAN_CODE", | |
value=None | |
) | |
cf_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per CODICE_FISCALE", | |
value=None | |
) | |
piva_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per PARTITA_IVA", | |
value=None | |
) | |
targa_threshold = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
step=0.05, | |
label="Soglia per TARGA", | |
value=None | |
) | |
with gr.TabItem("Formati Pseudonimi"): | |
gr.Markdown("### Personalizza i formati dei pseudonimi") | |
gr.Markdown("Usa {num} per inserire il numero progressivo, {type} per il tipo di entità, {orig} per l'iniziale dell'originale") | |
with gr.Row(): | |
with gr.Column(): | |
person_format = gr.Textbox( | |
label="Formato per PERSON", | |
value="Persona{num}", | |
placeholder="es. Persona{num}, P{num}, {orig}..." | |
) | |
location_format = gr.Textbox( | |
label="Formato per LOCATION", | |
value="Luogo{num}", | |
placeholder="es. Luogo{num}, L{num}..." | |
) | |
organization_format = gr.Textbox( | |
label="Formato per ORGANIZATION", | |
value="Organizzazione{num}", | |
placeholder="es. Org{num}, Azienda{num}..." | |
) | |
date_format = gr.Textbox( | |
label="Formato per DATE_TIME", | |
value="Data{num}", | |
placeholder="es. GG/MM/AAAA, Data{num}..." | |
) | |
phone_format = gr.Textbox( | |
label="Formato per PHONE_NUMBER", | |
value="+39-XXX-XXX-{num}", | |
placeholder="es. +39-XXX-XXX-{num}..." | |
) | |
with gr.Column(): | |
email_format = gr.Textbox( | |
label="Formato per EMAIL", | |
value="email{num}@esempio.com", | |
placeholder="es. user{num}@domain.com..." | |
) | |
iban_format = gr.Textbox( | |
label="Formato per IBAN_CODE", | |
value="IT00X0000000000000{num}", | |
placeholder="es. IT00X0000..." | |
) | |
cf_format = gr.Textbox( | |
label="Formato per CODICE_FISCALE", | |
value="ABCDEF00G00H000{num}", | |
placeholder="es. ABCDEF00G00H000{num}..." | |
) | |
piva_format = gr.Textbox( | |
label="Formato per PARTITA_IVA", | |
value="IT0000000000{num}", | |
placeholder="es. IT0000000000{num}..." | |
) | |
targa_format = gr.Textbox( | |
label="Formato per TARGA", | |
value="XX000{num}", | |
placeholder="es. XX000{num}..." | |
) | |
process_btn = gr.Button("Analizza e Anonimizza", variant="primary") | |
with gr.Column(scale=1): | |
gr.Markdown("### Seleziona i tipi di entità da anonimizzare") | |
with gr.Group(): | |
person_enabled = gr.Checkbox(label="👤 Persone (PERSON)", value=True) | |
location_enabled = gr.Checkbox(label="📍 Luoghi (LOCATION)", value=True) | |
organization_enabled = gr.Checkbox(label="🏢 Organizzazioni (ORGANIZATION)", value=True) | |
date_time_enabled = gr.Checkbox(label="📅 Date (DATE_TIME)", value=True) | |
phone_number_enabled = gr.Checkbox(label="📞 Numeri di telefono (PHONE_NUMBER)", value=True) | |
email_enabled = gr.Checkbox(label="📧 Email (EMAIL)", value=True) | |
iban_enabled = gr.Checkbox(label="💳 IBAN (IBAN_CODE)", value=True) | |
codice_fiscale_enabled = gr.Checkbox(label="🪪 Codici Fiscali (CODICE_FISCALE)", value=True) | |
partita_iva_enabled = gr.Checkbox(label="🏷️ Partite IVA (PARTITA_IVA)", value=True) | |
targa_enabled = gr.Checkbox(label="🚗 Targhe (TARGA)", value=True) | |
with gr.Row(): | |
select_all_btn = gr.Button("Seleziona tutti") | |
clear_all_btn = gr.Button("Deseleziona tutti") | |
with gr.Accordion("Guida rapida", open=False): | |
gr.Markdown(""" | |
**Tipi di anonimizzazione:** | |
- **Replace**: sostituisce l'entità con un tag (es. <PERSON>) | |
- **Redact**: oscura l'entità con caratteri (es. *****) | |
- **Pseudonymize**: sostituisce con valori fittizi (es. Persona1) | |
**Formato tag:** | |
- `<TAG>`: usa tag HTML (es. <PERSON>) | |
- `[TAG]`: usa parentesi quadre (es. [PERSON]) | |
- `{TAG}`: usa parentesi graffe (es. {PERSON}) | |
- `TAG_`: usa underscore (es. PERSON_) | |
**Preserva lunghezza:** | |
- Se attivo, mantiene la lunghezza originale dell'entità | |
- Utile per mantenere il formato del documento | |
""") | |
with gr.Tabs(): | |
with gr.TabItem("Risultati"): | |
html_output = gr.HTML(label="Testo con entità evidenziate") | |
anon_output = gr.Textbox(label="Testo anonimizzato", lines=5) | |
stats_output = gr.Markdown(label="Statistiche di rilevamento") | |
# Funzione per aggiornare l'anteprima dei formati di tag | |
def update_preview(tag_format, redact_char, preserve_length): | |
replace_examples = { | |
"<TAG>": "<PERSON>", | |
"[TAG]": "[PERSON]", | |
"{TAG}": "{PERSON}", | |
"TAG_": "PERSON_" | |
} | |
redact_example = redact_char * 5 | |
if preserve_length: | |
redact_note = " (mantenendo lunghezza originale)" | |
else: | |
redact_note = " (lunghezza fissa)" | |
return f""" | |
<div style='padding: 10px; background-color: #f0f0f0; border-radius: 5px;'> | |
<p><b>Replace:</b> {replace_examples[tag_format]}</p> | |
<p><b>Redact:</b> {redact_example}{redact_note}</p> | |
<p><b>Pseudonymize:</b> Persona1, Luogo1, Data1...</p> | |
</div> | |
""" | |
# Aggiorna l'anteprima quando cambiano i parametri | |
tag_format.change( | |
update_preview, | |
inputs=[tag_format, redact_char, preserve_length], | |
outputs=anteprima_html | |
) | |
redact_char.change( | |
update_preview, | |
inputs=[tag_format, redact_char, preserve_length], | |
outputs=anteprima_html | |
) | |
preserve_length.change( | |
update_preview, | |
inputs=[tag_format, redact_char, preserve_length], | |
outputs=anteprima_html | |
) | |
# Logica per i pulsanti di selezione | |
def select_all(): | |
return [True] * 10 | |
def clear_all(): | |
return [False] * 10 | |
select_all_btn.click( | |
select_all, | |
inputs=None, | |
outputs=[ | |
person_enabled, location_enabled, organization_enabled, date_time_enabled, | |
phone_number_enabled, email_enabled, iban_enabled, codice_fiscale_enabled, | |
partita_iva_enabled, targa_enabled | |
] | |
) | |
clear_all_btn.click( | |
clear_all, | |
inputs=None, | |
outputs=[ | |
person_enabled, location_enabled, organization_enabled, date_time_enabled, | |
phone_number_enabled, email_enabled, iban_enabled, codice_fiscale_enabled, | |
partita_iva_enabled, targa_enabled | |
] | |
) | |
# Callback per il pulsante di processo | |
process_btn.click( | |
process_text_with_entity_control, | |
inputs=[ | |
text_input, anon_type, use_stanford, use_regex, confidence, | |
person_enabled, location_enabled, organization_enabled, date_time_enabled, | |
phone_number_enabled, email_enabled, iban_enabled, codice_fiscale_enabled, | |
partita_iva_enabled, targa_enabled, | |
# Parametri di anonimizzazione avanzati | |
tag_format, redact_char, preserve_length, | |
# Metodi specifici per tipo | |
person_method, location_method, organization_method, date_method, | |
phone_method, email_method, iban_method, cf_method, piva_method, targa_method, | |
# Soglie specifiche per tipo | |
person_threshold, location_threshold, organization_threshold, date_threshold, | |
phone_threshold, email_threshold, iban_threshold, cf_threshold, piva_threshold, targa_threshold, | |
# Formati dei pseudonimi | |
person_format, location_format, organization_format, date_format, phone_format, | |
email_format, iban_format, cf_format, piva_format, targa_format | |
], | |
outputs=[html_output, anon_output, stats_output] | |
) | |
# Avvia l'interfaccia a blocchi (commenta la linea launch della cella 11 se la usi) | |
if __name__ == "__main__": | |
demo_blocks.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False, | |
show_error=True | |
) |