Files
stack/dev/scripts/generate_cclf_docs.py
kert f66b35f5fe reorg dev/ into scripts/ and seeds/, add PY2024 value sets
Split dev/ flat directory into dev/scripts/ (29 .py files) and
dev/seeds/ (PDFs, Excel, ZIPs, NDJSON, grafana, PY2023).
Update all Path(__file__) references to use parents[2] for project
root and dev/seeds/ for seed data. Fix path references in src/ too.

Add PY2024 CMS quality measure value sets (HWR, UAMCC, ACR) to
the REGISTRY and load into DuckDB — enables 2024->2025->2026 diffs.

Add HWR tables to notebook DIFF_SPECS now that two years exist.
2026-03-03 22:05:49 -05:00

717 lines
29 KiB
Python

"""Annotate CCLF express functions and pipe steps with IP documentation.
Reads the CCLF Information Packet PDF and injects relevant passages
from the IP directly into the docstrings of each function in
``src/aco/express/cclf.py`` and each step comment block in
``src/aco/pipe/cclf.py``.
The goal is traceability: every transformation has a citation back to
the authoritative CMS document explaining *why* the logic exists.
Usage::
python dev/scripts/generate_cclf_docs.py
Source: https://www.cms.gov/files/document/cclf-information-packet.pdf
"""
from __future__ import annotations
import re
import textwrap
from pathlib import Path
import pdfplumber
# ── PDF extraction ───────────────────────────────────────────────
_ROOT = Path(__file__).resolve().parents[2]
_SEEDS = _ROOT / "dev" / "seeds"
PDF_PATH = _SEEDS / "cclf-information-packet.pdf"
EXPRESS_PATH = _ROOT / "src" / "aco" / "express" / "cclf.py"
PIPE_PATH = _ROOT / "src" / "aco" / "pipe" / "cclf.py"
def _extract_pages(pdf_path: Path) -> dict[int, str]:
"""Extract text from every page of the PDF, keyed by 1-based page."""
pdf = pdfplumber.open(str(pdf_path))
pages = {}
for i, page in enumerate(pdf.pages):
text = page.extract_text() or ""
pages[i + 1] = text
return pages
def _extract_section(pages: dict[int, str], start_page: int, end_page: int) -> str:
"""Join page text for a range of pages."""
parts = []
for p in range(start_page, end_page + 1):
if p in pages:
parts.append(pages[p])
return "\n".join(parts)
def _extract_field_descriptions(
pages: dict[int, str],
) -> dict[str, dict[str, str]]:
"""Extract field-level descriptions from Appendix B tables.
Returns {cclf_id: {field_label: description}}.
"""
pdf = pdfplumber.open(str(PDF_PATH))
result: dict[str, dict[str, str]] = {}
current_file: str | None = None
for i in range(len(pdf.pages)):
text = pdf.pages[i].extract_text() or ""
for m in re.finditer(r"Table \d+: (.+?)\(CCLF(\w+)\)", text):
current_file = f"CCLF{m.group(2)}"
if current_file not in result:
result[current_file] = {}
tables = pdf.pages[i].extract_tables()
for table in tables:
if not table or not table[0]:
continue
header = table[0]
if not any("Element" in str(h) for h in header):
continue
for row in table[1:]:
if not row or not row[0]:
continue
elem = str(row[0]).strip()
if not elem.isdigit():
continue
label = str(row[1] or "").replace("\n", " ").strip()
label = label.replace(" ", "") # Fix PDF artifacts
desc = (
str(row[7] or "").replace("\n", " ").strip() if len(row) > 7 else ""
)
if current_file and label:
result.setdefault(current_file, {})[label.lower()] = desc
return result
# ── Documentation passages ───────────────────────────────────────
# Each entry maps a function name to the IP passage that justifies it.
# These are extracted from the PDF sections and lightly edited for
# clarity in a docstring context.
def _build_ip_passages(pages: dict[int, str]) -> dict[str, str]:
"""Build function→IP passage mapping from PDF content.
Passages are extracted once and associated with each function
based on which CCLF IP section governs that function's logic.
"""
passages: dict[str, str] = {}
# ── stg_beneficiary_xref (CCLF9) ────────────────────
passages["stg_beneficiary_xref"] = _clean("""
CCLF IP Section 3.1 "Matching MBIs" (p.14):
"A beneficiary's MBI is unique to that beneficiary but may change
over time. CCLFs 1 through 9, A, and B, as they are provided each
month, contain the beneficiary's most current MBI. When records from
multiple months are being combined, the Beneficiary XREF File (CCLF9)
can be used each month to cross reference records associated with the
current MBI to records associated with previous MBIs."
CCLF IP Section 5.1.1 "Creation of the Most Recent MBI field" (p.19):
"The beneficiary XREF file (CCLF9) contains a complete history of
MBIs ever used to identify a beneficiary, for beneficiaries who have
had a change in their MBI over time. The XREF file (CCLF9) provides
a crosswalk between all older MBIs and the most recent MBI."
CCLF9 fields used: CRNT_NUM (current MBI), PRVS_NUM (previous MBI),
PRVS_ID_EFCTV_DT (effective date of previous ID — used to pick latest).
""")
# ── stg_institutional_header (CCLF1 staging) ────────
passages["stg_institutional_header"] = _clean("""
CCLF IP Section 2.2.1 "Part A Claims Header File" (p.8-9):
"The Part A Claims Header File (CCLF1) contains header-level claim
information for institutional services covered by Part A and some
Part B. A single claim can be associated with multiple services.
The file includes provider CCN/OSCAR, beneficiary MBI, claim type,
service dates, bill type codes, DRG, payment amounts, and provider
NPIs."
MBI resolution: BENE_MBI_ID is joined to CCLF9._stg_beneficiary_xref
on PRVS_NUM to resolve to CURRENT_BENE_MBI_ID via COALESCE.
CCLF IP Section 5.3.1 "Calculating Total Part A and B Expenditures" (p.23):
"Identify the canceled claims in the Part A Header file. These claims
are identified by CLM_ADJSMT_TYPE_CD=1. Change the sign of the
variable CLM_PMT_AMT for each of these cancellation claims (i.e.,
multiply the CLM_PMT_AMT by -1)."
Fields negated on cancellation: CLM_PMT_AMT, CLM_MDCR_INSTNL_TOT_CHRG_AMT.
""")
# ── int_institutional_header_adr ─────────────────────
passages["int_institutional_header_adr"] = _clean("""
CCLF IP Section 4.4 "Debit/Credit Method" (p.19):
"The Debit/Credit method gives a full account/history of claims
processed over time. The variable CLM_ADJSMT_TYPE_CD identifies:
0 = Original Claim
1 = Cancellation Claim
2 = Adjustment Claim (adjustment to an original claim)"
CCLF IP Section 5.1.2 "Natural Key for Part A" (p.20):
Part A natural key: CLM_BLG_PRVDR_OSCAR_NUM, CLM_FROM_DT,
CLM_THRU_DT, Most Recent MBI (MR_MBI).
CCLF IP Section 5.2.1 "Related Claims in Part A Header File" (p.21-22):
"This group of claims consists of records that are all related to one
another — they all involve a claim for services provided to a
beneficiary by a single Part A provider during the same time period.
Sorting by CLM_EFCTV_DT and then by CLM_ADJSMT_TYPE_CD, yields
the final action claim as the last record."
"To identify 'final action' claims, match each cancellation claim with
an original or adjustment claim. Remove all matched pairs, yielding
only final action claim(s)."
ADR logic: rank by CUR_CLM_UNIQ_ID descending within the natural key
partition (PRVDR_OSCAR_NUM, CLM_FROM_DT, CLM_THRU_DT,
CURRENT_BENE_MBI_ID), keep rank=1 where CLM_ADJSMT_TYPE_CD != '1'.
""")
# ── stg_revenue_center (CCLF2) ──────────────────────
passages["stg_revenue_center"] = _clean("""
CCLF IP Section 2.2.2 "Part A Claim Revenue Center Detail File" (p.9):
"The Part A Claims Revenue Center Detail File (CCLF2) contains
line-item level detail for each claim from the Part A Claims Header
File. This file contains HCPCS code for each service, the date the
service was received, and for outpatient claims the payment amount
and allowed charge amount for individual services."
CCLF IP Section 3.5 "Part A Header vs Revenue Center Expenditures" (p.16):
"Both the Part A Header file (CCLF1) and the Part A Revenue Center
file (CCLF2) contain a payment field (CLM_PMT_AMT and
CLM_LINE_CVRD_PD_AMT respectively). The revenue center payment
amounts should only be relied on if they sum to the header level
payment amount."
Joined to CCLF1 header on CUR_CLM_UNIQ_ID + CURRENT_BENE_MBI_ID.
""")
# ── int_diagnosis_pivot (CCLF4) ─────────────────────
passages["int_diagnosis_pivot"] = _clean("""
CCLF IP Section 2.2.4 "Part A Diagnosis Code File" (p.9):
"The Part A Diagnosis Code File (CCLF4) contains the diagnosis code
for the principal diagnosis as well as all secondary diagnoses that
correspond with a given claim."
CCLF4 fields:
- CUR_CLM_UNIQ_ID: links back to CCLF1 claim header
- CLM_VAL_SQNC_NUM: sequence 1-25 identifying diagnosis position
- CLM_DGNS_CD: ICD-9/10 diagnosis code
- CLM_POA_IND: Present on Admission indicator
- DGNS_PRCDR_ICD_IND: '0'=ICD-10, '9'=ICD-9, 'U'=unknown
Pivots from long format (one row per diagnosis per claim) to wide
format (25 diagnosis_code_N and 25 diagnosis_poa_N columns per claim).
""")
# ── int_procedure_pivot (CCLF3) ─────────────────────
passages["int_procedure_pivot"] = _clean("""
CCLF IP Section 2.2.3 "Part A Procedure Code File" (p.9):
"The Part A Procedure Code File (CCLF3) contains detailed information
regarding claims from the Part A Claims Header File, such as the type
of surgical procedure performed and the date it was performed."
"The ICD Version Indicator is a single character denotation of whether
the code derived from ICD-9 (9) or ICD-10 (0). 'U' indicates unknown."
CCLF3 fields:
- CUR_CLM_UNIQ_ID: links back to CCLF1 claim header
- CLM_VAL_SQNC_NUM: sequence 1-25 identifying procedure position
- CLM_PRCDR_CD: ICD-9/10 procedure code
- CLM_PRCDR_PRFRM_DT: date procedure was performed
- DGNS_PRCDR_ICD_IND: '0'=ICD-10, '9'=ICD-9, 'U'=unknown
Pivots from long format to wide (25 procedure_code_N + procedure_date_N).
""")
# ── int_institutional_medical_claim ──────────────────
passages["int_institutional_medical_claim"] = _clean("""
CCLF IP Section 2.2 "Part A Claims Data" (p.8):
"Part A claim data files contain claims submitted by facilities such
as hospitals, SNFs, HHAs, rehabilitation facilities, and dialysis
facilities. These files are referred to as institutional or facility
files."
Joins four CCLF sources into the input_layer.medical_claim schema:
CCLF1 (header): claim_id, dates, bill type, DRG, payment, providers
CCLF2 (lines): line number, revenue center, HCPCS, modifiers, units
CCLF4 (dx pivot): diagnosis_code_1..25, diagnosis_poa_1..25
CCLF3 (px pivot): procedure_code_1..25, procedure_date_1..25
Key transformations:
- bill_type_code = CLM_BILL_FAC_TYPE_CD || CLM_BILL_CLSFCTN_CD
|| CLM_BILL_FREQ_CD (3-digit concatenation, IP Table 14 elements
8-9 and 29)
- drg_code = RIGHT(DGNS_DRG_CD, 3) — extract 3-digit MS-DRG from
5-character field (IP Table 14 element 17)
- diagnosis_code_type: DGNS_PRCDR_ICD_IND '0'->'icd-10-cm',
'9'->'icd-9-cm' (IP Table 14 element 30)
- procedure_code_type: same indicator mapped to 'icd-10-pcs'/'icd-9-pcs'
- claim_type = 'institutional' (all CCLF1 claims)
""")
# ── stg_physician_claim (CCLF5) ─────────────────────
passages["stg_physician_claim"] = _clean("""
CCLF IP Section 2.2.6 "Part B Physician File" (p.10):
"The Part B Physician File (CCLF5) consists of claim-line records but
includes both claim-level and line-level information. At the claim
level the file contains header level diagnosis code, disposition code,
and type of claim. At the line level the file contains provider
specialty, date of service, HCPCS code, HCPCS modifier code, payment
amount, allowed charge amount, line-level diagnosis code, units of
service, primary payer, provider TIN, and rendering NPI number."
CCLF IP Section 5.3.1 (p.23):
"Identify all the canceled records (line items) in the Part B
Physician file. The canceled line items are identified by
CLM_ADJSMT_TYPE_CD=1. Change the sign of CLM_LINE_CVRD_PD_AMT
and CLM_LINE_ALOWD_CHRG_AMT for each of these cancellation claims."
Fields negated on cancellation: CLM_LINE_CVRD_PD_AMT, CLM_LINE_ALOWD_CHRG_AMT.
""")
# ── int_physician_claim_adr ─────────────────────────
passages["int_physician_claim_adr"] = _clean("""
CCLF IP Section 5.1.2 "Natural Key for Part B Physician/DME" (p.20):
Part B natural key: CLM_CNTL_NUM, Most Recent MBI (MR_MBI).
CCLF IP Section 5.2.2 "Related Claims in the Part B Physician File" (p.22):
"In the Part B Physician File, you will find original claims,
cancellation claims, and adjustment claims."
CCLF IP Section 3.2 "Dropping Denied Claims" (p.15):
"For Part B Physician/DME claims, some individual line-items can be
denied, whereas other line-items are not denied. Part B claims need
to be dropped depending upon the value of CLM_CARR_PMT_DNL_CD, and
Part B line-items need to be dropped depending upon the value of
CLM_PRCSG_IND_CD."
ADR logic: rank by CUR_CLM_UNIQ_ID descending within the natural key
(RNDRG_PRVDR_NPI_NUM, CLM_FROM_DT, CLM_THRU_DT, CURRENT_BENE_MBI_ID),
keep rank=1 where CLM_ADJSMT_TYPE_CD != '1'.
""")
# ── int_physician_medical_claim ─────────────────────
passages["int_physician_medical_claim"] = _clean("""
CCLF IP Section 2.2.6 "Part B Physician File" (p.10):
CCLF5 contains both claim-level and line-level data in a single
file. Unlike Part A (which has separate header/line/dx/px files),
Part B has up to 12 inline diagnosis codes (CLM_DGNS_1_CD through
CLM_DGNS_12_CD) directly in each claim-line row.
Key field mappings from CCLF5 (IP Table 18):
- CUR_CLM_UNIQ_ID -> claim_id
- CLM_LINE_NUM -> claim_line_number
- CLM_POS_CD -> place_of_service_code
- CLM_LINE_HCPCS_CD -> hcpcs_code
- HCPCS_1_MDFR_CD..HCPCS_5_MDFR_CD -> hcpcs_modifier_1..5
- RNDRG_PRVDR_NPI_NUM -> rendering_npi
- CLM_RNDRG_PRVDR_TAX_NUM -> rendering_tin
- CLM_LINE_CVRD_PD_AMT -> paid_amount
- CLM_LINE_ALOWD_CHRG_AMT -> allowed_amount
- CLM_DGNS_1_CD..CLM_DGNS_12_CD -> diagnosis_code_1..12 (13-25 null)
- DGNS_PRCDR_ICD_IND -> diagnosis_code_type
- claim_type = 'professional' (all Part B physician claims)
""")
# ── stg_dme_claim (CCLF6) ──────────────────────────
passages["stg_dme_claim"] = _clean("""
CCLF IP Section 2.2.7 "Part B DME File" (p.10):
"The Part B DME File (CCLF6) consists of claim-line records but
includes both claim-level and line-level information. Claim-level
information includes date of service, disposition code, and type of
claim submitted (DMEPOS versus non-DMEPOS). Line-level information
includes date of service, HCPCS code, payment amount, allowed charge
amount, ordering NPI number, and 'paid to' NPI number."
Cancellation adjustment applied to CLM_LINE_CVRD_PD_AMT and
CLM_LINE_ALOWD_CHRG_AMT per IP Section 5.3.1.
""")
# ── int_dme_claim_adr ──────────────────────────────
passages["int_dme_claim_adr"] = _clean("""
CCLF IP Section 5.2.3 "Related Claims in the Part B DME File" (p.23):
"In the Part B DME File, you will find original claims, cancellation
claims, and adjustment claims. A variety of related claims are found
including:
1. An original claim with no other related claims.
2. A set of related claims consisting of an original claim and an
adjustment claim.
3. A set of related claims consisting of two original claims and
one cancellation claim.
4. A set of related claims consisting of three original claims
and two cancellation claims."
ADR logic mirrors Part B physician: rank within natural key partition,
keep latest non-cancelled version.
""")
# ── int_dme_medical_claim ──────────────────────────
passages["int_dme_medical_claim"] = _clean("""
CCLF IP Section 2.2.7 "Part B DME File" (p.10):
Key field mappings from CCLF6 (IP Table 19):
- CUR_CLM_UNIQ_ID -> claim_id
- CLM_LINE_NUM -> claim_line_number
- CLM_POS_CD -> place_of_service_code
- CLM_LINE_HCPCS_CD -> hcpcs_code
- CLM_LINE_CVRD_PD_AMT -> paid_amount
- CLM_LINE_ALOWD_CHRG_AMT -> allowed_amount
- ORDRG_PRVDR_NPI_NUM -> rendering_npi (ordering provider)
- CLM_BLG_PRVDR_NPI_NUM -> billing_npi
- claim_type = 'professional' (DME is classified as professional)
Note: CCLF6 does not contain diagnosis codes or procedure codes;
all 25 dx/px columns are null. Diagnosis information for DME claims
would need to be obtained through other sources.
""")
# ── medical_claim (union) ──────────────────────────
passages["medical_claim"] = _clean("""
CCLF IP Section 2.2 "Part A Claims Data" + Section 2.2.6-2.2.7 (p.8-10):
The three claim types are combined into a single medical_claim table:
- Institutional (CCLF1+2+3+4): Hospital, SNF, HHA, rehab, dialysis.
Includes Part A and some Part B services billed on institutional
claim forms. claim_type = 'institutional'.
- Professional/Physician (CCLF5): Part B services from physicians,
NPPs, and other individual practitioners. claim_type = 'professional'.
- Professional/DME (CCLF6): Durable Medical Equipment, Prosthetics,
Orthotics, and Supplies. claim_type = 'professional'.
CCLF IP Section 5.4 "Part A vs Part B Claims" (p.27):
"The Part A claims files will include Medicare provider payments for
some services covered under both Part A and Part B. To distinguish,
use the Claim Facility Type Code and Classification Code."
""")
# ── stg_pharmacy_claim (CCLF7) ─────────────────────
passages["stg_pharmacy_claim"] = _clean("""
CCLF IP Section 2.3 "Part D Claims Data" (p.10):
"The Part D File (CCLF7) contains prescription drug information at
the beneficiary level. Data elements include the NDC, quantity
dispensed, days supplied, prescribing provider ID, service provider
ID, and patient payment amount."
MBI resolution via CCLF9 crosswalk applied to BENE_MBI_ID.
""")
# ── int_pharmacy_claim_adr ─────────────────────────
passages["int_pharmacy_claim_adr"] = _clean("""
CCLF IP Section 3.3 "Part D Data Limitations" (p.15):
"The Part D claims contained in the CCLF are 'final action' claims,
unlike the other claims-related files which are debit/credit claims.
Part D cancellation claims are always submitted with a $0 payment
amount. As you create a claims record over time by combining many
monthly CCLF data feeds for Part D claims, you will need to identify
for any given set of Related Claims the most recent claim and
delete/ignore all of the previous related claims for that event."
CCLF IP Section 5.2.4 "Related Claims in the Part D File" (p.23):
"In the Part D File, you will find original claims, cancellation
claims, and adjustment claims."
ADR logic: rank by CLM_ADJSMT_TYPE_CD descending (adjustment=2 >
cancellation=1 > original=0) within CUR_CLM_UNIQ_ID partition,
keep rank=1 where type != '1'.
""")
# ── pharmacy_claim ─────────────────────────────────
passages["pharmacy_claim"] = _clean("""
CCLF IP Section 2.3 "Part D Claims Data" (p.10):
Key field mappings from CCLF7 (IP Table 20):
- CUR_CLM_UNIQ_ID -> claim_id
- CLM_LINE_NDC_CD -> ndc_code ("A universal unique product identifier
for human drugs")
- CLM_LINE_FROM_DT -> dispensing_date, paid_date
- CLM_LINE_SRVC_UNIT_QTY -> quantity
- CLM_LINE_DAYS_SUPLY_QTY -> days_supply
- CLM_LINE_RX_FILL_NUM -> refills
- CLM_LINE_BENE_PMT_AMT -> paid_amount, copayment_amount ("The dollar
amount paid by the beneficiary not reimbursed by a third party")
NPI qualification (IP Table 20 elements 6-7, 13):
- PRVDR_SRVC_ID_QLFYR_CD = '01' means NPI; '06' = UPIN, '07' = NCPDP
- CLM_SRVC_PRVDR_GNRC_ID_NUM -> dispensing_provider_npi (when = '01')
- PRVDR_PRSBNG_ID_QLFYR_CD = '01' means NPI
- CLM_PRSBNG_PRVDR_GNRC_ID_NUM -> prescribing_provider_npi (when = '01')
""")
# ── stg_beneficiary_demographics (CCLF8) ──────────
passages["stg_beneficiary_demographics"] = _clean("""
CCLF IP Section 2.4.1 "Beneficiary Demographics File" (p.10-12):
"This file contains the beneficiary's current MBI, first/middle/last
name, ZIP code, date of birth, sex, race, age, Medicare Status Code,
dual eligibility status, hospice begin/end dates, and date of death
if a decedent."
CCLF IP Section 3.1 (p.14):
MBI resolution via CCLF9 applied before deduplication to ensure a
single row per resolved beneficiary identity.
Dedup: unique on CURRENT_BENE_MBI_ID, keeping first occurrence.
""")
# ── eligibility ────────────────────────────────────
passages["eligibility"] = _clean("""
CCLF IP Section 2.4.1 "Beneficiary Demographics File" (p.10):
Key field mappings from CCLF8 (IP Table 21):
- BENE_MBI_ID (resolved) -> person_id, member_id
- BENE_SEX_CD -> gender: '0'=Unknown, '1'=Male, '2'=Female
- BENE_RACE_CD -> race: '0'=Unknown, '1'=White, '2'=Black,
'3'=Other, '4'=Asian, '5'=Hispanic, '6'=North American Native
- BENE_DOB -> birth_date
- BENE_DEATH_DT -> death_date; death_flag = 1 when not null
- BENE_RNG_BGN_DT -> enrollment_start_date ("Date beneficiary
enrolled in Hospice" — used as enrollment proxy)
- BENE_RNG_END_DT -> enrollment_end_date
- BENE_ORGNL_ENTLMT_RSN_CD -> original_reason_entitlement_code:
'0'=OASI, '1'=DIB, '2'=ESRD, '3'=DIB+ESRD
- BENE_DUAL_STUS_CD -> dual_status_code
- BENE_MDCR_STUS_CD -> medicare_status_code
- BENE_1ST_NAME, BENE_MIDL_NAME, BENE_LAST_NAME -> name fields
- BENE_LINE_1_ADR -> address
- GEO_ZIP_PLC_NAME -> city
- GEO_USPS_STATE_CD -> state
- GEO_ZIP5_CD -> zip_code
""")
return passages
def _clean(text: str) -> str:
"""Dedent and strip a passage."""
return textwrap.dedent(text).strip()
# ── Docstring injection ─────────────────────────────────────────
def _inject_express_docs(source: str, passages: dict[str, str]) -> str:
"""Inject IP passages into express function docstrings.
For each function, finds its docstring and appends the relevant
IP passage in a ``CCLF IP Reference`` section.
"""
lines = source.split("\n")
output: list[str] = []
i = 0
while i < len(lines):
line = lines[i]
output.append(line)
# Detect function definition
fn_match = re.match(r"^def (\w+)\(", line)
if fn_match:
fn_name = fn_match.group(1)
if fn_name in passages:
# Scan forward to find the closing triple-quote of
# the existing docstring
i += 1
in_docstring = False
docstring_indent = ""
triple_count = 0
while i < len(lines):
cur = lines[i]
output.append(cur)
# Count triple quotes on this line
tq = cur.count('"""')
triple_count += tq
if tq >= 1 and not in_docstring:
in_docstring = True
# Detect indentation from the opening line
m = re.match(r"^(\s+)", cur)
if m:
docstring_indent = m.group(1)
# Closing triple-quote: second occurrence
if triple_count >= 2:
# Found the closing """. Insert IP passage
# just before it.
closing_line = output.pop()
# Build the passage block
passage = passages[fn_name]
passage_lines = _format_passage(passage, docstring_indent)
output.extend(passage_lines)
output.append(closing_line)
break
i += 1
i += 1
return "\n".join(output)
def _format_passage(passage: str, indent: str) -> list[str]:
"""Format an IP passage for insertion into a docstring."""
result = [f"{indent}"] # blank line separator
result.append(f"{indent}CCLF IP Reference")
result.append(f"{indent}~~~~~~~~~~~~~~~~~~")
for line in passage.split("\n"):
if line.strip():
result.append(f"{indent}{line}")
else:
result.append(f"{indent}")
return result
def _inject_pipe_docs(source: str, passages: dict[str, str]) -> str:
"""Inject IP summary into pipe module docstring.
Adds a field-mapping reference table to the module docstring.
"""
# Build a summary section for the pipe module docstring
summary_lines = [
"",
"CCLF IP Reference for Each Step",
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
"",
"Each step below implements logic described in the CCLF",
"Information Packet (Version 41.0, 07/16/2025).",
"See ``src/aco/express/cclf.py`` for full IP citations",
"embedded in each function's docstring.",
"",
"Key IP sections governing this pipeline:",
"",
"- Section 2.2: Part A Claims Data (CCLF1-4, institutional)",
"- Section 2.2.6: Part B Physician File (CCLF5, professional)",
"- Section 2.2.7: Part B DME File (CCLF6, professional/DME)",
"- Section 2.3: Part D Claims Data (CCLF7, pharmacy)",
"- Section 2.4.1: Beneficiary Demographics (CCLF8, eligibility)",
"- Section 3.1: Matching MBIs (CCLF9 crosswalk logic)",
"- Section 3.2: Dropping Denied Claims (Part B filtering)",
"- Section 3.3: Part D Data Limitations (final-action claims)",
"- Section 3.5: Header vs Revenue Center Expenditures",
"- Section 4.4: Debit/Credit Method & Adjustment Type Codes",
"- Section 5.1: Natural Key & MBI XREF Creation",
"- Section 5.2: Related Claims Resolution (ADR by file type)",
"- Section 5.3: Expenditure Calculation (sign-change rules)",
]
# Find the closing """ of the module docstring and insert before it
lines = source.split("\n")
output: list[str] = []
triple_count = 0
inserted = False
for line in lines:
tq = line.count('"""')
triple_count += tq
# Second triple-quote closes the module docstring
if triple_count == 2 and not inserted:
# Insert summary before closing """
for sl in summary_lines:
output.append(sl)
output.append("")
inserted = True
output.append(line)
return "\n".join(output)
# ── Main ─────────────────────────────────────────────────────────
def main() -> None:
print(f"Reading {PDF_PATH.name}...")
pages = _extract_pages(PDF_PATH)
print(f" {len(pages)} pages extracted")
print("Building IP passages for each function...")
passages = _build_ip_passages(pages)
print(f" {len(passages)} functions annotated")
# ── Annotate express module ──────────────────────────
print(f"\nAnnotating {EXPRESS_PATH.name}...")
express_src = EXPRESS_PATH.read_text()
# Check which functions already have IP references
already = set()
for fn_name in passages:
pattern = rf"def {fn_name}\(.*?CCLF IP Reference"
if re.search(pattern, express_src, re.DOTALL):
already.add(fn_name)
if already:
print(
f" Skipping {len(already)} already-annotated: {', '.join(sorted(already))}"
)
passages_to_apply = {k: v for k, v in passages.items() if k not in already}
else:
passages_to_apply = passages
if passages_to_apply:
new_express = _inject_express_docs(express_src, passages_to_apply)
EXPRESS_PATH.write_text(new_express)
print(
f" Wrote {len(new_express.splitlines())} lines "
f"({len(passages_to_apply)} functions annotated)"
)
else:
print(" All functions already annotated, no changes.")
# ── Annotate pipe module ─────────────────────────────
print(f"\nAnnotating {PIPE_PATH.name}...")
pipe_src = PIPE_PATH.read_text()
if "CCLF IP Reference" in pipe_src:
print(" Already annotated, no changes.")
else:
new_pipe = _inject_pipe_docs(pipe_src, passages)
PIPE_PATH.write_text(new_pipe)
print(f" Wrote {len(new_pipe.splitlines())} lines")
print("\nDone.")
if __name__ == "__main__":
main()