///|
fn rule(
id : String,
label : String,
kind : PhiKind,
pattern : String,
placeholder : String,
confidence : Int,
contextual? : Bool = false,
) -> Rule {
{
id,
label,
kind,
pattern,
placeholder,
confidence,
enabled: true,
contextual,
}
}
///|
/// Built-in rules cover common PHI in Chinese and English clinical notes.
/// They intentionally favor precision over exhaustive recall.
pub fn builtin_rules() -> Array[Rule] {
[
rule(
"cn_name_context",
"Chinese contextual name",
PersonName,
"(姓名|患者|病人|联系人|家属)[:: ]{1,3}[\\u4e00-\\u9fff]{2,6}",
"[NAME]",
94,
contextual=true,
),
rule(
"en_name_context",
"English contextual name",
PersonName,
"(Name|Patient|Contact)[:: ]{1,3}[A-Z][a-z]+( [A-Z][a-z]+){0,2}",
"[NAME]",
90,
contextual=true,
),
rule(
"cn_id",
"Chinese resident identity number",
IdNumber,
"\\b[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])([0-2]\\d|3[0-1])\\d{3}[0-9Xx]\\b",
"[ID]",
98,
),
rule(
"passport",
"Passport-like identifier",
IdNumber,
"\\b([EGPS][0-9]{8}|[A-Z]{1,2}[0-9]{6,9})\\b",
"[ID]",
80,
),
rule(
"cn_mobile",
"Chinese mobile phone",
Phone,
"\\b1[3-9]\\d{9}\\b",
"[PHONE]",
96,
),
rule(
"us_phone",
"North American phone",
Phone,
"\\b\\(?[2-9][0-9]{2}\\)?[ -.]?[0-9]{3}[-.]?[0-9]{4}\\b",
"[PHONE]",
88,
),
rule(
"iso_date",
"ISO-like date",
Date,
"\\b(19|20)\\d{2}[-/.](0?[1-9]|1[0-2])[-/.]([0-2]?\\d|3[0-1])\\b",
"[DATE]",
84,
),
rule(
"cn_date",
"Chinese date",
Date,
"(19|20)\\d{2}年(0?[1-9]|1[0-2])月([0-2]?\\d|3[0-1])日",
"[DATE]",
86,
),
rule(
"email",
"Email address",
Email,
"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}",
"[EMAIL]",
95,
),
rule(
"mrn_context",
"Medical record number",
MedicalRecord,
"(住院号|门诊号|病历号|MRN|Medical Record No\\.)[:: ]{1,3}[A-Za-z0-9-]{4,24}",
"[MRN]",
92,
contextual=true,
),
rule(
"insurance_context",
"Insurance number",
Insurance,
"(医保号|社保号|Insurance ID)[:: ]{1,3}[A-Za-z0-9-]{6,32}",
"[INSURANCE]",
89,
contextual=true,
),
rule(
"hospital_context",
"Organization or hospital",
Organization,
"(医院|科室|Hospital|Department)[:: ]{1,3}[\\u4e00-\\u9fffA-Za-z0-9 .-]{2,30}",
"[ORG]",
76,
contextual=true,
),
rule(
"address_context",
"Address",
Address,
"(住址|地址|Address)[:: ]{1,3}[\\u4e00-\\u9fffA-Za-z0-9 #,.-]{4,60}",
"[ADDRESS]",
82,
contextual=true,
),
]
}
///|
pub fn enabled_rules(rules : Array[Rule]) -> Array[Rule] {
rules.filter(fn(r) { r.enabled })
}