Référence API¶
GDPR Pseudonymizer — Documentation des modules pour les développeurs
Vue d'ensemble¶
Le package gdpr_pseudonymizer est organisé en sous-packages :
| Package | Rôle |
|---|---|
core |
Orchestration du traitement de documents |
nlp |
Pipeline de reconnaissance d'entités nommées (NER) |
pseudonym |
Attribution des pseudonymes et gestion des bibliothèques |
data |
Base de données, modèles, chiffrement et dépôts |
validation |
Processus de validation interactive avec contrôle humain |
cli |
Interface en ligne de commande (basée sur Typer) |
utils |
Gestion des fichiers, configuration et journalisation |
Module Core (gdpr_pseudonymizer.core)¶
DocumentProcessor¶
Orchestre le traitement d'un document : détection des entités, validation, attribution des pseudonymes et production du fichier de sortie.
from gdpr_pseudonymizer.core.document_processor import DocumentProcessor
processor = DocumentProcessor(
db_path="mappings.db",
passphrase="your-secure-passphrase",
theme="neutral", # neutral | star_wars | lotr
model_name="spacy"
)
result = processor.process_document(
input_path="input.txt",
output_path="output.txt",
skip_validation=False, # Passer à True pour un usage programmatique (sans interface)
entity_type_filter=None # Facultatif : ensemble de types, ex. {"PERSON", "LOCATION"}
)
ProcessingResult¶
Objet retourné par DocumentProcessor.process_document() :
| Attribut | Type | Description |
|---|---|---|
success |
bool |
Indique si le traitement s'est terminé sans erreur |
input_file |
str |
Chemin du fichier d'entrée |
output_file |
str |
Chemin du fichier de sortie |
entities_detected |
int |
Nombre total d'entités détectées |
entities_new |
int |
Nouveaux pseudonymes attribués |
entities_reused |
int |
Pseudonymes réutilisés (idempotence) |
processing_time_seconds |
float |
Durée totale du traitement |
error_message |
str | None |
Description de l'erreur en cas d'échec |
Module NLP (gdpr_pseudonymizer.nlp)¶
DetectedEntity¶
Objet représentant une entité nommée détectée :
| Attribut | Type | Description |
|---|---|---|
text |
str |
Texte d'origine de l'entité |
entity_type |
str |
Classification : PERSON, LOCATION, ORG |
start_pos |
int |
Position de début dans le texte (en caractères) |
end_pos |
int |
Position de fin dans le texte (en caractères) |
confidence |
float | None |
Score de confiance NER (0.0-1.0) |
gender |
str | None |
Genre : male, female, neutral, unknown |
is_ambiguous |
bool |
Indique si l'entité est signalée comme ambiguë |
source |
str |
Source de la détection : spacy, regex ou hybrid |
EntityDetector (classe abstraite)¶
Interface que tout détecteur NER doit implémenter :
class EntityDetector(ABC):
@abstractmethod
def load_model(self, model_name: str) -> None: ...
@abstractmethod
def detect_entities(self, text: str) -> list[DetectedEntity]: ...
@abstractmethod
def get_model_info(self) -> dict[str, str]: ...
@property
@abstractmethod
def supports_gender_classification(self) -> bool: ...
HybridDetector¶
Détecteur par défaut, qui combine le NER spaCy avec la détection par expressions régulières. C'est le détecteur utilisé par DocumentProcessor.
from gdpr_pseudonymizer.nlp.hybrid_detector import HybridDetector
detector = HybridDetector()
detector.load_model("fr_core_news_lg")
entities = detector.detect_entities("Marie Dubois travaille a Paris.")
SpaCyDetector¶
Détecteur NER utilisant uniquement spaCy :
from gdpr_pseudonymizer.nlp.spacy_detector import SpaCyDetector
detector = SpaCyDetector()
detector.load_model("fr_core_news_lg")
entities = detector.detect_entities(text)
RegexMatcher¶
Détection d'entités par expressions régulières : titres français, noms composés, dictionnaires de noms, suffixes d'organisations :
from gdpr_pseudonymizer.nlp.regex_matcher import RegexMatcher
matcher = RegexMatcher()
matcher.load_patterns() # Charge depuis config/detection_patterns.yaml
entities = matcher.match_entities(text)
stats = matcher.get_pattern_stats()
NameDictionary¶
Dictionnaire de noms français pour la détection par expressions régulières :
from gdpr_pseudonymizer.nlp.name_dictionary import NameDictionary
names = NameDictionary()
names.load()
names.is_first_name("Marie") # True
names.is_last_name("Dubois") # True
EntityGrouping (module entity_grouping)¶
Regroupe les différentes formes d'une même entité en un seul élément à valider, ce qui réduit la charge de validation pour l'utilisateur. Par exemple, « Marie Dubois », « Pr. Dubois » et « Dubois » sont regroupés en un seul élément.
from gdpr_pseudonymizer.nlp.entity_grouping import group_entity_variants
groups = group_entity_variants(detected_entities)
for canonical, occurrences, variant_texts in groups:
print(f"{canonical.text} (formes rencontrées : {variant_texts})")
Type de retour : list[tuple[DetectedEntity, list[DetectedEntity], set[str]]]
Chaque tuple contient :
| Élément | Type | Description |
|---|---|---|
canonical |
DetectedEntity |
Entité représentative (forme textuelle la plus longue) |
occurrences |
list[DetectedEntity] |
Toutes les occurrences de l'entité dans le groupe |
variant_texts |
set[str] |
Formes textuelles distinctes au sein du groupe |
Règles de regroupement par type d'entité :
| Type | Règle |
|---|---|
PERSON |
Suppression des titres + correspondance par nom de famille. « Marie Dubois » et « Dubois » sont regroupés. Des prénoms différents restent séparés (« Marie Dubois » vs « Jean Dubois »). Les noms de famille ambigus correspondant à plusieurs personnes sont isolés. |
LOCATION |
Suppression des prépositions françaises. « à Lyon » et « Lyon » sont regroupés. |
ORG |
Correspondance insensible à la casse. « ACME Corp » et « acme corp » sont regroupés. |
Module Pseudonym (gdpr_pseudonymizer.pseudonym)¶
LibraryBasedPseudonymManager¶
Charge les bibliothèques de pseudonymes depuis des fichiers JSON et attribue les pseudonymes en tenant compte du genre :
from gdpr_pseudonymizer.pseudonym.library_manager import LibraryBasedPseudonymManager
manager = LibraryBasedPseudonymManager()
manager.load_library("star_wars")
assignment = manager.assign_pseudonym(
entity_type="PERSON",
first_name="Marie",
last_name="Dubois",
gender="female"
)
print(assignment.pseudonym_full) # ex. "Leia Organa"
PseudonymAssignment¶
Objet retourné lors de l'attribution d'un pseudonyme :
| Attribut | Type | Description |
|---|---|---|
pseudonym_full |
str |
Pseudonyme complet |
pseudonym_first |
str | None |
Prénom du pseudonyme (PERSON uniquement) |
pseudonym_last |
str | None |
Nom de famille du pseudonyme (PERSON uniquement) |
theme |
str |
Thème de la bibliothèque utilisée |
exhaustion_percentage |
float |
Taux d'utilisation de la bibliothèque (0.0-1.0) |
is_ambiguous |
bool |
Indique une ambiguïté |
ambiguity_reason |
str | None |
Motif de l'ambiguïté |
GenderDetector¶
Détecte automatiquement le genre des prénoms français à partir d'un dictionnaire embarqué de 945 prénoms issu de l'INSEE. Utilisé par CompositionalPseudonymEngine pour attribuer automatiquement des pseudonymes du même genre.
from gdpr_pseudonymizer.pseudonym.gender_detector import GenderDetector
detector = GenderDetector()
detector.load()
detector.detect_gender("Marie") # "female"
detector.detect_gender("Jean") # "male"
detector.detect_gender("Camille") # None (épicène)
detector.detect_gender("Xyzabc") # None (inconnu)
# Détection à partir du nom complet (extrait le prénom, vérifie le type d'entité)
detector.detect_gender_from_full_name("Marie Dupont", "PERSON") # "female"
detector.detect_gender_from_full_name("Jean-Pierre Martin", "PERSON") # "male" (composé : utilise le premier élément)
detector.detect_gender_from_full_name("Paris", "LOCATION") # None (entité non-PERSON)
Méthodes principales :
| Méthode | Description |
|---|---|
load() |
Charge le dictionnaire de genre depuis le fichier JSON (chargement différé au premier appel) |
detect_gender(first_name) |
Détecte le genre d'un prénom. Retourne "male", "female" ou None |
detect_gender_from_full_name(full_name, entity_type) |
Extrait le prénom du nom complet et détecte le genre. Retourne toujours None pour les entités non-PERSON |
Statistiques du dictionnaire : 470 masculins, 457 féminins, 18 épicènes. Recherche insensible à la casse.
CompositionalPseudonymEngine¶
Gère la logique de résolution par composition : « Marie Dubois » correspond à « Leia Organa », et « Marie » seul correspond à « Leia » pour garantir la cohérence. Peut intégrer GenderDetector pour attribuer automatiquement des pseudonymes du même genre.
from gdpr_pseudonymizer.pseudonym.assignment_engine import CompositionalPseudonymEngine
engine = CompositionalPseudonymEngine(
pseudonym_manager=manager,
mapping_repository=repo,
gender_detector=detector # Facultatif : active la détection automatique du genre
)
result = engine.assign_compositional_pseudonym(
entity_text="Marie Dubois",
entity_type="PERSON",
gender="female"
)
Méthodes principales :
| Méthode | Description |
|---|---|
assign_compositional_pseudonym(entity_text, entity_type, gender) |
Attribue un pseudonyme avec réutilisation des composants |
strip_titles(text) |
Supprime les titres honorifiques français (Dr., Mme, Maître) |
strip_prepositions(text) |
Supprime les prépositions françaises des noms de lieux |
parse_full_name(entity_text) |
Décompose en (prénom, nom, est_composé) |
find_standalone_components(component, component_type) |
Recherche une correspondance existante pour un composant |
Module Data (gdpr_pseudonymizer.data)¶
Fonctions de base de données¶
from gdpr_pseudonymizer.data.database import init_database, open_database
# Créer une nouvelle base de données chiffrée
init_database("mappings.db", "your-passphrase")
# Ouvrir une base existante (gestionnaire de contexte)
with open_database("mappings.db", "your-passphrase") as db_session:
# Utiliser les dépôts avec db_session
pass
Modèles SQLAlchemy¶
Entity (table entities)¶
| Colonne | Type | Description |
|---|---|---|
id |
str (UUID) |
Clé primaire |
entity_type |
str |
PERSON, LOCATION, ORG |
first_name |
str | None |
Prénom d'origine (chiffré) |
last_name |
str | None |
Nom de famille d'origine (chiffré) |
full_name |
str |
Texte complet de l'entité (chiffré, unique) |
pseudonym_first |
str | None |
Prénom du pseudonyme attribué |
pseudonym_last |
str | None |
Nom de famille du pseudonyme attribué |
pseudonym_full |
str |
Pseudonyme complet |
gender |
str | None |
Genre détecté |
confidence_score |
float | None |
Score de confiance NER |
theme |
str |
Thème de la bibliothèque utilisée |
first_seen_timestamp |
datetime |
Date de première détection |
Operation (table operations)¶
| Colonne | Type | Description |
|---|---|---|
id |
str (UUID) |
Clé primaire |
timestamp |
datetime |
Horodatage de l'opération |
operation_type |
str |
PROCESS, BATCH, VALIDATE, etc. |
files_processed |
list[str] |
Tableau JSON des chemins de fichiers |
entity_count |
int |
Nombre d'entités traitées |
processing_time_seconds |
float |
Durée |
success |
bool |
Résultat |
Dépôts¶
MappingRepository (abstrait)¶
class MappingRepository(ABC):
def find_by_full_name(self, full_name: str) -> Entity | None: ...
def find_by_component(self, component: str, component_type: str) -> list[Entity]: ...
def save(self, entity: Entity) -> Entity: ...
def save_batch(self, entities: list[Entity]) -> list[Entity]: ...
def find_all(self, entity_type=None, is_ambiguous=None) -> list[Entity]: ...
AuditRepository¶
from gdpr_pseudonymizer.data.repositories.audit_repository import AuditRepository
repo = AuditRepository(session)
repo.log_operation(operation)
operations = repo.find_operations(operation_type="PROCESS", success=True)
repo.export_to_csv("audit_log.csv")
EncryptionService¶
Chiffrement authentifié déterministe AES-256-SIV :
from gdpr_pseudonymizer.data.encryption import EncryptionService
salt = EncryptionService.generate_salt()
valid, msg = EncryptionService.validate_passphrase("my-passphrase")
service = EncryptionService(passphrase="...", salt=salt)
ciphertext = service.encrypt("Marie Dubois")
plaintext = service.decrypt(ciphertext)
Module Validation (gdpr_pseudonymizer.validation)¶
Processus de validation¶
from gdpr_pseudonymizer.validation.workflow import run_validation_workflow
validated_entities = run_validation_workflow(
entities=detected_entities,
document_text=text,
document_path="input.txt",
pseudonym_assigner=my_assigner_fn # Fonction de rappel facultative
)
États de révision¶
| État | Description |
|---|---|
PENDING |
En attente de révision |
CONFIRMED |
Confirmé comme entité correcte |
REJECTED |
Rejeté (faux positif) |
MODIFIED |
Texte de l'entité modifié par l'utilisateur |
ADDED |
Ajouté manuellement par l'utilisateur |
Actions utilisateur¶
| Classe | Description |
|---|---|
ConfirmAction(entity) |
Accepter l'entité et son pseudonyme |
RejectAction(entity) |
Signaler comme faux positif |
ModifyAction(entity, new_text) |
Modifier le texte de l'entité |
AddAction(text, entity_type, start_pos, end_pos) |
Ajouter une entité non détectée |
ChangePseudonymAction(entity, new_pseudonym) |
Remplacer le pseudonyme |
Module Utils (gdpr_pseudonymizer.utils)¶
Gestion des fichiers¶
from gdpr_pseudonymizer.utils.file_handler import read_file, write_file, validate_file_path
text = read_file("input.txt")
write_file("output.txt", pseudonymized_text)
validate_file_path("doc.txt", allowed_extensions=[".txt", ".md"])
Configuration¶
from gdpr_pseudonymizer.utils.config_manager import load_config, Config
config = load_config("config.yaml") # Ou None pour les valeurs par défaut
# config.theme, config.model_name, config.db_path, etc.
Journalisation¶
from gdpr_pseudonymizer.utils.logger import configure_logging, get_logger
configure_logging("INFO")
logger = get_logger("my_module")
logger.info("entity_detected", entity_type="PERSON", confidence=0.92)
Exceptions¶
Toutes les exceptions héritent de PseudonymizerError :
| Exception | Déclenchée lorsque |
|---|---|
ConfigurationError |
Configuration invalide ou manquante |
ModelNotFoundError |
Le modèle NLP n'a pas pu être chargé |
EncryptionError |
Le chiffrement ou le déchiffrement échoue |
ValidationError |
Erreur dans le processus de validation |
FileProcessingError |
Erreur de lecture ou d'écriture de fichier |
from gdpr_pseudonymizer.exceptions import (
PseudonymizerError,
ConfigurationError,
ModelNotFoundError,
EncryptionError,
ValidationError,
FileProcessingError,
)
Points d'extension¶
Bibliothèques de pseudonymes personnalisées¶
Créez un fichier JSON dans data/pseudonyms/ en respectant ce schéma :
{
"theme": "my_theme",
"data_sources": [
{
"source_name": "Description",
"url": "https://...",
"license": "Type de licence",
"usage_justification": "Justification de l'utilisation",
"extraction_date": "2026-01-01",
"extraction_method": "Méthode de collecte des données"
}
],
"first_names": {
"male": ["Name1", "Name2"],
"female": ["Name3", "Name4"],
"neutral": ["Name5", "Name6"]
},
"last_names": ["LastName1", "LastName2"],
"locations": {
"cities": ["City1", "City2"],
"regions": ["Region1", "Region2"]
},
"organizations": {
"companies": ["Company1", "Company2"],
"agencies": ["Agency1"],
"institutions": ["Institute1"]
}
}
Contenu minimal : 500+ prénoms, 500+ noms de famille, 80+ lieux, 35+ organisations.
Utilisation :
manager = LibraryBasedPseudonymManager()
manager.load_library("my_theme")
Ou en ligne de commande :
poetry run gdpr-pseudo process doc.txt --theme my_theme
Détecteur NLP personnalisé¶
Créez une sous-classe de EntityDetector :
from gdpr_pseudonymizer.nlp.entity_detector import EntityDetector, DetectedEntity
class MyDetector(EntityDetector):
def load_model(self, model_name: str) -> None:
# Charger votre modèle
pass
def detect_entities(self, text: str) -> list[DetectedEntity]:
# Retourner les entités détectées
pass
def get_model_info(self) -> dict[str, str]:
return {"name": "my_model", "version": "1.0"}
@property
def supports_gender_classification(self) -> bool:
return False
Exemple d'utilisation programmatique¶
Exemple complet de pseudonymisation d'un document par programme :
from gdpr_pseudonymizer.core.document_processor import DocumentProcessor
# Initialiser le processeur
processor = DocumentProcessor(
db_path="project.db",
passphrase="MySecurePassphrase123",
theme="neutral",
model_name="spacy"
)
# Traiter le document (skip_validation=True pour un usage non interactif)
result = processor.process_document(
input_path="interview.txt",
output_path="interview_pseudonymized.txt",
skip_validation=True,
entity_type_filter={"PERSON", "LOCATION"} # Facultatif : ne traiter que ces types
)
if result.success:
print(f"Traité : {result.entities_detected} entités")
print(f"Nouvelles : {result.entities_new}, réutilisées : {result.entities_reused}")
print(f"Durée : {result.processing_time_seconds:.2f}s")
else:
print(f"Erreur : {result.error_message}")
Documentation associée¶
- Référence de la ligne de commande — Documentation des commandes
- Méthodologie — Approche technique et conformité RGPD
- Guide d'installation — Instructions d'installation
- Tutoriel — Guide d'utilisation