feat: enrich correlation debug with fit_signal and orphan breakdown
_call_fits_incident now returns (bool, signal_str) so each correlation decision records exactly what evidence fired: unit_overlap, vehicle_overlap, location_proximity, time_fallback, tactical_default, or the corresponding false-return variants (unit_loc_conflict, content_divergence, etc.). - corr_fit_signal and corr_matched_units written to call docs for fast/single and fast/disambig paths - Admin debug endpoint exposes the new fields in calls_detail - Orphan section adds orphans_by_talkgroup summary (count, no-type count, sweep-exhausted count per TGID) and raises orphan limit 100 → 250 - Admin page shows corr_path and fit_signal distribution panels above raw JSON; time_fallback highlighted in yellow as a diagnostic marker No correlation logic changed — diagnostic data only.
This commit is contained in:
@@ -67,6 +67,8 @@ async def debug_correlation(
|
||||
"corr_score": call.get("corr_score"),
|
||||
"corr_candidates": call.get("corr_candidates"),
|
||||
"corr_shared_units": call.get("corr_shared_units"),
|
||||
"corr_fit_signal": call.get("corr_fit_signal"),
|
||||
"corr_matched_units": call.get("corr_matched_units"),
|
||||
"corr_sweep_count": call.get("corr_sweep_count"),
|
||||
"skip_reason": call.get("skip_reason"),
|
||||
}
|
||||
@@ -108,10 +110,29 @@ async def debug_correlation(
|
||||
]
|
||||
orphans.sort(key=lambda c: c.get("started_at", ""), reverse=True)
|
||||
|
||||
# Summarise orphans by talkgroup so the volume and source are immediately visible.
|
||||
orphans_by_tg: dict[str, dict] = {}
|
||||
for o in orphans:
|
||||
tg_key = str(o.get("talkgroup_id") or "unknown")
|
||||
if tg_key not in orphans_by_tg:
|
||||
orphans_by_tg[tg_key] = {
|
||||
"talkgroup_id": o.get("talkgroup_id"),
|
||||
"talkgroup_name": o.get("talkgroup_name") or "unknown",
|
||||
"count": 0,
|
||||
"no_type_count": 0,
|
||||
"sweep_exhausted_count": 0,
|
||||
}
|
||||
orphans_by_tg[tg_key]["count"] += 1
|
||||
if not o.get("incident_type") and not o.get("tags"):
|
||||
orphans_by_tg[tg_key]["no_type_count"] += 1
|
||||
if (o.get("corr_sweep_count") or 0) >= 3:
|
||||
orphans_by_tg[tg_key]["sweep_exhausted_count"] += 1
|
||||
|
||||
return {
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"incident_count": len(incident_records),
|
||||
"orphaned_call_count": len(orphans),
|
||||
"incidents": incident_records,
|
||||
"orphaned_calls": orphans[:100],
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"incident_count": len(incident_records),
|
||||
"orphaned_call_count": len(orphans),
|
||||
"orphans_by_talkgroup": sorted(orphans_by_tg.values(), key=lambda x: x["count"], reverse=True),
|
||||
"incidents": incident_records,
|
||||
"orphaned_calls": orphans[:250],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user