- add 3 signal pitch assets (workflow flowchart, market opportunity, win-win-win) as HTML + PNG - fix doc_state_machine cascade and supplied sentinel flags - fix CSVImport mapping review, WorklistTable colored bullets and expand row - fix App.jsx auth gate, api.js CORS handling - add signal E2E test script and stress test generator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
202 lines
7 KiB
Python
202 lines
7 KiB
Python
"""
|
|
doc_state_machine.py
|
|
Signal — STTIL Solutions
|
|
|
|
Computes per-patient documentation status across 5 items.
|
|
Requirements are payer-dependent. Produces cascade consequence chain.
|
|
|
|
PHI Contract: no patient names, DOBs, or contact info in any parameter.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import date
|
|
from typing import Optional
|
|
|
|
|
|
# Payer types that require PECOS enrollment
|
|
_PECOS_REQUIRED = {"medicare", "medicare_advantage"}
|
|
|
|
# Payer types that require Prior Authorization for CGM
|
|
_PA_REQUIRED = {"medicare_advantage", "medicaid", "commercial"}
|
|
|
|
# Medicare FFS: PA not required (CGM coverage expansion 2023 + CMS-1828-F exemption path)
|
|
_PA_NOT_REQUIRED = {"medicare"}
|
|
|
|
|
|
@dataclass
|
|
class DocState:
|
|
swo: str = "Pending" # "On File" | "Pending" | "Expired"
|
|
visit: str = "Not Confirmed" # "Confirmed YYYY-MM-DD" | "Estimated YYYY-MM-DD" | "Not Confirmed"
|
|
pecos: str = "Not Verified" # "Verified" | "Not Verified" | "N/A" | "Pending — Verify"
|
|
pa: str = "Required — Not Started" # "Not Required" | "Approved" | "Pending" | "Denied" | "Required — Not Started" | "Pending — Verify"
|
|
diagnosis: str = "Missing" # "On File" | "Missing" | "Pending — Verify"
|
|
cascade: list = field(default_factory=list)
|
|
|
|
|
|
def _resolve_visit_display(
|
|
csv_visit_date: Optional[date],
|
|
confirmed_visit_date: Optional[date],
|
|
is_transfer: bool,
|
|
) -> tuple[str, bool]:
|
|
"""
|
|
Returns (display_string, is_confirmed).
|
|
is_confirmed=True means the date is reliable (from CSV or staff-confirmed).
|
|
"""
|
|
if is_transfer:
|
|
return "Pending — Verify", False
|
|
if csv_visit_date:
|
|
return f"Confirmed {csv_visit_date.isoformat()}", True
|
|
if confirmed_visit_date:
|
|
return f"Confirmed {confirmed_visit_date.isoformat()}", True
|
|
return "Not Confirmed — Estimated Only", False
|
|
|
|
|
|
def _build_cascade(
|
|
swo: str,
|
|
visit_confirmed: bool,
|
|
pecos: str,
|
|
pa: str,
|
|
diagnosis: str,
|
|
payer_type: str,
|
|
swo_supplied: bool = True,
|
|
visit_supplied: bool = True,
|
|
diagnosis_supplied: bool = True,
|
|
pecos_supplied: bool = True,
|
|
pa_supplied: bool = True,
|
|
) -> list:
|
|
"""
|
|
Build the full consequence chain for all documentation gaps.
|
|
Only flags gaps for columns that were actually present in the CSV.
|
|
An absent column is "not evaluated", not a gap — green means no known blocker.
|
|
Type A = doc gaps Signal can help close.
|
|
Type B = route-to-action alerts for denials.
|
|
"""
|
|
cascade = []
|
|
|
|
# Type A: Documentation gaps — only fire when the column was supplied
|
|
if visit_supplied and not visit_confirmed:
|
|
cascade.append(
|
|
"Qualifying visit not on file — confirm with prescriber before scheduling shipment"
|
|
)
|
|
|
|
if swo_supplied and swo in ("Pending", "Expired"):
|
|
label = "not on file" if swo == "Pending" else "expired"
|
|
cascade.append(
|
|
f"Written order {label} — obtain from prescriber before claim submission"
|
|
)
|
|
|
|
if diagnosis_supplied and diagnosis == "Missing":
|
|
cascade.append(
|
|
"Diagnosis code not on file — obtain from prescriber records before billing"
|
|
)
|
|
|
|
if pecos_supplied and payer_type in _PECOS_REQUIRED and pecos == "Not Verified":
|
|
cascade.append(
|
|
"Ordering provider PECOS enrollment not verified — confirm NPI is active before claim submission"
|
|
)
|
|
|
|
if pa_supplied and payer_type not in _PA_NOT_REQUIRED and pa == "Required — Not Started":
|
|
cascade.append(
|
|
"Prior authorization not started — obtain before scheduling shipment"
|
|
)
|
|
|
|
# Type B: Route-to-action alert — PA denied is always surfaced (supplied-and-bad)
|
|
if pa == "Denied":
|
|
cascade.append(
|
|
"Prior authorization denied — file Level 1 appeal within 65 days. "
|
|
"Approximately 80% of first-level Medicare Advantage appeals succeed."
|
|
)
|
|
|
|
return cascade
|
|
|
|
|
|
def compute_doc_state(
|
|
payer_type: str,
|
|
csv_swo_status: Optional[str],
|
|
csv_visit_date: Optional[date],
|
|
confirmed_visit_date: Optional[date],
|
|
csv_pecos_verified: Optional[str],
|
|
csv_pa_status: Optional[str],
|
|
csv_diagnosis_on_file: Optional[str],
|
|
is_transfer: bool = False,
|
|
) -> DocState:
|
|
"""
|
|
Compute the full documentation state for one patient.
|
|
|
|
payer_type: "medicare" | "medicare_advantage" | "medicaid" | "commercial"
|
|
All csv_* fields are Optional — None means not present in the CSV.
|
|
"""
|
|
|
|
# Transfer patients: everything is pending verification
|
|
if is_transfer:
|
|
return DocState(
|
|
swo="Pending — Verify",
|
|
visit="Pending — Verify",
|
|
pecos="Pending — Verify",
|
|
pa="Pending — Verify",
|
|
diagnosis="Pending — Verify",
|
|
cascade=[
|
|
"Patient transferred from prior supplier",
|
|
"Verify: SWO from current prescriber, qualifying visit within 6 months, PA status with current payer",
|
|
],
|
|
)
|
|
|
|
# SWO
|
|
swo_map = {"on file": "On File", "pending": "Pending", "expired": "Expired"}
|
|
swo = swo_map.get((csv_swo_status or "").strip().lower(), "Pending")
|
|
|
|
# Visit
|
|
visit_display, visit_confirmed = _resolve_visit_display(
|
|
csv_visit_date, confirmed_visit_date, is_transfer
|
|
)
|
|
|
|
# PECOS — only required for Medicare
|
|
if payer_type in _PECOS_REQUIRED:
|
|
pecos_val = (csv_pecos_verified or "").strip().lower()
|
|
pecos = "Verified" if pecos_val in ("yes", "verified", "true", "1") else "Not Verified"
|
|
else:
|
|
pecos = "N/A"
|
|
|
|
# PA — not required for Medicare FFS; required for MA, Medicaid, Commercial
|
|
if payer_type in _PA_NOT_REQUIRED:
|
|
pa = "Not Required"
|
|
elif csv_pa_status:
|
|
pa_map = {
|
|
"approved": "Approved",
|
|
"pending": "Pending",
|
|
"denied": "Denied",
|
|
"not required": "Not Required",
|
|
"not started": "Required — Not Started",
|
|
}
|
|
pa_key = csv_pa_status.strip().lower()
|
|
pa = pa_map.get(pa_key, "Required — Not Started")
|
|
else:
|
|
pa = "Required — Not Started"
|
|
|
|
# Diagnosis
|
|
dx_val = (csv_diagnosis_on_file or "").strip().lower()
|
|
diagnosis = "On File" if dx_val in ("yes", "true", "on file", "1") else "Missing"
|
|
|
|
swo_supplied = csv_swo_status is not None
|
|
visit_supplied = (csv_visit_date is not None) or (confirmed_visit_date is not None)
|
|
diagnosis_supplied = csv_diagnosis_on_file is not None
|
|
pecos_supplied = csv_pecos_verified is not None
|
|
pa_supplied = csv_pa_status is not None
|
|
|
|
cascade = _build_cascade(
|
|
swo, visit_confirmed, pecos, pa, diagnosis, payer_type,
|
|
swo_supplied=swo_supplied,
|
|
visit_supplied=visit_supplied,
|
|
diagnosis_supplied=diagnosis_supplied,
|
|
pecos_supplied=pecos_supplied,
|
|
pa_supplied=pa_supplied,
|
|
)
|
|
|
|
return DocState(
|
|
swo=swo,
|
|
visit=visit_display,
|
|
pecos=pecos,
|
|
pa=pa,
|
|
diagnosis=diagnosis,
|
|
cascade=cascade,
|
|
)
|