`_stem_pattern`'s `re.I` made `[A-Z]` match any letter, so an enumeration item like "( 9) 99439 (code for non-complex chronic care management)." read as the opening of 99439's own descriptor; `descriptor_runs` then consumed the following list item as an element paragraph, and `extract_run` minted rows anchored to a paragraph naming a different code entirely. Live table already held wrong rows from this (an APCM paragraph under 99439, a TCM enumeration line under 99490). Keep the stem match case-insensitive (some rules print descriptors lowercase) but reject the cross-reference form with a negative lookahead on "code(s)". `is_element_paragraph` also rejects lines that open with an enumeration marker or contain "code(s) for" — the same enumeration items were passing its ")."-ending heuristic too. Claude-Session: https://claude.ai/code/session_01Aum3pEMAM3yQVdFSdVe6Gc
174 lines
6.9 KiB
Python
174 lines
6.9 KiB
Python
"""pfs.elements — closed element vocabulary + deterministic descriptor parse."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pfs.elements import (
|
|
VOCAB,
|
|
Element,
|
|
ElementType,
|
|
is_element_paragraph,
|
|
parse_descriptor,
|
|
slug,
|
|
)
|
|
|
|
CCM_99490 = (
|
|
"Chronic care management services, at least 20 minutes of clinical staff time "
|
|
"directed by a physician or other qualified health care professional, per calendar "
|
|
"month, with the following required elements: multiple (two or more) chronic "
|
|
"conditions expected to last at least 12 months, or until the death of the patient; "
|
|
"chronic conditions place the patient at significant risk of death, acute "
|
|
"exacerbation/decompensation, or functional decline; comprehensive care plan "
|
|
"established, implemented, revised, or monitored."
|
|
)
|
|
G2058 = (
|
|
"Chronic care management services, each additional 20 minutes of clinical staff time "
|
|
"directed by a physician or other qualified health care professional, per calendar month "
|
|
"(List separately in addition to code for primary procedure). (Use G2058 in conjunction "
|
|
"with 99490). (Do not report 99490, G2058 in the same calendar month as 99487, 99489, 99491)"
|
|
)
|
|
APCM_G0556_STEM = (
|
|
"Advanced primary care management services provided by clinical staff and directed by a "
|
|
"physician or other qualified health care professional who is responsible for all primary "
|
|
"care and serves as the continuing focal point for all needed health care services, per "
|
|
"calendar month, with the following elements, as appropriate:"
|
|
)
|
|
|
|
|
|
def _has(elements, type_, value, detail=None):
|
|
return any(
|
|
e.type == type_ and e.value == value and (detail is None or e.detail == detail)
|
|
for e in elements
|
|
)
|
|
|
|
|
|
class TestVocab:
|
|
def test_every_type_has_values(self):
|
|
for t in ElementType:
|
|
assert VOCAB[t], t
|
|
|
|
def test_values_are_slugs(self):
|
|
for values in VOCAB.values():
|
|
for v in values:
|
|
assert v == v.lower() and " " not in v, v
|
|
|
|
|
|
class TestTimeAndPeriod:
|
|
def test_first_20_minutes_per_calendar_month(self):
|
|
els = parse_descriptor(CCM_99490)
|
|
assert _has(els, ElementType.TIME, "first", "20")
|
|
assert _has(els, ElementType.PERIOD, "calendar-month")
|
|
|
|
def test_each_additional(self):
|
|
els = parse_descriptor(G2058)
|
|
assert _has(els, ElementType.TIME, "each-additional", "20")
|
|
|
|
def test_per_30_days(self):
|
|
els = parse_descriptor(
|
|
"Chronic care management, at least 20 minutes, per 30 days"
|
|
)
|
|
assert _has(els, ElementType.PERIOD, "30-days")
|
|
|
|
|
|
class TestActorAndPopulation:
|
|
def test_clinical_staff_directed(self):
|
|
els = parse_descriptor(CCM_99490)
|
|
assert _has(els, ElementType.ACTOR, "clinical-staff-directed")
|
|
|
|
def test_personally_by_physician(self):
|
|
els = parse_descriptor(
|
|
"Chronic care management services, provided personally by a physician or other "
|
|
"qualified health care professional, at least 30 minutes, per calendar month"
|
|
)
|
|
assert _has(els, ElementType.ACTOR, "physician-or-qhp-personally")
|
|
|
|
def test_two_or_more_chronic_conditions(self):
|
|
els = parse_descriptor(CCM_99490)
|
|
assert _has(
|
|
els, ElementType.POPULATION, "multiple-chronic-conditions-12-months"
|
|
)
|
|
assert _has(els, ElementType.POPULATION, "significant-risk")
|
|
|
|
|
|
class TestActivityAndRelation:
|
|
def test_care_plan_activity(self):
|
|
els = parse_descriptor(CCM_99490)
|
|
assert _has(els, ElementType.ACTIVITY, "comprehensive-care-plan")
|
|
|
|
def test_addon_and_exclusions(self):
|
|
els = parse_descriptor(G2058)
|
|
assert _has(els, ElementType.RELATION, "addon-of", "99490")
|
|
for code in ("99487", "99489", "99491"):
|
|
assert _has(els, ElementType.RELATION, "not-with", code)
|
|
assert _has(els, ElementType.BILLING, "list-separately")
|
|
|
|
def test_apcm_stem_billing_and_actor(self):
|
|
els = parse_descriptor(APCM_G0556_STEM)
|
|
assert _has(els, ElementType.ACTOR, "clinical-staff-directed")
|
|
assert _has(els, ElementType.PERIOD, "calendar-month")
|
|
assert _has(els, ElementType.BILLING, "focal-point-all-primary-care")
|
|
|
|
|
|
class TestModality:
|
|
def test_telecommunications(self):
|
|
els = parse_descriptor(
|
|
"Synchronous audio-video visit for the evaluation and management of a new patient, "
|
|
"furnished using an interactive telecommunications system"
|
|
)
|
|
assert _has(els, ElementType.MODALITY, "interactive-telecommunications")
|
|
assert _has(els, ElementType.MODALITY, "audio-video")
|
|
|
|
def test_audio_only(self):
|
|
els = parse_descriptor(
|
|
"Telephone evaluation and management service, audio-only"
|
|
)
|
|
assert _has(els, ElementType.MODALITY, "audio-only")
|
|
|
|
|
|
class TestHelpers:
|
|
def test_sorted_unique(self):
|
|
els = parse_descriptor(G2058 + " " + G2058)
|
|
assert list(els) == sorted(
|
|
set(els), key=lambda e: (e.type.value, e.value, e.detail)
|
|
)
|
|
|
|
def test_element_paragraph_heuristic(self):
|
|
assert is_element_paragraph("Consent;")
|
|
assert is_element_paragraph(
|
|
"++ Document in patient's medical record that consent was obtained."
|
|
)
|
|
assert is_element_paragraph(
|
|
"Comprehensive care plan established, implemented, revised, or monitored)."
|
|
)
|
|
assert not is_element_paragraph(
|
|
"Comment: Several commenters noted that the CPT Editorial Panel created a new code"
|
|
)
|
|
assert not is_element_paragraph(
|
|
"Response: It is our preference to use CPT codes unless Medicare has a programmatic need"
|
|
)
|
|
assert not is_element_paragraph(
|
|
"We proposed that HCPCS codes G0556 through G0558 would describe APCM services furnished per calendar month."
|
|
)
|
|
|
|
def test_slug(self):
|
|
assert slug(Element(ElementType.TIME, "first", "20")) == "time=first:20"
|
|
assert slug(Element(ElementType.ACTIVITY, "consent")) == "activity=consent"
|
|
|
|
def test_enumeration_list_items_are_not_element_paragraphs(self):
|
|
# C1: "( 9) 99439 (code for non-complex chronic care management)."
|
|
# is a cross-reference enumeration item, not a fragment of 99439's
|
|
# own descriptor, even though it ends in ")." like a real element
|
|
# line. The still-accepted lines below must keep passing.
|
|
assert not is_element_paragraph(
|
|
"( 9) 99439 (code for non-complex chronic care management)."
|
|
)
|
|
assert not is_element_paragraph(
|
|
"( 10) 99457 and 99458 (codes for remote physiologic monitoring)."
|
|
)
|
|
assert is_element_paragraph("Consent;")
|
|
assert is_element_paragraph(
|
|
"++ Document in patient's medical record that consent was obtained."
|
|
)
|
|
assert is_element_paragraph(
|
|
"Comprehensive care plan established, implemented, revised, or monitored)."
|
|
)
|