"""Align UI encounter types to specialty Schedule listings by care setting. Specialty **26 (Paediatrics)** and **00 (Family Practice)** use different Consultations and Visits sections. Encounter labels map to letter-prefix families plus BM25 steering keywords for that specialty. """ from __future__ import annotations from .specialty_registry import normalize_specialty_code # Ordered (substring -> (prefixes, keywords)) rules. First match wins — more # specific Paediatrics phrases must precede generic ones. _PAEDS_RULES: list[tuple[tuple[str, ...], tuple[list[str], str]]] = [ # --- Newborn & delivery (H261 / H267 live in PAEDIATRICS (26)) --- ( ("maternal delivery", "attendance at maternal", "delivery attendance"), (["H"], "attendance at maternal delivery H267 Paediatrics"), ), ( ("newborn",), (["H"], "newborn care in hospital or home H261 Paediatrics"), ), # --- Prenatal (before hospital so "Hospital - Prenatal …" keeps A+C) --- # SoB PAEDIATRICS (26) lists A665 with the same-section alternatives below; # keep them in-scope so limited/repeat/MSA can be recommended when prenatal # elements are not met. ( ("prenatal",), ( ["A", "C"], "prenatal consultation A665 C665 Paediatrics " "limited consultation A565 repeat consultation A266 " "medical specific assessment A263 A264 A661 " "level 1 level 2 paediatric assessment A261 A262", ), ), # --- Long-term care (must precede generic 'complex') --- ( ("long-term", "long term", "continuing care", "ltc"), ( ["W"], "long-term care paediatric consultation subsequent visit " "periodic health visit", ), ), # --- Hospital in-patient (C-series Paediatrics mirrors) --- # Before developmental so "Hospital - … Neurodevelopmental" stays C. ( ("subsequent", "concurrent", "daily round"), ( ["C"], "hospital in-patient subsequent visit concurrent care " "medical specific re-assessment", ), ), ( ("hospital", "admission", "inpatient", "in-patient", "mrp"), ( ["C"], "hospital in-patient paediatric consultation medical specific " "assessment concurrent care neurodevelopmental", ), ), # --- Ambulatory consultations (before developmental; "neurodevelopmental # consultation" is an A/C/W consult listing, not K developmental care) --- ( ("well-baby", "well baby", "18 month", "18-month", "enhanced 18"), ( ["A"], "enhanced 18 month well baby visit A268 Paediatrics", ), ), ( ( "special paediatric", "extended special", "neurodevelopmental consultation", "special / extended", "consultation / limited", "specialist consultation", "consultation", ), ( ["A"], "paediatric consultation limited repeat special extended " "neurodevelopmental A260 A265 A565 A266 A662 A667 A695", ), ), ( ("medical specific", "level 2", "general assessment"), ( ["A"], "level 2 paediatric assessment medical specific assessment " "re-assessment A262 A263 A264 A661", ), ), ( ("level 1", "partial", "recheck", "minor"), ( ["A"], "level 1 paediatric assessment A261", ), ), # --- Developmental / behavioural care (K119 / K122 / K123) --- # Narrow needles so "neurodevelopmental consultation" stays on A/C/W. ( ( "developmental -", "behavioural care", "behavioral care", "developmental care", "developmental assessment incentive", "family developmental", "individual developmental", ), ( ["K", "A"], "paediatric developmental behavioural care K119 K122 K123 " "neurodevelopmental", ), ), # --- Interprofessional / case conference --- ( ( "case conference", "multi-disciplinary", "multidisciplinary", "inter-professional", "interprofessional", ), ( ["K"], "developmental behavioural care case conference " "interprofessional special services", ), ), # --- Virtual: audio/telephone before generic "virtual" --- ( ("audio", "telephone"), ( ["A"], "office paediatric assessment consultation virtual telephone", ), ), ( ("virtual", "video"), (["A"], "office paediatric assessment consultation virtual video"), ), ( ("ambulatory", "office", "outpatient"), ( ["A"], "paediatric assessment consultation well baby office ambulatory", ), ), # --- Emergency: Paediatrics (26) only lists newborn/delivery under H --- ( ("emergency",), ( ["H", "A"], "paediatric assessment consultation emergency urgent", ), ), ] # Family Practice (00) — A001/A007/A003 ladder + hospital/LTC/K017. _FP_RULES: list[tuple[tuple[str, ...], tuple[list[str], str]]] = [ ( ("periodic health", "annual health", "complete physical"), ( ["K", "A"], "periodic health visit K017 Family Practice office", ), ), ( ("long-term", "long term", "continuing care", "ltc", "nursing home"), ( ["W"], "long-term care subsequent visit periodic health W001 W002 W003 " "W004 W109 Family Practice", ), ), ( ("subsequent", "concurrent", "supportive", "daily round"), ( ["C"], "hospital in-patient subsequent visit concurrent care supportive " "care C002 C007 C008 C010 Family Practice", ), ), ( ("hospital", "admission", "inpatient", "in-patient", "mrp"), ( ["C"], "hospital in-patient general assessment consultation " "C003 C004 C005 C006 Family Practice", ), ), # Intermediate before well-baby so "Intermediate assessment / well baby care" # steers to A007; pure enhanced-18 / well-baby labels still hit the next rule. ( ("intermediate",), ( ["A"], "intermediate assessment A007 Family Practice", ), ), ( ("well-baby", "well baby", "18 month", "18-month", "enhanced 18"), ( ["A"], "well baby care A007 enhanced 18 month well baby visit A002 " "Family Practice", ), ), # Ambulatory consult → office A only (hospital C mirrors stay on Hospital rules). ( ("repeat consultation", "repeat consult"), ( ["A"], "repeat consultation A006 Family Practice office", ), ), ( ("consultation", "consult"), ( ["A"], "consultation repeat consultation A006 Family Practice office", ), ), ( ("general assessment", "complete assessment", "comprehensive"), ( ["A"], "general assessment A003 Family Practice", ), ), ( ("re-assessment", "reassessment", "general re"), ( ["A"], "general re-assessment A004 Family Practice", ), ), ( ("mini",), ( ["A"], "mini assessment A008 Family Practice", ), ), ( ("minor", "brief", "focused", "single complaint"), ( ["A"], "minor assessment A001 Family Practice", ), ), ( ("audio", "telephone", "virtual", "video"), ( ["A"], "office assessment Family Practice virtual visit A001 A007 A003", ), ), ( ("ambulatory", "office", "outpatient", "clinic"), ( ["A"], "office assessment Family Practice A001 A007 A003 A004 A008", ), ), ( ("emergency", "urgent"), ( ["A", "H"], "office assessment urgent Family Practice A001 A007", ), ), ] def align_encounter( encounter_type: str | None, specialty_code: str | None = None, ) -> tuple[list[str], str]: """Return (preferred code prefixes, specialty steering keywords).""" if not encounter_type: return [], "" et = encounter_type.lower() code = normalize_specialty_code(specialty_code) rules = _FP_RULES if code == "00" else _PAEDS_RULES for needles, result in rules: if any(n in et for n in needles): return list(result[0]), result[1] return [], "" def prefix_filter(prefixes: list[str]) -> list[dict]: """OpenSearch filter clause restricting billing_code to the given prefixes.""" if not prefixes: return [] return [ { "bool": { "should": [{"prefix": {"billing_code": p}} for p in prefixes], "minimum_should_match": 1, } } ]