Metadata-Version: 2.4
Name: pseudonimizar
Version: 0.1.0
Summary: Pseudonimización local de documentos jurídicos en español. No envía nada por red.
Project-URL: Homepage, https://github.com/triari-partners/pseudonimizar
Project-URL: Repository, https://github.com/triari-partners/pseudonimizar
Project-URL: Changelog, https://github.com/triari-partners/pseudonimizar/blob/main/CHANGELOG.md
Author: Triari Partners
License: MIT
License-File: LICENSE
Keywords: anonimizacion,español,legal,pii,pseudonimizacion,rgpd
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Legal Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: Spanish
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: <3.13,>=3.10
Requires-Dist: click>=8.1
Requires-Dist: presidio-analyzer>=2.2.355
Requires-Dist: pypdf>=4.0
Requires-Dist: python-docx>=1.1
Requires-Dist: regex>=2024.0
Requires-Dist: spacy<3.9,>=3.7
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# pseudonimizar

**Pseudonimización local de documentos jurídicos en español.** Sustituye los
identificadores reales de un `.docx` o `.pdf` (nombres, DNI/NIE/CIF, IBAN,
teléfonos, direcciones, sociedades…) por **etiquetas ficticias consistentes**,
de modo que el documento sea seguro para subir a un LLM de consumo sin exponer
datos de cliente.

> **El contenido del documento NUNCA sale de tu máquina por red.** La única
> conexión admitida es la descarga del modelo de lenguaje la **primera vez**.
> No se guarda ninguna tabla de correspondencia real↔ficticio en disco
> (*solo ida, sin mapa*).

---

## Uso en un paso

```bash
uvx --with pip pseudonimizar contrato.docx
```

Esto descarga y ejecuta la herramienta al vuelo (necesitas solo
[`uv`](https://docs.astral.sh/uv/), un único binario que instala Python por ti).

> **¿Por qué `--with pip`?** El modelo de lenguaje (~0,5 GB) no es una dependencia
> de PyPI (límites de tamaño): se instala en runtime la primera vez, y para eso el
> entorno efímero de `uvx` necesita `pip`. Con `--with pip` el modelo se instala
> en el entorno cacheado y **persiste**: las siguientes ejecuciones van directas
> (~1 s). El slash command `/pseudonimizar` ya añade `--with pip` por ti.

Produce una copia segura junto al original:

```
contrato.docx   →  contrato.anon.docx
escritura.pdf   →  escritura.anon.txt
```

y un resumen de lo sustituido:

```
✓ Pseudonimizado en local. Nada se ha enviado por red.
  Personas ................... 4
  Sociedades / orgs .......... 2
  DNI ........................ 3
  IBAN / cuentas ............. 1
  Teléfonos .................. 2
  Emails ..................... 1
  Direcciones ................ 2
Copia segura: contrato.anon.docx
Recuerda: revisa 10 s lo indirecto (fechas señaladas, importes singulares, fincas).
```

**Primera vez:** se descarga el modelo `es_core_news_lg` (~0,5 GB, una sola vez,
queda cacheado). Para dejarlo listo en un setup (p. ej. de un aula) sin esperar
en directo:

```bash
uvx --with pip pseudonimizar preparar
```

A partir de ahí funciona **sin conexión**.

## Opciones

```
pseudonimizar <archivo> [opciones]
pseudonimizar preparar            # descarga/cachea el modelo, sin tocar documentos
```

| Opción | Efecto | Defecto |
|---|---|---|
| `<archivo>` | Documento de entrada (`.docx` o `.pdf`). | — (obligatorio) |
| `--salida, -s RUTA` | Ruta de salida. | junto al original |
| `--idioma, -i CODE` | Idioma del documento. | `es` |
| `--umbral, -u FLOAT` | Umbral de confianza mínimo (bajo a propósito: mejor sobre-redactar). | `0.35` |
| `--silencioso, -q` | No imprime el resumen. | desactivado |
| `--version` / `--help` | Versión / ayuda. | — |

**Códigos de salida:** `0` ok · `1` error de uso (archivo inexistente, formato no
soportado, PDF escaneado sin texto) · `2` error interno.

## Qué detecta

**Identificadores directos** (se redactan automáticamente):

- Nombres de **personas** y **sociedades / organizaciones** (incl. razón social
  con forma jurídica: S.L., S.A., S.L.U., S.Coop., A.I.E.…).
- **DNI, NIE, CIF/NIF** (con validación de letra/dígito de control).
- **IBAN / cuentas** (con validación módulo 97).
- **Teléfonos**, **emails**, **matrículas** de vehículo.
- **Direcciones** postales, **nº de expediente/procedimiento**, **nº de protocolo**.
- **Topónimos** (ciudades, provincias) → etiqueta `Lugar N`.

Los **organismos públicos** (juzgados, AEAT, registros, notarías, colegios…) se
**mantienen visibles** a propósito: no son datos confidenciales del cliente y dan
contexto.

**Identificadores indirectos** (NO se redactan; los cubre el vistazo humano de
10 s): fechas muy señaladas, importes singulares, datos registrales de finca,
cargos poco comunes.

## Garantías de privacidad

- **Sin red durante el procesado.** Verificado por test (`test_sin_red`): con la
  red bloqueada y el modelo cacheado, pseudonimizar un documento completa con
  éxito.
- **Sin mapa en disco.** El mapeo real↔ficticio vive en memoria y se descarta al
  terminar. No hay comando de «rehidratar» (decisión de diseño: *solo ida*).
- **Sin telemetría.** El paquete no llama a casa ni registra uso.
- **Consistencia por archivo.** Dentro de un documento, el mismo valor recibe
  siempre la misma etiqueta; cada documento tiene su mapeo independiente.

## Limitaciones conocidas (v1)

- **DOCX:** al modificar un párrafo se colapsa su formato inline (negritas/
  cursivas parciales se pierden en ESE párrafo; los párrafos sin cambios se
  conservan intactos). Cuadros de texto y SmartArt quedan fuera de alcance.
- **PDF:** se extrae el texto a `.anon.txt` (no se conserva maquetación). Un PDF
  **escaneado** (sin texto) se detecta y se avisa; **OCR fuera de alcance**.
- **Variantes de un mismo nombre:** «María González García» y «Sra. González»
  pueden recibir etiquetas distintas (no hay correferencia en v1).
- **Idioma:** español. Catalán/euskera/gallego fuera de alcance en v1.
- **Detección por modelo:** los nombres/organizaciones dependen de
  `es_core_news_lg`; el recall es muy alto pero no perfecto. De ahí el principio
  «mejor sobre-redactar» y el vistazo humano de 10 s antes de subir.

## Desarrollo

```bash
uv venv --python 3.11
uv pip install -e ".[dev]"
python -m spacy download es_core_news_lg     # o: pseudonimizar preparar
python scripts/crear_gold.py                 # genera el gold-set anotado
pytest                                        # 157 pruebas
python scripts/medir.py                       # tabla de precision/recall vs gold
ruff check src tests scripts
```

Arquitectura (módulos en `src/pseudonimizar/`): `cli` (typer) · `engine`
(análisis → solapes → sustitución) · `recognizers_es` (regex deterministas) ·
`nlp` (spaCy + Presidio) · `mapping` (etiquetas consistentes) · `docx_io` /
`pdf_io` (E/S) · `modelo` (bootstrap del modelo).

## Licencia

[MIT](LICENSE) · © 2026 Triari Partners
