Chaeyoon Claude Opus 4.8 commited on
Commit
9ee0f17
·
1 Parent(s): 28fea0d

Rename output/ -> outputs/; add docs/CHANGELOG.md

Browse files

- Generated artifacts now write to outputs/ (data/raw = inputs stay under data/;
the old data/out is legacy and gitignored).
- Update all path refs: src/trust_demo.py, streamlit_app.py, tests/run_eval.py,
.gitignore, README.md, CLAUDE.md.
- Add docs/CHANGELOG.md documenting big changes per PR since the fork's initial commit.

Verified: ruff clean; 23 tests pass; run_eval writes outputs/results.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

.gitignore CHANGED
@@ -1,8 +1,8 @@
1
  # NHS PHI safety: raw data, generated output, and the re-identification vault NEVER leave the repo
2
  data/*
3
  !data/.gitkeep
4
- output/*
5
- !output/.gitkeep
6
  *.vault.json
7
 
8
  # Python / packaging
 
1
  # NHS PHI safety: raw data, generated output, and the re-identification vault NEVER leave the repo
2
  data/*
3
  !data/.gitkeep
4
+ outputs/*
5
+ !outputs/.gitkeep
6
  *.vault.json
7
 
8
  # Python / packaging
CLAUDE.md CHANGED
@@ -9,8 +9,8 @@ data leaves a Trust. Encode Club "Trusted Data & AI Infrastructure" hackathon; f
9
  python -m venv .venv; .\.venv\Scripts\Activate.ps1
10
  pip install -e ".[app,dev]"; python -m spacy download en_core_web_lg
11
 
12
- python tests/run_eval.py --compare --limit 300 # VERIFIABLE SIGNAL: rules vs presidio+rules -> output/results.json
13
- python -m src.trust_demo # two NHS Trusts share only de-identified data -> output/
14
  streamlit run streamlit_app.py # demo (Try-it / Metrics / Governance / Two-Trust)
15
  python -m pytest tests/ -v
16
 
@@ -29,7 +29,7 @@ python -m pytest tests/ -v
29
  WITHOUT spaCy/Presidio (the fallback path). snake_case / PascalCase.
30
 
31
  ## Data rules (treat the synthetic notes as if real NHS PHI)
32
- - `data/raw/`, `output/`, and any vault export are gitignored — never commit. Never paste note text
33
  into prompts; point at file paths.
34
  - The note→patient join (`src/data.py` ground truth) is the EVAL-ONLY oracle. It must NEVER feed
35
  detection/transform — that is data leakage and invalidates the metric.
 
9
  python -m venv .venv; .\.venv\Scripts\Activate.ps1
10
  pip install -e ".[app,dev]"; python -m spacy download en_core_web_lg
11
 
12
+ python tests/run_eval.py --compare --limit 300 # VERIFIABLE SIGNAL: rules vs presidio+rules -> outputs/results.json
13
+ python -m src.trust_demo # two NHS Trusts share only de-identified data -> outputs/
14
  streamlit run streamlit_app.py # demo (Try-it / Metrics / Governance / Two-Trust)
15
  python -m pytest tests/ -v
16
 
 
29
  WITHOUT spaCy/Presidio (the fallback path). snake_case / PascalCase.
30
 
31
  ## Data rules (treat the synthetic notes as if real NHS PHI)
32
+ - `data/raw/`, `outputs/`, and any vault export are gitignored — never commit. Never paste note text
33
  into prompts; point at file paths.
34
  - The note→patient join (`src/data.py` ground truth) is the EVAL-ONLY oracle. It must NEVER feed
35
  detection/transform — that is data leakage and invalidates the metric.
README.md CHANGED
@@ -88,7 +88,7 @@ src/ the package
88
  tests/ unit tests + run_eval.py (the evaluation CLI)
89
  docs/ tool card + project docs
90
  data/ input CSVs (gitignored)
91
- output/ generated artifacts: results.json, manifests (gitignored)
92
  streamlit_app.py the demo UI pyproject.toml packaging + lint/test config
93
  ```
94
 
@@ -113,8 +113,8 @@ pip install -e ".[app,dev]"
113
  python -m spacy download en_core_web_lg # or en_core_web_sm for a faster, lighter run
114
 
115
  # 3) run
116
- python tests/run_eval.py --compare --limit 300 # reproduce the table -> output/results.json
117
- python -m src.trust_demo # two NHS Trusts share only de-identified data -> output/
118
  streamlit run streamlit_app.py # demo: Try-it · Metrics · Governance · Two-Trust
119
  pytest -q # unit tests
120
  ```
 
88
  tests/ unit tests + run_eval.py (the evaluation CLI)
89
  docs/ tool card + project docs
90
  data/ input CSVs (gitignored)
91
+ outputs/ generated artifacts: results.json, manifests (gitignored)
92
  streamlit_app.py the demo UI pyproject.toml packaging + lint/test config
93
  ```
94
 
 
113
  python -m spacy download en_core_web_lg # or en_core_web_sm for a faster, lighter run
114
 
115
  # 3) run
116
+ python tests/run_eval.py --compare --limit 300 # reproduce the table -> outputs/results.json
117
+ python -m src.trust_demo # two NHS Trusts share only de-identified data -> outputs/
118
  streamlit run streamlit_app.py # demo: Try-it · Metrics · Governance · Two-Trust
119
  pytest -q # unit tests
120
  ```
docs/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ Big changes since the fork of [`NoteGuard/Automatic-PII-preprocessing-tool`](https://github.com/NoteGuard/Automatic-PII-preprocessing-tool),
4
+ grouped by pull request / milestone (newest first). Encode Club "Trusted Data & AI Infrastructure" hackathon.
5
+
6
+ ## Gold-RAP restructure & Hugging Face deploy — 2026-06-20
7
+ Branches `dev/refactor-cleancode` + `feat/hf-spaces-demo`, merged to `main`.
8
+ - Reorganised to a Gold-RAP "analysis as a product" layout: `src/` package, `tests/`, `docs/`,
9
+ `data/` (inputs), `outputs/` (generated artifacts).
10
+ - Renamed package `noteguard/` → `src/`; British spelling throughout (`recognisers`).
11
+ - Packaged via `pyproject.toml` as the single dependency source (removed `requirements.txt`).
12
+ - CI (`.github/workflows/ci.yml`) runs `ruff` + `pytest` on every push/PR; added logging.
13
+ - Streamlit demo moved to repo root (`streamlit_app.py`); `run_eval.py` moved under `tests/`.
14
+ - Hugging Face Spaces (Docker SDK) deploy: `Dockerfile`, README front-matter, `DEPLOY_HF_SPACES.md`.
15
+ - Decluttered Claude tooling (eval skill, post-edit hook, local settings) and the root changelog.
16
+
17
+ ## Post-PR #3 hardening — 2026-06-20
18
+ Direct fixes on `main` after the team-briefing work.
19
+ - Only **date-of-birth** is masked; visit / admission dates are kept (clinically useful). Human-readable
20
+ redaction labels (e.g. `[NHS number]`).
21
+ - Fixed pseudonym mis-mapping caused by spaCy ORGANIZATION over-tagging.
22
+ - Removed the roster / gazetteer feature (kept the metric honest — no circularity).
23
+ - Consolidated the frontend on Streamlit; removed the Gradio demo.
24
+
25
+ ## PR #3 — Action items 1–4 from the team briefing — 2026-06-20
26
+ - Default spaCy model `en_core_web_lg` (best UK-name recall; clinical transformer tested and rejected).
27
+ - **Human-in-the-loop review queue**: low-confidence spans are still redacted *and* flagged for IG
28
+ analyst confirmation before SDE-pool admission.
29
+ - ORGANIZATION excluded from Presidio's kept entities (it over-tagged labels like "NHS"/"GMC"); NHS
30
+ site names caught by a LOCATION rule instead.
31
+ - Overlap-safe, priority-ranked span merge (precise rule entities beat broad NER spans).
32
+
33
+ ## PR #2 — UK entity backport — 2026-06-20
34
+ - Added `UK_NINO`, `UK_PASSPORT`, `UK_VEHICLE_REGISTRATION` recognisers and comma-separated
35
+ NHS-number detection, with format-correct fake surrogates for each.
36
+
37
+ ## PR #1 — NoteGuard de-identification gate — 2026-06-20
38
+ Consolidated two parallel implementations into one package.
39
+ - One pluggable `Detector` interface: `RuleDetector` / `PresidioDetector`, degrading gracefully to
40
+ pure-Python rules when spaCy/Presidio are unavailable.
41
+ - Patient-consistent pseudonymisation via a Faker(en_GB) vault + per-patient date-shift; redaction mode.
42
+ - Evaluation harness: per-entity precision / recall / F1 **and** a measured residual-leakage rate,
43
+ using the dataset's structured patient/admission tables as free ground truth.
44
+ - Two-Trust "sanitise-at-source" demo; Streamlit demo UI.
45
+
46
+ ## Initial NoteGuard implementation
47
+ - Pure-Python `noteguard` package: rule recognisers (NHS Modulus-11 + context anchor, postcode, date,
48
+ phone, email), detection layer, redaction/pseudonymisation transforms, evaluation, pipeline.
49
+ - Loads the NHSE synthetic clinical notes (3 CSVs joined on `person_id`/`admission_id`).
50
+
51
+ ## Initial commit
52
+ - Fork of `NoteGuard/Automatic-PII-preprocessing-tool`.
{output → outputs}/.gitkeep RENAMED
File without changes
src/trust_demo.py CHANGED
@@ -22,7 +22,7 @@ from .evaluate import _find_all, ground_truth_spans, value_variants
22
  from .pipeline import Pipeline
23
  from .transform import PSEUDONYM, PseudonymVault
24
 
25
- OUT_DIR = Path(__file__).resolve().parent.parent / "output"
26
  TRUST_NAMES = {0: "Trust A (Northgate NHS Foundation Trust)", 1: "Trust B (Riverside NHS Trust)"}
27
 
28
 
 
22
  from .pipeline import Pipeline
23
  from .transform import PSEUDONYM, PseudonymVault
24
 
25
+ OUT_DIR = Path(__file__).resolve().parent.parent / "outputs"
26
  TRUST_NAMES = {0: "Trust A (Northgate NHS Foundation Trust)", 1: "Trust B (Riverside NHS Trust)"}
27
 
28
 
streamlit_app.py CHANGED
@@ -24,8 +24,8 @@ from src.evaluate import evaluate # noqa: E402
24
  from src.pipeline import Pipeline # noqa: E402
25
  from src.transform import PSEUDONYM, REDACTION, PseudonymVault # noqa: E402
26
 
27
- OUT_DIR = REPO / "output"
28
- RESULTS = REPO / "output" / "results.json"
29
 
30
  ENTITY_COLORS = {
31
  "PERSON": "#ffd6e0", "UK_NHS": "#ffe9b3", "DATE_TIME": "#d4f4dd", "UK_POSTCODE": "#cfe8ff",
 
24
  from src.pipeline import Pipeline # noqa: E402
25
  from src.transform import PSEUDONYM, REDACTION, PseudonymVault # noqa: E402
26
 
27
+ OUT_DIR = REPO / "outputs"
28
+ RESULTS = REPO / "outputs" / "results.json"
29
 
30
  ENTITY_COLORS = {
31
  "PERSON": "#ffd6e0", "UK_NHS": "#ffe9b3", "DATE_TIME": "#d4f4dd", "UK_POSTCODE": "#cfe8ff",
tests/run_eval.py CHANGED
@@ -4,7 +4,7 @@
4
  python tests/run_eval.py --method pseudonym # leakage under pseudonymisation
5
  python tests/run_eval.py --compare # rules-only vs presidio+rules
6
 
7
- Writes output/results.json (consumed by the Streamlit metrics panel) and prints a summary.
8
  This is the pipeline's evaluation entry point; it lives under tests/ alongside the unit tests.
9
  """
10
  from __future__ import annotations
@@ -23,7 +23,7 @@ from src.detect import RuleDetector, build_detector # noqa: E402
23
  from src.evaluate import EvalResult, evaluate # noqa: E402
24
  from src.transform import REDACTION # noqa: E402
25
 
26
- OUTPUT_DIR = REPO / "output"
27
  logger = logging.getLogger("noteguard.eval")
28
 
29
 
@@ -49,7 +49,7 @@ def main() -> None:
49
  ap.add_argument("--method", default=REDACTION, choices=["redaction", "pseudonym"])
50
  ap.add_argument("--no-presidio", action="store_true", help="rules only")
51
  ap.add_argument("--compare", action="store_true", help="rules vs presidio+rules")
52
- ap.add_argument("--out", default=None, help="output JSON path (default: output/results.json)")
53
  args = ap.parse_args()
54
 
55
  logger.info("loading notes (limit=%s) ...", args.limit)
 
4
  python tests/run_eval.py --method pseudonym # leakage under pseudonymisation
5
  python tests/run_eval.py --compare # rules-only vs presidio+rules
6
 
7
+ Writes outputs/results.json (consumed by the Streamlit metrics panel) and prints a summary.
8
  This is the pipeline's evaluation entry point; it lives under tests/ alongside the unit tests.
9
  """
10
  from __future__ import annotations
 
23
  from src.evaluate import EvalResult, evaluate # noqa: E402
24
  from src.transform import REDACTION # noqa: E402
25
 
26
+ OUTPUT_DIR = REPO / "outputs"
27
  logger = logging.getLogger("noteguard.eval")
28
 
29
 
 
49
  ap.add_argument("--method", default=REDACTION, choices=["redaction", "pseudonym"])
50
  ap.add_argument("--no-presidio", action="store_true", help="rules only")
51
  ap.add_argument("--compare", action="store_true", help="rules vs presidio+rules")
52
+ ap.add_argument("--out", default=None, help="output JSON path (default: outputs/results.json)")
53
  args = ap.parse_args()
54
 
55
  logger.info("loading notes (limit=%s) ...", args.limit)