deidkit 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deidkit-0.1.2/src/deidkit.egg-info → deidkit-0.1.3}/PKG-INFO +13 -1
- {deidkit-0.1.2 → deidkit-0.1.3}/README.md +12 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/pyproject.toml +1 -1
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/cli.py +30 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/spacy_ner.py +10 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/version.py +1 -1
- {deidkit-0.1.2 → deidkit-0.1.3/src/deidkit.egg-info}/PKG-INFO +13 -1
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/SOURCES.txt +1 -0
- deidkit-0.1.3/tests/test_ner_model_helpers.py +30 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/LICENSE +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/MANIFEST.in +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/examples/benchmark.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/examples/make_sample.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/examples/policy.example.yaml +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/examples/policy.meridian.yaml +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/examples/quickstart.md +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/setup.cfg +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/__init__.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/__main__.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/api.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/config.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/NOTICES.md +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/context_triggers_en.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/context_triggers_es.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/en_given_names.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/en_surnames.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/es_given_names.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/es_surnames.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/honorifics_en.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/honorifics_es.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_stoplist_en.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_stoplist_es.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_vocab.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/dates.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/__init__.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/checksums.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/context.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/gazetteer.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/patterns.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/pipeline.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/types.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/engine.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/__init__.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/names.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/surrogates.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/io.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/learn.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/mapping.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/report.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/resources.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/schema.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/secret.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/textnorm.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/dependency_links.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/entry_points.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/requires.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/top_level.txt +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_api.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_checksums.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_dates.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_db.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_detect.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_engine.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_hardening.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_known_phi.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_learn.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_medical_vocab.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_names.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_fixes.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_freetext_leaks.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_hardening.py +0 -0
- {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_schema_lang.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deidkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Schema-driven pseudonymization for clinical/tabular datasets: synthetic names, interval-preserving date shifting, multi-stage free-text PII detection, and before/after audit exports.
|
|
5
5
|
Author: Meridian Data
|
|
6
6
|
License: MIT
|
|
@@ -420,6 +420,18 @@ extra_stoplist_files=["learned/learned_stoplist.txt"])`.
|
|
|
420
420
|
Start conservative, work the review queue down, and turn on the NER stage only if
|
|
421
421
|
you need more recall — the audit is how you prove the result.
|
|
422
422
|
|
|
423
|
+
**Enabling NER** is a one-time model install (the model is ~500 MB and ships
|
|
424
|
+
separately from the package, not bundled):
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
pip install "deidkit[ner]" # installs spaCy
|
|
428
|
+
deidkit download-model es_core_news_lg # fetches the model (once)
|
|
429
|
+
deidkit run <input> deid_out --spacy-model es_core_news_lg
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
If the model isn't installed, deidkit still runs on the deterministic rules and
|
|
433
|
+
prints a clear warning — it never fails, but free-text name recall is lower.
|
|
434
|
+
|
|
423
435
|
*In code*, the same knobs are `Policy` fields:
|
|
424
436
|
|
|
425
437
|
```python
|
|
@@ -376,6 +376,18 @@ extra_stoplist_files=["learned/learned_stoplist.txt"])`.
|
|
|
376
376
|
Start conservative, work the review queue down, and turn on the NER stage only if
|
|
377
377
|
you need more recall — the audit is how you prove the result.
|
|
378
378
|
|
|
379
|
+
**Enabling NER** is a one-time model install (the model is ~500 MB and ships
|
|
380
|
+
separately from the package, not bundled):
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
pip install "deidkit[ner]" # installs spaCy
|
|
384
|
+
deidkit download-model es_core_news_lg # fetches the model (once)
|
|
385
|
+
deidkit run <input> deid_out --spacy-model es_core_news_lg
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
If the model isn't installed, deidkit still runs on the deterministic rules and
|
|
389
|
+
prints a clear warning — it never fails, but free-text name recall is lower.
|
|
390
|
+
|
|
379
391
|
*In code*, the same knobs are `Policy` fields:
|
|
380
392
|
|
|
381
393
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deidkit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Schema-driven pseudonymization for clinical/tabular datasets: synthetic names, interval-preserving date shifting, multi-stage free-text PII detection, and before/after audit exports."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -221,6 +221,29 @@ def cmd_run_db(args) -> int:
|
|
|
221
221
|
return 0
|
|
222
222
|
|
|
223
223
|
|
|
224
|
+
def cmd_download_model(args) -> int:
|
|
225
|
+
"""Download the spaCy model the optional NER stage uses (one-time, ~500 MB)."""
|
|
226
|
+
model = args.model
|
|
227
|
+
try:
|
|
228
|
+
import spacy # noqa: F401
|
|
229
|
+
from spacy.cli import download
|
|
230
|
+
except ImportError:
|
|
231
|
+
print('error: spaCy is not installed. First run: pip install "deidkit[ner]"',
|
|
232
|
+
file=sys.stderr)
|
|
233
|
+
return 2
|
|
234
|
+
print(f"Downloading spaCy model '{model}' (~500 MB, one-time)…")
|
|
235
|
+
try:
|
|
236
|
+
download(model)
|
|
237
|
+
except SystemExit as exc: # spacy.cli.download exits non-zero on failure
|
|
238
|
+
if exc.code:
|
|
239
|
+
print(f"error: could not download '{model}'. Check the model name and "
|
|
240
|
+
f"your network.", file=sys.stderr)
|
|
241
|
+
return 2
|
|
242
|
+
print(f"Done. Enable NER with: --spacy-model {model} "
|
|
243
|
+
f"(or spacy_model='{model}' in code).")
|
|
244
|
+
return 0
|
|
245
|
+
|
|
246
|
+
|
|
224
247
|
def cmd_run(args) -> int:
|
|
225
248
|
from .api import deidentify
|
|
226
249
|
|
|
@@ -379,6 +402,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
379
402
|
_add_policy_args(pd_)
|
|
380
403
|
pd_.set_defaults(func=cmd_run_db)
|
|
381
404
|
|
|
405
|
+
pdm = sub.add_parser(
|
|
406
|
+
"download-model",
|
|
407
|
+
help="Download the spaCy NER model for the optional NER stage (needs deidkit[ner]).")
|
|
408
|
+
pdm.add_argument("model", nargs="?", default="es_core_news_lg",
|
|
409
|
+
help="spaCy model name (default es_core_news_lg).")
|
|
410
|
+
pdm.set_defaults(func=cmd_download_model)
|
|
411
|
+
|
|
382
412
|
px = sub.add_parser("scan", help="Show detections for a text (tuning/debug).")
|
|
383
413
|
g = px.add_mutually_exclusive_group()
|
|
384
414
|
g.add_argument("--text", help="Inline text to scan.")
|
|
@@ -32,6 +32,16 @@ class SpacyNER:
|
|
|
32
32
|
self.available = True
|
|
33
33
|
except Exception as exc: # ImportError or OSError (model not found)
|
|
34
34
|
self.error = str(exc)
|
|
35
|
+
import warnings
|
|
36
|
+
|
|
37
|
+
warnings.warn(
|
|
38
|
+
f"NER requested but spaCy model {model!r} is not available "
|
|
39
|
+
f"({type(exc).__name__}). The NER stage is OFF, so free-text name "
|
|
40
|
+
f"recall is reduced — names not in the gazetteer/anchored may be "
|
|
41
|
+
f"missed. Install it once with: deidkit download-model {model} "
|
|
42
|
+
f'(after: pip install "deidkit[ner]").',
|
|
43
|
+
stacklevel=3,
|
|
44
|
+
)
|
|
35
45
|
|
|
36
46
|
def find(self, text: str) -> List[Detection]:
|
|
37
47
|
if not self.available or not text:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deidkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Schema-driven pseudonymization for clinical/tabular datasets: synthetic names, interval-preserving date shifting, multi-stage free-text PII detection, and before/after audit exports.
|
|
5
5
|
Author: Meridian Data
|
|
6
6
|
License: MIT
|
|
@@ -420,6 +420,18 @@ extra_stoplist_files=["learned/learned_stoplist.txt"])`.
|
|
|
420
420
|
Start conservative, work the review queue down, and turn on the NER stage only if
|
|
421
421
|
you need more recall — the audit is how you prove the result.
|
|
422
422
|
|
|
423
|
+
**Enabling NER** is a one-time model install (the model is ~500 MB and ships
|
|
424
|
+
separately from the package, not bundled):
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
pip install "deidkit[ner]" # installs spaCy
|
|
428
|
+
deidkit download-model es_core_news_lg # fetches the model (once)
|
|
429
|
+
deidkit run <input> deid_out --spacy-model es_core_news_lg
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
If the model isn't installed, deidkit still runs on the deterministic rules and
|
|
433
|
+
prints a clear warning — it never fails, but free-text name recall is lower.
|
|
434
|
+
|
|
423
435
|
*In code*, the same knobs are `Policy` fields:
|
|
424
436
|
|
|
425
437
|
```python
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""The NER model is separate from the package (too big for PyPI); it must be a
|
|
2
|
+
one-command install, and a missing model must warn loudly (not silently reduce
|
|
3
|
+
recall). These tests cover the helper command and the warning — without ever
|
|
4
|
+
downloading a model."""
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
from deidkit.cli import build_parser, cmd_download_model
|
|
9
|
+
from deidkit.detect.spacy_ner import SpacyNER
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_missing_model_warns_and_disables():
|
|
13
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
14
|
+
warnings.simplefilter("always")
|
|
15
|
+
ner = SpacyNER("definitely_not_a_real_model_zzz")
|
|
16
|
+
assert ner.available is False
|
|
17
|
+
assert ner.find("Paciente Juan Perez consulta.") == [] # degrades, no crash
|
|
18
|
+
msgs = " ".join(str(w.message) for w in caught)
|
|
19
|
+
assert "download-model" in msgs and "NER" in msgs # loud + actionable
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_cli_wires_download_model_with_default():
|
|
23
|
+
args = build_parser().parse_args(["download-model"])
|
|
24
|
+
assert args.func is cmd_download_model
|
|
25
|
+
assert args.model == "es_core_news_lg"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_cli_download_model_accepts_explicit_name():
|
|
29
|
+
args = build_parser().parse_args(["download-model", "en_core_web_sm"])
|
|
30
|
+
assert args.model == "en_core_web_sm"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|