""" server-26#127 — upstream dispatch-vs-chatter classifier, shadow mode. Fixtures are real transcripts, not invented ones: pulled from `corr_dump_9-7_0437am.json`, `corr_dump_9-7_pm.json`, `corr_dump_9-12.json` and the hand-labeled examples in `CORRELATION_REVIEW_0907b.md` / `CORRELATION_REVIEW_0912.md`. The "must classify False" set specifically includes every transcript those review docs flagged as dangerous to drop — a false positive here is a real event silently losing its scene once this classifier ever goes live, which is a much worse failure than a missed chatter call staying in the existing (already-working) pipeline. """ import pytest from app.internal.chatter_classifier import classify_chatter # ───────────────────────────────────────────────────────────────────────────── # Must classify as chatter # ───────────────────────────────────────────────────────────────────────────── CHATTER_EXAMPLES = [ # Bare acknowledgements / unit check-ins (CORRELATION_REVIEW_0907b.md) ("114 Paul.\n114 Paul, Metro Central.\n10-4.", "bare_acknowledgement"), ("Affirmative, in charge of 10-8. 10-8, 10-4.", "bare_acknowledgement"), ("6-8, you can show me 98. 10-4.", "bare_acknowledgement"), ("10-4, 10-4 Central, 98. 10-4, 98.", "bare_acknowledgement"), ("7 for Post 1 and 2, 98. Affirm.", "bare_acknowledgement"), ("11-Victor to Central. 11-Victor. 72-Holland, 1-5. Central.", "bare_acknowledgement"), # Roll call (CORRELATION_REVIEW_0907b.md / _0912.md) ("Post 4, Ossining. And to volunteer patrol, stand by for roll call.", "roll_call"), ("Headquarters to all cars, stand by for roll call.", "roll_call"), ("All Troop NYC Patrols, stand by for roll call.", "roll_call"), ( "Car 100, roll call.\nHenry 1.\nHenry 1.\nSam 1.\nSam 1.\n45 Baker.\n" "45 Baker.\n11 Adam.\nAdam.\n11 Baker.\nBaker.\nStaff 1.\n1.\nStaff 2.", "roll_call", ), ( "Headquarters, all cars on a roll call. Baker 1? Baker 1. Henry 1? " "Henry 1. Sam 2? Sam 2. 11 Adam? 11. 11 Baker? 11 Baker.", "roll_call", ), ("Because all cars came out for roll call.", "roll_call"), ("10-1. KL Cars, that concludes roll call, time is 3-31.", "roll_call"), # Minimal single-word / bare-code transmissions (orphan pool, all 3 dumps) ("10-4.", "bare_acknowledgement"), ("Roger.", "bare_acknowledgement"), ("Clear.", "bare_acknowledgement"), ("Affirmative.", "bare_acknowledgement"), ("Received.", "bare_acknowledgement"), ("10-8, clear. 10-4.", "bare_acknowledgement"), ("Post 4, 10-8. 10-4.", "bare_acknowledgement"), ("Central to 6 Henry.", "bare_acknowledgement"), ] @pytest.mark.parametrize("transcript,expected_reason", CHATTER_EXAMPLES) def test_classifies_chatter(transcript, expected_reason): is_chatter, reason = classify_chatter(transcript) assert is_chatter is True assert reason == expected_reason # ───────────────────────────────────────────────────────────────────────────── # Must NOT classify as chatter — real events, including every transcript the # review docs specifically named as dangerous to drop. # ───────────────────────────────────────────────────────────────────────────── REAL_EVENT_EXAMPLES = [ # The major "extinguishing fire" call (severity=major, tags=[extinguishing-fire]) ("Dispatch, this is 7-4, extinguishing fire.", "extinguishing_fire"), # Geocoded 911-hangup call (has location_coords) ( "7, Charlie. Charlie, check and advise, we've got a call for service " "coming over, it's going to be a 9-1-1 hangout, no voice contact. " "Looks like it was an automated message saying it's the Doral Hat Company.", "geocoded_911_hangup", ), # Pursuit updates (severity=major, tags include pursuit / low-speed-pursuit) ("I'm aware of that one. It's a low-speed pursuit. It's refusing to pull over.", "low_speed_pursuit"), ( "1. Headquarters to 5-charlie. I'm going to say the last thing to anyone.\n" "2. Info, Sgt. Repeat.\n" "3. The SP is on a pursuit southbound on I-684. It's approaching the airport.\n" "4. Okay, thank you.\n5. 23-59.", "pursuit_i684", ), # "6 Alpha ... Pelham Station" subject check (CORRELATION_REVIEW_0907b.md's # own "genuinely distinct events" list) — looks like a bare check-in but # dispatches a unit to a specific location. ( "6 Alpha, this is Central. 7 Alpha here.\n" "6 Alpha, can you show me on scene at Pelham Station? Stand by.", "pelham_station_subject_check", ), # Property-retrieval call (tags=[property-retrieval]) ( "Property was retrieved with a 911. Can I get a phone number? 10-4. " "Phone number is 214792. 214792.", "property_retrieval", ), # Subject check south of Maronex Station (tags=[subject-check]) ( "Proceed. Show me on a subject south of Maronex Station. Can I get a " "15 check by New York client ID?", "maronex_subject_check", ), # Trespassing at milepost 13.7 (tags=[trespassing]) ( "Can you just 10-5 that job? You came over real muffled.\n" "10-4, there's going to be a trespass on the tracks.\n" "Train 8755 reports two juveniles, one male, one female, both wearing " "white shirts, track three side, at milepost 13.7.", "trespass_milepost_13_7", ), # MVA (severity=moderate, tags=[traffic-accident]) ("1. Train patrol 9.\n2. MVA 4, how close is it?\n3. 10-4.", "mva"), ] @pytest.mark.parametrize("transcript,label", REAL_EVENT_EXAMPLES, ids=[l for _, l in REAL_EVENT_EXAMPLES]) def test_does_not_classify_real_events_as_chatter(transcript, label): is_chatter, reason = classify_chatter(transcript) assert is_chatter is False, f"{label}: false positive, reason={reason!r}" assert reason is None # ───────────────────────────────────────────────────────────────────────────── # Edge cases # ───────────────────────────────────────────────────────────────────────────── def test_empty_transcript_not_chatter(): assert classify_chatter("") == (False, None) assert classify_chatter(None) == (False, None) assert classify_chatter(" ") == (False, None) def test_unrecognized_content_defaults_to_not_chatter(): # Anything containing real descriptive words the classifier doesn't # recognize must fall through to (False, None), not guess. is_chatter, reason = classify_chatter("Shots fired, officer down, requesting backup immediately.") assert is_chatter is False assert reason is None