Merge pull request 'reopen: act on drb-correlation-review of e972cac' (#177) from fix/reopen-review into main
This commit was merged in pull request #177.
This commit is contained in:
@@ -224,6 +224,16 @@ def _normalize_unit(unit: str) -> str:
|
|||||||
return key or unit.strip().lower()
|
return key or unit.strip().lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _after_close(inc: dict, now: datetime) -> bool:
|
||||||
|
try:
|
||||||
|
closed = datetime.fromisoformat(str(inc.get("resolved_at") or "").replace("Z", "+00:00"))
|
||||||
|
except ValueError:
|
||||||
|
return True
|
||||||
|
if closed.tzinfo is None:
|
||||||
|
closed = closed.replace(tzinfo=timezone.utc)
|
||||||
|
return now > closed
|
||||||
|
|
||||||
|
|
||||||
def _is_trackable_unit(unit: str) -> bool:
|
def _is_trackable_unit(unit: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Whether a unit is concrete enough to hold an incident open until it clears.
|
Whether a unit is concrete enough to hold an incident open until it clears.
|
||||||
@@ -2029,7 +2039,8 @@ async def _update_incident(
|
|||||||
incident_id = inc["incident_id"]
|
incident_id = inc["incident_id"]
|
||||||
|
|
||||||
call_ids = list(inc.get("call_ids") or [])
|
call_ids = list(inc.get("call_ids") or [])
|
||||||
if call_id not in call_ids:
|
is_new_call = call_id not in call_ids
|
||||||
|
if is_new_call:
|
||||||
call_ids.append(call_id)
|
call_ids.append(call_id)
|
||||||
|
|
||||||
talkgroup_ids = list(inc.get("talkgroup_ids") or [])
|
talkgroup_ids = list(inc.get("talkgroup_ids") or [])
|
||||||
@@ -2101,10 +2112,11 @@ async def _update_incident(
|
|||||||
# thin traffic rides along without extending its life.
|
# thin traffic rides along without extending its life.
|
||||||
if refresh_activity:
|
if refresh_activity:
|
||||||
updates["updated_at"] = _floor_at_started_at(inc, now).isoformat()
|
updates["updated_at"] = _floor_at_started_at(inc, now).isoformat()
|
||||||
updates["substantive_call_count"] = (
|
if is_new_call: # a second scene of the same call is not a second call
|
||||||
inc.get("substantive_call_count")
|
updates["substantive_call_count"] = (
|
||||||
if inc.get("substantive_call_count") is not None else len(inc.get("call_ids") or [])
|
inc.get("substantive_call_count")
|
||||||
) + 1
|
if inc.get("substantive_call_count") is not None else len(inc.get("call_ids") or [])
|
||||||
|
) + 1
|
||||||
else:
|
else:
|
||||||
updates["last_thin_at"] = now.isoformat()
|
updates["last_thin_at"] = now.isoformat()
|
||||||
# Update incident type when a re-classified call provides a concrete type.
|
# Update incident type when a re-classified call provides a concrete type.
|
||||||
@@ -2122,8 +2134,11 @@ async def _update_incident(
|
|||||||
# Signal-based auto-resolve: every tracked unit has cleared, none still active.
|
# Signal-based auto-resolve: every tracked unit has cleared, none still active.
|
||||||
# Requires at least one unit to have explicitly signalled back-in-service so we
|
# Requires at least one unit to have explicitly signalled back-in-service so we
|
||||||
# don't fire on incidents where units were never tracked (no unit mentions at all).
|
# don't fire on incidents where units were never tracked (no unit mentions at all).
|
||||||
if inc.get("status") == "resolved":
|
if inc.get("status") == "resolved" and refresh_activity and _after_close(inc, now):
|
||||||
# A timer close was provisional and a related call just arrived.
|
# A timer close was provisional and a related, substantive call arrived
|
||||||
|
# after it. A thin "10-4" rides along without reopening (it would not
|
||||||
|
# refresh updated_at, so the next sweep would just close it again),
|
||||||
|
# and neither does a sweep link of a call from before the close.
|
||||||
updates.update({"status": "active", "resolved_at": None, "resolved_via": None,
|
updates.update({"status": "active", "resolved_at": None, "resolved_via": None,
|
||||||
"reopenable": False, "reopened_count": (inc.get("reopened_count") or 0) + 1})
|
"reopenable": False, "reopened_count": (inc.get("reopened_count") or 0) + 1})
|
||||||
logger.info(f"Correlator: reopened timer-closed incident {incident_id} (call {call_id})")
|
logger.info(f"Correlator: reopened timer-closed incident {incident_id} (call {call_id})")
|
||||||
@@ -2373,6 +2388,10 @@ async def _find_cross_system_parent(
|
|||||||
best_score = 0.0
|
best_score = 0.0
|
||||||
|
|
||||||
for inc in recent:
|
for inc in recent:
|
||||||
|
# A timer-closed incident is in `recent` only so a related call can
|
||||||
|
# reopen it; it must not be adopted as another agency's parent.
|
||||||
|
if inc.get("status") != "active":
|
||||||
|
continue
|
||||||
# Only cross-system candidates
|
# Only cross-system candidates
|
||||||
if system_id in (inc.get("system_ids") or []):
|
if system_id in (inc.get("system_ids") or []):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -93,3 +93,9 @@ def test_thin_calls_do_not_fill_the_call_cap():
|
|||||||
assert ic._incident_at_capacity(inc, now) is None
|
assert ic._incident_at_capacity(inc, now) is None
|
||||||
legacy = {k: v for k, v in inc.items() if k != "substantive_call_count"}
|
legacy = {k: v for k, v in inc.items() if k != "substantive_call_count"}
|
||||||
assert ic._incident_at_capacity(legacy, now).startswith("call_cap")
|
assert ic._incident_at_capacity(legacy, now).startswith("call_cap")
|
||||||
|
|
||||||
|
|
||||||
|
def test_reopen_only_for_a_call_after_the_close():
|
||||||
|
closed = {"resolved_at": "2026-09-22T15:00:00+00:00"}
|
||||||
|
assert ic._after_close(closed, datetime(2026, 9, 22, 15, 5, tzinfo=timezone.utc))
|
||||||
|
assert not ic._after_close(closed, datetime(2026, 9, 22, 14, 55, tzinfo=timezone.utc))
|
||||||
|
|||||||
Reference in New Issue
Block a user