Given a transcript and a list of sensitive patterns, return a redacted
transcript and a flag saying whether storage requires restricted
handling.
Hidden answer: invariant, tests, and Python solution
Invariant: each sensitive detector is validated and applied to the
original transcript before persistence, overlapping matches are
redacted as one span, and any match upgrades handling. Test no
matches, repeated matches, overlapping categories, invalid detector
labels or patterns, and transcripts that should be discarded rather
than stored.
import re
def redact_transcript(text, detectors):
spans = []
for label, pattern in detectors:
if not re.fullmatch(r"[A-Z_]+", label):
raise ValueError("detector labels must be trusted marker names")
regex = re.compile(pattern)
spans.extend((m.start(), m.end(), label) for m in regex.finditer(text))
if not spans:
return text, False
spans.sort(key=lambda span: (span[0], -span[1]))
merged = []
for start, end, label in spans:
if not merged or start > merged[-1][1]:
merged.append([start, end, {label}])
else:
merged[-1][1] = max(merged[-1][1], end)
merged[-1][2].add(label)
parts = []
cursor = 0
for start, end, labels in merged:
parts.append(text[cursor:start])
parts.append("[REDACTED_" + "_".join(sorted(labels)) + "]")
cursor = end
parts.append(text[cursor:])
return "".join(parts), True
detectors = [
("EMAIL", r"\b[\w.+-]+@[\w-]+\.[\w.-]+\b"),
("PHONE", r"\b\d{3}[-.]\d{3}[-.]\d{4}\b"),
]