Slaiwala commited on
Commit
f2e05cd
·
verified ·
1 Parent(s): 06d7379

Update spine_coder/spine_coder_core.py

Browse files
Files changed (1) hide show
  1. spine_coder/spine_coder_core.py +69 -23
spine_coder/spine_coder_core.py CHANGED
@@ -95,7 +95,36 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
95
  suggestions: List[Dict[str, Any]] = []
96
  case_modifiers: List[Dict[str, str]] = []
97
 
98
- # --- Levels / spans / laterality ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  levels = _extract_levels(t)
100
  inters = _count_interspaces(levels)
101
  span = _span_segments(levels)
@@ -149,7 +178,6 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
149
  no_decompression_due_to_revision = True
150
 
151
  # ---------- (2b) New posterior instrumentation WITHOUT explicit fusion ----------
152
- # If new screws/rods/construct placed but no TLIF/PLIF/posterior-fusion wording, still code instrumentation.
153
  if (
154
  not _has(t, r"\btlif\b|\bplif\b|posterolateral\b.*\bfusion|posterior\b.*\bfusion")
155
  and (mentions_inst_post or _has(t, r"\b(new|placed|inserted|reinserted)\b.{0,20}\b(screw|rod|construct|instrumentation)\b"))
@@ -163,52 +191,69 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
163
  "22844":"Posterior segmental instrumentation (8+ segments)"}[code]
164
  _add(suggestions, code, desc, "New posterior instrumentation documented.", "instrumentation", False, 0.82)
165
 
166
- # ---------- Exposure-only guard ----------
167
- exposure_only = _has(t, r"\banterior (exposure|approach) performed\b") and not _has(
168
- t, r"\bfusion|discectomy|interbody|arthrodesis|cage|plate|spacer"
169
- )
170
-
171
- # ---------- (2) ACDF with guards (history/removal/exposure-only) ----------
172
  found_removal = _has(t, rem_pat_either)
173
  acdf_history = _has(t, r"\b(prior|previous|history of|s/?p)\s*acdf\b")
174
- acdf_current = _has(t, r"\banterior cervical discectomy|anterior cervical fusion|smith[- ]?robinson\b")
175
- if (acdf_current or (_has(t, r"\bacdf\b") and not acdf_history)) and not (found_removal and not acdf_current) and not exposure_only:
 
 
 
 
 
 
 
 
 
176
  n = max(1, inters or 1)
177
  _add(suggestions, "22551", "ACDF, first interspace (includes discectomy)",
178
  "Anterior cervical fusion pattern detected.", "ACDF", True, 0.95, mods=lat_mods)
179
  if n > 1:
180
  _add(suggestions, "22552", f"ACDF, each additional interspace ×{n-1}",
181
  "Multi-level ACDF.", "ACDF add-on", False, 0.9, units=(n-1), mods=lat_mods)
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  if mentions_plate_ant:
183
- est_span = span if (span and span >= 2) else (n + 1)
 
184
  code = _inst_code_by_span(est_span, anterior=True)
185
  if code:
186
  desc = {"22845":"Anterior instrumentation (2–3 segments)",
187
  "22846":"Anterior instrumentation (4–7 segments)",
188
  "22847":"Anterior instrumentation (8+ segments)"}[code]
189
- _add(suggestions, code, desc, "Anterior plate present; span estimated from levels.", "instrumentation", False, 0.8)
190
 
191
  # ---------- (3a) Implicit TLIF when keywords imply the construct ----------
192
- # Fire when facetectomy + interbody cage/device + pedicle screws appear together.
193
  if (
194
  not _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion")
195
- and _has(t, r"\bfacetectom(y|ies)\b|complete facetectomy|hemifacetectomy")
196
- and _has(t, r"\binterbody (cage|device|spacer)\b|peek cage|titanium cage|allograft spacer")
197
  and (mentions_pedicle or _has(t, r"\bpedicle screw(s)?\b"))
198
  and region in {"lumbar", "thoracic"}
199
  ):
200
  n = max(1, inters or 1)
201
  _add(suggestions, "22633", "Posterior/posterolateral + posterior interbody, single level",
202
- "Implicit TLIF/PLIF: facetectomy + interbody device + pedicle screws.", "TLIF/PLIF", True, 0.92, mods=lat_mods)
203
  if n > 1:
204
  _add(suggestions, "22634", f"Posterior interbody each additional interspace ×{n-1}",
205
- "Multi-level implicit TLIF/PLIF.", "TLIF/PLIF add-on", False, 0.88, units=(n-1), mods=lat_mods)
206
  code = _inst_code_by_span(span or (n+1), anterior=False)
207
  if code:
208
  desc = {"22842":"Posterior segmental instrumentation (2–3 segments)",
209
  "22843":"Posterior segmental instrumentation (4–7 segments)",
210
  "22844":"Posterior segmental instrumentation (8+ segments)"}[code]
211
- _add(suggestions, code, desc, "Posterior instrumentation detected.", "instrumentation", False, 0.82)
212
 
213
  # ---------- (3) TLIF / PLIF (beats posterior fusion) ----------
214
  if _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion"):
@@ -227,7 +272,7 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
227
  _add(suggestions, code, desc, "Posterior instrumentation detected.", "instrumentation", False, 0.82)
228
 
229
  # ---------- (4) ALIF ----------
230
- if _has(t, r"\balif\b|anterior lumbar interbody fusion") and not exposure_only:
231
  n = max(1, inters or 1)
232
  _add(suggestions, "22558", "Anterior lumbar interbody fusion, single interspace",
233
  "ALIF detected.", "ALIF", True, 0.9)
@@ -395,7 +440,7 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
395
  for k, pat in mod_map.items():
396
  if _has(t, pat): case_modifiers.append({"modifier": k, "reason": reasons[k]})
397
 
398
- # EXPLICIT_BILATERAL_DECOMP: If 'bilateral' appears near decompression nouns, force 50 if not already set
399
  if not any(m["modifier"]=="50" for m in case_modifiers):
400
  if _has(t, r"\bbilateral\b") and _has(t, r"(foraminotom(y|ies)|facetectom(y|ies)|laminectom(y|ies)|laminotom(y|ies))"):
401
  case_modifiers.append({"modifier":"50","reason": reasons["50"]})
@@ -446,8 +491,9 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
446
  has_50 = any(m["modifier"] == "50" for m in case_modifiers)
447
  lat_row_mods = [] if has_50 else lat_mods
448
  for row in out:
449
- if lat_row_mods and not row.get("modifiers"):
450
- row["modifiers"] = lat_row_mods[:]
 
451
  if has_50:
452
  for row in out:
453
  if row.get("modifiers"):
@@ -484,7 +530,7 @@ def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10
484
  elif _has(t, bilateral_procedure_pat):
485
  case_laterality = "bilateral"
486
 
487
- # ---------- OPTIONAL: surface tech flags in primary rationale (non-billing, trust-building) ----------
488
  if flags_list:
489
  for r in out:
490
  if r.get("primary", False):
 
95
  suggestions: List[Dict[str, Any]] = []
96
  case_modifiers: List[Dict[str, str]] = []
97
 
98
+ # --- Early guard: exposure-only / access-only -----------------------------
99
+ # Flexible: catches "anterior exposure of L4–S1 performed by vascular surgeon ... No fusion performed"
100
+ exposure_only = (
101
+ _has(t, r"\bexpos(e|ed|ure)\b|\bapproach\b") and
102
+ _has(t, r"\banterior\b|retroperitoneal|vascular (surgeon|exposure)|access\b") and
103
+ _has(t, r"\bno (fusion|arthrodesis|interbody|implants?|cage|plate|screw|instrumentation)\b|without (fusion|interbody|instrumentation)") and
104
+ not _has(t, r"\bfusion|arthrodesis|discectom\w+|interbody|tlif|plif|alif|acdf|arthroplasty|stimulator|instrument|hardware|screw|rod|plate|cage")
105
+ )
106
+ if exposure_only:
107
+ return {
108
+ "payer": payer,
109
+ "region": "unknown",
110
+ "levels": [],
111
+ "interspaces_est": 0,
112
+ "span_segments_est": 0,
113
+ "suggestions": [{
114
+ "cpt": "00000",
115
+ "desc": "No recognizable spine CPT pattern found",
116
+ "rationale": "Exposure-only access without decompression/fusion/instrumentation.",
117
+ "confidence": 0.0, "category": "none", "primary": True, "modifiers": [], "units": 1, "score": 0.0
118
+ }],
119
+ "case_modifiers": [],
120
+ "flags": [],
121
+ "flags_map": {},
122
+ "laterality": "na",
123
+ "build": "FINAL-v2.1",
124
+ "mode": "standard",
125
+ }
126
+
127
+ # --- Levels / spans / laterality -----------------------------------------
128
  levels = _extract_levels(t)
129
  inters = _count_interspaces(levels)
130
  span = _span_segments(levels)
 
178
  no_decompression_due_to_revision = True
179
 
180
  # ---------- (2b) New posterior instrumentation WITHOUT explicit fusion ----------
 
181
  if (
182
  not _has(t, r"\btlif\b|\bplif\b|posterolateral\b.*\bfusion|posterior\b.*\bfusion")
183
  and (mentions_inst_post or _has(t, r"\b(new|placed|inserted|reinserted)\b.{0,20}\b(screw|rod|construct|instrumentation)\b"))
 
191
  "22844":"Posterior segmental instrumentation (8+ segments)"}[code]
192
  _add(suggestions, code, desc, "New posterior instrumentation documented.", "instrumentation", False, 0.82)
193
 
194
+ # ---------- (2) ACDF with guards (history/removal) + broadened trigger ----------
 
 
 
 
 
195
  found_removal = _has(t, rem_pat_either)
196
  acdf_history = _has(t, r"\b(prior|previous|history of|s/?p)\s*acdf\b")
197
+ acdf_current = (
198
+ _has(t, r"\banterior cervical discectomy|anterior cervical fusion|smith[- ]?robinson\b")
199
+ or (
200
+ (region in {"cervical","cervicothoracic"})
201
+ and _has(t, r"\bdiscectom\w+\b")
202
+ and (_has(t, r"\b(interbody|cage|arthrodesis|plate)\b") or mentions_plate_ant)
203
+ )
204
+ or _has(t, r"\bacdf\b")
205
+ )
206
+
207
+ if acdf_current and not (found_removal and not _has(t, r"\bacdf\b")) and not exposure_only:
208
  n = max(1, inters or 1)
209
  _add(suggestions, "22551", "ACDF, first interspace (includes discectomy)",
210
  "Anterior cervical fusion pattern detected.", "ACDF", True, 0.95, mods=lat_mods)
211
  if n > 1:
212
  _add(suggestions, "22552", f"ACDF, each additional interspace ×{n-1}",
213
  "Multi-level ACDF.", "ACDF add-on", False, 0.9, units=(n-1), mods=lat_mods)
214
+
215
+ # Plate span inference: "spanning C4–C7" or "span C4–C7"
216
+ m_span = re.search(r"\bspan\w*\s*(c\d)\s*[-–—]\s*(c\d)\b", t, flags=re.I)
217
+ plate_span_est = None
218
+ if m_span:
219
+ try:
220
+ c_lo = int(re.sub(r"\D","", m_span.group(1)))
221
+ c_hi = int(re.sub(r"\D","", m_span.group(2)))
222
+ if c_hi >= c_lo:
223
+ plate_span_est = c_hi - c_lo + 1
224
+ except Exception:
225
+ plate_span_est = None
226
+
227
  if mentions_plate_ant:
228
+ est_span = (plate_span_est if plate_span_est and plate_span_est >= 2
229
+ else (span if (span and span >= 2) else (n + 1)))
230
  code = _inst_code_by_span(est_span, anterior=True)
231
  if code:
232
  desc = {"22845":"Anterior instrumentation (2–3 segments)",
233
  "22846":"Anterior instrumentation (4–7 segments)",
234
  "22847":"Anterior instrumentation (8+ segments)"}[code]
235
+ _add(suggestions, code, desc, "Anterior plate present; span estimated from levels/plate span.", "instrumentation", False, 0.8)
236
 
237
  # ---------- (3a) Implicit TLIF when keywords imply the construct ----------
 
238
  if (
239
  not _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion")
240
+ and _has(t, r"\bfacetectom(y|ies)\b|complete facetectomy|hemifacetectomy|transforaminal")
241
+ and _has(t, r"\binterbody (cage|device|spacer)\b|peek (cage|spacer)|titanium (cage|spacer)|allograft spacer")
242
  and (mentions_pedicle or _has(t, r"\bpedicle screw(s)?\b"))
243
  and region in {"lumbar", "thoracic"}
244
  ):
245
  n = max(1, inters or 1)
246
  _add(suggestions, "22633", "Posterior/posterolateral + posterior interbody, single level",
247
+ "Implicit TLIF/PLIF: facetectomy + interbody device + pedicle screws.", "TLIF/PLIF", True, 0.93, mods=lat_mods)
248
  if n > 1:
249
  _add(suggestions, "22634", f"Posterior interbody each additional interspace ×{n-1}",
250
+ "Multi-level implicit TLIF/PLIF.", "TLIF/PLIF add-on", False, 0.89, units=(n-1), mods=lat_mods)
251
  code = _inst_code_by_span(span or (n+1), anterior=False)
252
  if code:
253
  desc = {"22842":"Posterior segmental instrumentation (2–3 segments)",
254
  "22843":"Posterior segmental instrumentation (4–7 segments)",
255
  "22844":"Posterior segmental instrumentation (8+ segments)"}[code]
256
+ _add(suggestions, code, desc, "Posterior instrumentation detected (implicit TLIF).", "instrumentation", False, 0.83)
257
 
258
  # ---------- (3) TLIF / PLIF (beats posterior fusion) ----------
259
  if _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion"):
 
272
  _add(suggestions, code, desc, "Posterior instrumentation detected.", "instrumentation", False, 0.82)
273
 
274
  # ---------- (4) ALIF ----------
275
+ if _has(t, r"\balif\b|anterior lumbar interbody fusion"):
276
  n = max(1, inters or 1)
277
  _add(suggestions, "22558", "Anterior lumbar interbody fusion, single interspace",
278
  "ALIF detected.", "ALIF", True, 0.9)
 
440
  for k, pat in mod_map.items():
441
  if _has(t, pat): case_modifiers.append({"modifier": k, "reason": reasons[k]})
442
 
443
+ # Force -50 if 'bilateral' appears near decompression nouns and not already set
444
  if not any(m["modifier"]=="50" for m in case_modifiers):
445
  if _has(t, r"\bbilateral\b") and _has(t, r"(foraminotom(y|ies)|facetectom(y|ies)|laminectom(y|ies)|laminotom(y|ies))"):
446
  case_modifiers.append({"modifier":"50","reason": reasons["50"]})
 
491
  has_50 = any(m["modifier"] == "50" for m in case_modifiers)
492
  lat_row_mods = [] if has_50 else lat_mods
493
  for row in out:
494
+ if row["category"] in {"decompression","decompression add-on","TLIF/PLIF","posterior_fusion","posterior_fusion add-on"}:
495
+ if lat_row_mods and not row.get("modifiers"):
496
+ row["modifiers"] = lat_row_mods[:]
497
  if has_50:
498
  for row in out:
499
  if row.get("modifiers"):
 
530
  elif _has(t, bilateral_procedure_pat):
531
  case_laterality = "bilateral"
532
 
533
+ # ---------- OPTIONAL: surface tech flags in primary rationale ----------
534
  if flags_list:
535
  for r in out:
536
  if r.get("primary", False):