From e0fdc4fbbc6af1418be6f16ddcea40396d1b92fc Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Sep 2026 10:19:24 -0400 Subject: [PATCH] intelligence: a plate read on a patrol channel is a traffic stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner: plate reads should become traffic stops. Held-out replay of 09-21 (server-26#170): Ossining's Post 4 stops were read out only as plates ("Frank David Boy, 4514", "Lincoln, Charlie, Robert, 7-4-0-7") and never became incidents. The self-initiated backstop now treats two+ phonetic letters followed by 3-7 digits as a stop — but only when extraction found no other event in the call (a plate on an MVA, tow or parked-car complaint stays with that event), and never on MTA/rail/bridge/fire/EMS/DPW talkgroups. c2-core: 475 pass. Co-Authored-By: Claude Opus 5.5 --- drb-c2-core/app/internal/intelligence.py | 21 ++++++++++++++++++++ drb-c2-core/tests/test_clearance_tracking.py | 14 +++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drb-c2-core/app/internal/intelligence.py b/drb-c2-core/app/internal/intelligence.py index bc786e8..30daf5e 100644 --- a/drb-c2-core/app/internal/intelligence.py +++ b/drb-c2-core/app/internal/intelligence.py @@ -517,12 +517,33 @@ _NO_BACKSTOP_TG = re.compile(r"\b(mta|rail|railroad|train|transit|bridges? and t r"rescue|ambulance|dpw|public works)\b", re.IGNORECASE) +# A plate read aloud — two or more phonetic letters then 3-7 digits: +# "Frank David Boy, 4514", "Lincoln, Charlie, Robert, 7-4-0-7". On a patrol +# channel that is a unit running a car it has stopped. Held-out replay of +# 09-21 (server-26#170): Ossining's Post 4 stops were read out only as plates +# and never became incidents. +_PHONETIC = (r"(?:adam|alpha|baker|boy|bravo|charlie|charles|david|delta|eddie|edward|echo|frank|" + r"george|golf|henry|hotel|ida|india|john|juliet|king|kilo|lincoln|lima|larry|mary|" + r"michael|mike|nora|nancy|november|ocean|oscar|peter|paul|papa|queen|robert|romeo|" + r"sam|sierra|tom|tango|union|uniform|victor|william|whiskey|x-ray|xray|young|yankee|zebra|zulu)") +_PLATE_READ = re.compile(rf"\b{_PHONETIC}(?:[,\s]+{_PHONETIC}){{1,3}}[,\s]+\d(?:[\s-]?\d){{2,6}}\b", + re.IGNORECASE) + + def _self_initiated_backstop( text: str, tags: list, incident_type: Optional[str], severity: str, talkgroup_name: Optional[str] = None, ) -> tuple[list, Optional[str], str]: if talkgroup_name and _NO_BACKSTOP_TG.search(talkgroup_name): return tags, incident_type, severity + # A plate read only stands for a stop when extraction found no other + # event in the call: the plate on an MVA, a tow or a parked-car complaint + # belongs to that event, not to a new stop. + if not tags and _PLATE_READ.search(text or ""): + tags = ["traffic-stop"] + incident_type = incident_type or "police" + if severity == "routine": + severity = "minor" for pattern, tag in _SELF_INITIATED: m = pattern.search(text or "") if not m or _NEGATED.search(text[: m.start()]): diff --git a/drb-c2-core/tests/test_clearance_tracking.py b/drb-c2-core/tests/test_clearance_tracking.py index 8747256..0f20328 100644 --- a/drb-c2-core/tests/test_clearance_tracking.py +++ b/drb-c2-core/tests/test_clearance_tracking.py @@ -140,3 +140,17 @@ def test_short_stop_report_opens_a_scene(): return await intelligence.extract_scenes("c1", "Adam 3 on a stop.", "Ch 1 (Patched with 155.310)") scenes = asyncio.run(run()) assert len(scenes) == 1 and scenes[0]["tags"] == ["traffic-stop"] and scenes[0]["incident_type"] == "police" + + +def test_plate_read_on_a_patrol_channel_is_a_stop(): + from app.internal.intelligence import _self_initiated_backstop as b + ch = "Ossining - Police Dispatch" + for t in ("Post 4. 52-62, 3-3. Hemlock Circle. Frank David Boy, 4514. 10-8.", + "4, Ossining. 52-22, Ramapo, New York. Lincoln, Charlie, Robert, 7-4-0-7 on a Chevy.", + "New York, Mary, Charlie, Nora, 5-8-6-7."): + assert b(t, [], None, "routine", ch) == (["traffic-stop"], "police", "minor"), t + # a plate on a call that is already about something else stays with it + assert b("MVA, plate Mary George Sam 2740", ["mva"], "accident", "moderate", ch) == (["mva"], "accident", "moderate") + # not on rail/bridge channels, and not without digits + assert b("Frank David Boy 4514", [], None, "routine", "MTA Bridges and Tunnels - Whitestone") == ([], None, "routine") + assert b("Charlie, David, go ahead.", [], None, "routine", ch) == ([], None, "routine") -- 2.54.0