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.
Files changed (72) hide show
  1. {deidkit-0.1.2/src/deidkit.egg-info → deidkit-0.1.3}/PKG-INFO +13 -1
  2. {deidkit-0.1.2 → deidkit-0.1.3}/README.md +12 -0
  3. {deidkit-0.1.2 → deidkit-0.1.3}/pyproject.toml +1 -1
  4. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/cli.py +30 -0
  5. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/spacy_ner.py +10 -0
  6. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/version.py +1 -1
  7. {deidkit-0.1.2 → deidkit-0.1.3/src/deidkit.egg-info}/PKG-INFO +13 -1
  8. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/SOURCES.txt +1 -0
  9. deidkit-0.1.3/tests/test_ner_model_helpers.py +30 -0
  10. {deidkit-0.1.2 → deidkit-0.1.3}/LICENSE +0 -0
  11. {deidkit-0.1.2 → deidkit-0.1.3}/MANIFEST.in +0 -0
  12. {deidkit-0.1.2 → deidkit-0.1.3}/examples/benchmark.py +0 -0
  13. {deidkit-0.1.2 → deidkit-0.1.3}/examples/make_sample.py +0 -0
  14. {deidkit-0.1.2 → deidkit-0.1.3}/examples/policy.example.yaml +0 -0
  15. {deidkit-0.1.2 → deidkit-0.1.3}/examples/policy.meridian.yaml +0 -0
  16. {deidkit-0.1.2 → deidkit-0.1.3}/examples/quickstart.md +0 -0
  17. {deidkit-0.1.2 → deidkit-0.1.3}/setup.cfg +0 -0
  18. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/__init__.py +0 -0
  19. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/__main__.py +0 -0
  20. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/api.py +0 -0
  21. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/config.py +0 -0
  22. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/NOTICES.md +0 -0
  23. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/context_triggers_en.txt +0 -0
  24. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/context_triggers_es.txt +0 -0
  25. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/en_given_names.txt +0 -0
  26. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/en_surnames.txt +0 -0
  27. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/es_given_names.txt +0 -0
  28. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/es_surnames.txt +0 -0
  29. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/honorifics_en.txt +0 -0
  30. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/honorifics_es.txt +0 -0
  31. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_stoplist_en.txt +0 -0
  32. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_stoplist_es.txt +0 -0
  33. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/data/medical_vocab.txt +0 -0
  34. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/dates.py +0 -0
  35. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/__init__.py +0 -0
  36. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/checksums.py +0 -0
  37. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/context.py +0 -0
  38. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/gazetteer.py +0 -0
  39. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/patterns.py +0 -0
  40. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/pipeline.py +0 -0
  41. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/detect/types.py +0 -0
  42. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/engine.py +0 -0
  43. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/__init__.py +0 -0
  44. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/names.py +0 -0
  45. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/generators/surrogates.py +0 -0
  46. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/io.py +0 -0
  47. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/learn.py +0 -0
  48. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/mapping.py +0 -0
  49. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/report.py +0 -0
  50. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/resources.py +0 -0
  51. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/schema.py +0 -0
  52. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/secret.py +0 -0
  53. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit/textnorm.py +0 -0
  54. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/dependency_links.txt +0 -0
  55. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/entry_points.txt +0 -0
  56. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/requires.txt +0 -0
  57. {deidkit-0.1.2 → deidkit-0.1.3}/src/deidkit.egg-info/top_level.txt +0 -0
  58. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_api.py +0 -0
  59. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_checksums.py +0 -0
  60. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_dates.py +0 -0
  61. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_db.py +0 -0
  62. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_detect.py +0 -0
  63. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_engine.py +0 -0
  64. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_hardening.py +0 -0
  65. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_known_phi.py +0 -0
  66. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_learn.py +0 -0
  67. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_medical_vocab.py +0 -0
  68. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_names.py +0 -0
  69. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_fixes.py +0 -0
  70. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_freetext_leaks.py +0 -0
  71. {deidkit-0.1.2 → deidkit-0.1.3}/tests/test_qa_hardening.py +0 -0
  72. {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.2
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.2"
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,3 +1,3 @@
1
1
  """Single source of truth for the package version."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deidkit
3
- Version: 0.1.2
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
@@ -63,6 +63,7 @@ tests/test_known_phi.py
63
63
  tests/test_learn.py
64
64
  tests/test_medical_vocab.py
65
65
  tests/test_names.py
66
+ tests/test_ner_model_helpers.py
66
67
  tests/test_qa_fixes.py
67
68
  tests/test_qa_freetext_leaks.py
68
69
  tests/test_qa_hardening.py
@@ -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