///|
pub fn default_rule_pack() -> RulePack {
rule_pack(
name="clinical-default",
version="1.0.0",
locale=Mixed,
description="Deterministic Chinese-English clinical PHI rules",
rules=comprehensive_rules(),
)
}
///|
pub fn chinese_rule_pack() -> RulePack {
let rules = rules_for_locale(comprehensive_rules(), Chinese)
rule_pack(
name="clinical-zh",
version="1.0.0",
locale=Chinese,
description="Chinese clinical and insurance fields",
rules~,
)
}
///|
pub fn english_rule_pack() -> RulePack {
let rules = rules_for_locale(comprehensive_rules(), English)
rule_pack(
name="clinical-en",
version="1.0.0",
locale=English,
description="English clinical and insurance fields",
rules~,
)
}
///|
pub fn high_precision_rule_pack() -> RulePack {
let rules = rules_above_confidence(comprehensive_rules(), 90)
rule_pack(
name="clinical-high-precision",
version="1.0.0",
locale=Mixed,
description="High precision identifiers for conservative pipelines",
rules~,
)
}
///|
pub fn pack_rule_ids(pack : RulePack) -> Array[String] {
rule_ids(pack.rules)
}
///|
pub fn pack_enabled_rules(pack : RulePack) -> Array[Rule] {
enabled_rules(pack.rules)
}
///|
pub fn pack_disabled_rules(pack : RulePack) -> Array[Rule] {
pack.rules.filter(fn(item) { !item.enabled })
}
///|
pub fn pack_rule(pack : RulePack, id : String) -> Rule? {
let mut result = None
for item in pack.rules {
if item.id == id {
result = Some(item)
}
}
result
}
///|
pub fn pack_has_rule(pack : RulePack, id : String) -> Bool {
pack_rule(pack, id) is Some(_)
}
///|
pub fn pack_enable_rule(pack : RulePack, id : String) -> RulePack {
let rules = pack.rules.map(fn(item) {
if item.id == id {
{ ..item, enabled: true }
} else {
item
}
})
{ ..pack, rules, }
}
///|
pub fn pack_disable_rule(pack : RulePack, id : String) -> RulePack {
let rules = pack.rules.map(fn(item) {
if item.id == id {
{ ..item, enabled: false }
} else {
item
}
})
{ ..pack, rules, }
}
///|
pub fn pack_replace_rule(pack : RulePack, replacement : Rule) -> RulePack {
let mut replaced = false
let rules = pack.rules.map(fn(item) {
if item.id == replacement.id {
replaced = true
replacement
} else {
item
}
})
if !replaced {
rules.push(replacement)
}
{ ..pack, rules, }
}
///|
pub fn pack_filter_confidence(pack : RulePack, minimum : Int) -> RulePack {
{ ..pack, rules: pack.rules.filter(fn(item) { item.confidence >= minimum }) }
}
///|
pub fn pack_filter_kind(pack : RulePack, kind : PhiKind) -> RulePack {
{ ..pack, rules: rules_for_kind(pack.rules, kind) }
}
///|
pub fn pack_with_lifecycle(
pack : RulePack,
lifecycle : RuleLifecycle,
) -> RulePack {
{ ..pack, lifecycle, }
}
///|
pub fn pack_validate(pack : RulePack) -> Array[RuleIssue] {
validate_rules(pack.rules)
}
///|
pub fn pack_is_valid(pack : RulePack) -> Bool {
pack_validate(pack).is_empty()
}
///|
pub fn pack_summary(pack : RulePack) -> String {
[
"name=\{pack.name}",
"version=\{pack.version}",
"locale=\{pack.locale}",
"lifecycle=\{rule_lifecycle_name(pack.lifecycle)}",
"rules=\{pack.rules.length()}",
"valid=\{pack_is_valid(pack)}",
].join("\n")
}
///|
pub fn pack_to_text(pack : RulePack) -> String {
let lines = [pack.name + "@" + pack.version, pack.description]
for rule in pack.rules {
lines.push(
[
rule.id,
rule.label,
phi_kind_name(rule.kind),
"\{rule.confidence}",
"\{rule.enabled}",
rule.pattern,
].join("\t"),
)
}
lines.join("\n")
}
///|
pub fn pack_checksum(pack : RulePack) -> String {
stable_hash(pack_to_text(pack))
}
///|
pub fn select_pack(profile : Profile) -> RulePack {
match profile {
ChineseClinical => chinese_rule_pack()
EnglishClinical => english_rule_pack()
HighPrecision => high_precision_rule_pack()
AllBuiltins => default_rule_pack()
}
}
///|
pub fn profile_redaction(
input : String,
profile : Profile,
mode : ReplacementMode,
) -> DeidResult raise DeidError {
let policy = with_policy_mode(RedactionPolicy::default(), mode)
redact_with_policy(input, policy, rules=select_pack(profile).rules)
}
///|
pub fn packs_have_disjoint_ids(left : RulePack, right : RulePack) -> Bool {
!left.rules.any(fn(a) { right.rules.any(fn(b) { a.id == b.id }) })
}
///|
pub fn merge_rule_packs(left : RulePack, right : RulePack) -> RulePack {
let rules = left.rules.copy()
for rule in right.rules {
if !rules.any(fn(item) { item.id == rule.id }) {
rules.push(rule)
}
}
rule_pack(
name=left.name + "+" + right.name,
version=left.version,
locale=Mixed,
description=left.description + "; " + right.description,
rules~,
)
}