""" rollup.py Signal — STTIL Solutions Patient-level rollup of coverage-line readiness verdicts (build plan 02, P3). Design locked in readiness-model-brief section 4 and the phase-2 design Item 3: - The light = worst gradeable line. Severity (worst first): Plan Type Needed > At Risk > Action Needed > On Track > Clear to Ship. - Any gradeable Plan Type Needed line caps the patient no greener than Action Needed (the patient IS being served; one unmapped plan type must not read as the whole patient being ungradeable). - Non-gradeable lines (non-CGM) never participate in the light or the counts. - Tabs count patients, not rows. A patient with no graded gradeable lines has rollup_status None — the UI treatment is an open Kisa question; no label is invented here. PHI CONTRACT: operates on patient_id only; this module logs nothing. """ from __future__ import annotations from collections import defaultdict from dataclasses import dataclass from typing import Optional from core.dedup import CoverageLine from core.readiness import LineStatus # Higher = worse. Plan Type Needed ranks worst for LINE severity ordering; # its patient-level effect is the capping rule below, not raw worstness. SEVERITY = { LineStatus.CLEAR_TO_SHIP: 0, LineStatus.ON_TRACK: 1, LineStatus.ACTION_NEEDED: 2, LineStatus.AT_RISK: 3, LineStatus.PLAN_TYPE_NEEDED: 4, } @dataclass class PatientRollup: patient_id: str lines: list[CoverageLine] # all lines, incl. non-gradeable rollup_status: Optional[LineStatus] # None = no graded gradeable lines cgm_line_count: int # gradeable lines total_line_count: int # all lines plan_type_needed: bool # any gradeable line in PTN def rollup_patient(lines: list[CoverageLine]) -> PatientRollup: """Roll one patient's coverage lines into a single verdict, per the locked truth table (plan 02 P3): | gradeable statuses present | rollup_status | | none | None | | all Plan Type Needed | Plan Type Needed | | some PTN + others | worse of (Action Needed, worst other)| | no PTN | worst by severity | """ if not lines: raise ValueError("rollup_patient requires at least one CoverageLine") graded = [ l for l in lines if l.gradeable and l.line_status is not None ] statuses = [LineStatus(l.line_status) for l in graded] ptn = [s for s in statuses if s == LineStatus.PLAN_TYPE_NEEDED] if not statuses: status: Optional[LineStatus] = None elif len(ptn) == len(statuses): status = LineStatus.PLAN_TYPE_NEEDED elif ptn: worst_other = max( (s for s in statuses if s != LineStatus.PLAN_TYPE_NEEDED), key=SEVERITY.get, ) status = max(worst_other, LineStatus.ACTION_NEEDED, key=SEVERITY.get) else: status = max(statuses, key=SEVERITY.get) return PatientRollup( patient_id=lines[0].patient_id, lines=lines, rollup_status=status, cgm_line_count=sum(1 for l in lines if l.gradeable), total_line_count=len(lines), plan_type_needed=bool(ptn), ) def rollup_patients(lines: dict) -> dict[str, PatientRollup]: """Group CoverageLines (any keying) by patient and roll each up.""" by_patient: dict[str, list[CoverageLine]] = defaultdict(list) for line in lines.values(): by_patient[line.patient_id].append(line) return { patient_id: rollup_patient(patient_lines) for patient_id, patient_lines in by_patient.items() }