correlator: stop non-primary scenes inheriting the call doc's pin (#87)
_build_context fell back to call_doc.get("location_coords") whenever a
scene passed no coordinates of its own. One radio call can be split
into several scenes, but only the primary scene's geocode is ever
written to the call doc — so every non-primary scene silently
inherited the primary scene's pin. That fabricated location_proximity,
the strongest accept signal the correlator has, for a scene that had
no location at all, and drove it into the primary scene's incident on
a pin it never had.
Drop the fallback: coords = location_coords. A scene with no location
is now correctly judged thin, cannot win the location path, cannot
supply call_coords to _call_fits_incident, and cannot seed
_find_cross_system_parent.
recorrelation_sweep.py, the only other caller of correlate_call, was
verified to already pass both location and location_coords explicitly
from the call doc, so the fallback there was a no-op and this change
is behavior-preserving for that path.
Adds test_a_scene_with_no_location_does_not_inherit_the_call_docs_pin
to test_incident_identity.py, pinning ctx["coords"] is None and
ctx["is_thin_call"] is True when location=None but the call doc
carries a location_coords.
Ref: server-26#87
This commit is contained in:
@@ -789,7 +789,14 @@ async def _build_context(
|
||||
location = clean_location(location)
|
||||
if location is None:
|
||||
location_coords = None
|
||||
coords = location_coords or call_doc.get("location_coords")
|
||||
# NOT `location_coords or call_doc.get("location_coords")` — server-26#87.
|
||||
# A radio call can be split into several scenes, and only the primary
|
||||
# scene's geocode is written to the call doc. Falling back to it here
|
||||
# would hand every non-primary scene the primary scene's pin, fabricating
|
||||
# location_proximity (the strongest accept signal) for a scene that has
|
||||
# no location of its own and driving over-merges. If a scene passes no
|
||||
# coords, it has none — it is judged thin and must win on its own signal.
|
||||
coords = location_coords
|
||||
is_thin_call = _is_thin_call(
|
||||
call_units, call_vehicles, coords, tags, location, call_severity, reassignment
|
||||
)
|
||||
|
||||
@@ -229,6 +229,31 @@ async def test_a_bare_number_never_reaches_the_correlator():
|
||||
assert ctx["location_coords"] is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_scene_with_no_location_does_not_inherit_the_call_docs_pin():
|
||||
"""
|
||||
server-26#87. One call can be split into several scenes, and only the
|
||||
primary scene's geocode is written to the call doc. A non-primary scene
|
||||
that passes no location of its own must not inherit that pin — doing so
|
||||
fabricates location_proximity, the strongest accept signal, for a scene
|
||||
that has none, and drives it into the primary scene's incident.
|
||||
"""
|
||||
with patch("app.internal.incident_correlator.fstore") as mock_fstore:
|
||||
mock_fstore.doc_get = AsyncMock(
|
||||
return_value={"location_coords": GRASSLANDS}
|
||||
)
|
||||
mock_fstore.collection_list = AsyncMock(return_value=[])
|
||||
ctx = await _build_context(
|
||||
call_id="call-scene-2", units=None, vehicles=None, cleared_units=None,
|
||||
location_coords=None, reference_time=NOW,
|
||||
system_id="sys-1", talkgroup_id=383, talkgroup_name=DISPATCH_TG,
|
||||
tags=[], incident_type="police", location=None,
|
||||
reassignment=False, create_if_new=True,
|
||||
)
|
||||
assert ctx["coords"] is None
|
||||
assert ctx["is_thin_call"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_bare_number_never_becomes_an_incident_location_or_title():
|
||||
inc = await _create(tags=["flames"], location="49", coords=None,
|
||||
|
||||
Reference in New Issue
Block a user