Skip to content

Commit 42eef36

Browse files
rootroot
authored andcommitted
v2.5: Add system badges, smaller TG/tones, trigger filter support
- System name as badge in tone hits table - Smaller/slimmer talkgroup and tones columns - Trigger badges in table - Date range filter support in API - Trigger ID filter in API - Optional radio_system_id for All Systems view
1 parent 7c14622 commit 42eef36

4 files changed

Lines changed: 72 additions & 16 deletions

File tree

routes/api/tone_finder.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""
1818
import json
1919
import os
20+
import datetime
2021
from collections import defaultdict
2122
from pathlib import Path
2223
from urllib.parse import urlparse
@@ -40,39 +41,72 @@ def list_calls():
4041
db = current_app.config["db"]
4142
logger = current_app.config["logger"]
4243

43-
# ── required & validated params ──────────────────────────────
44-
try:
45-
rsid = int(request.args["radio_system_id"])
46-
except (KeyError, ValueError):
47-
return _err("radio_system_id (int) is required", 400)
44+
# ── params ──────────────────────────────
45+
rsid_arg = request.args.get("radio_system_id")
46+
rsid = None
47+
if rsid_arg:
48+
try:
49+
rsid = int(rsid_arg)
50+
except ValueError:
51+
pass
4852

4953
tone_type = request.args.get("tone_type")
5054
if tone_type and tone_type not in ALLOWED_TONE_TYPES:
5155
return _err("invalid tone_type", 400)
5256

5357
trigger_only = request.args.get("trigger_only") in ("1", "true", "yes")
58+
trigger_id = request.args.get("trigger_id")
59+
60+
# Date filters
61+
date_from = request.args.get("date_from")
62+
date_to = request.args.get("date_to")
5463

5564
limit = max(1, min(int(request.args.get("limit", 100)), 500))
5665
offset = max(0, int(request.args.get("offset", 0)))
5766

5867
# ── SQL build ────────────────────────────────────────────────
5968
tone_filter = []
60-
params = [rsid]
69+
params = []
70+
71+
if rsid:
72+
tone_filter.append("cr.radio_system_id = ?")
73+
params.append(rsid)
74+
75+
# Filter by specific trigger
76+
if trigger_id:
77+
tone_filter.append("""
78+
cr.call_id IN (
79+
SELECT DISTINCT ttm.call_id FROM tone_trigger_map ttm
80+
WHERE ttm.alert_trigger_id = ?
81+
)
82+
""")
83+
params.append(int(trigger_id))
6184

6285
if tone_type:
6386
tone_filter.append("cte.tone_type = ?")
6487
params.append(tone_type)
6588
if trigger_only:
6689
tone_filter.append("cte.matches_trigger = 1")
6790

68-
where_extra = f"AND {' AND '.join(tone_filter)}" if tone_filter else ""
91+
# Date filters
92+
if date_from:
93+
tone_filter.append("cr.start_epoch_s >= ?")
94+
params.append(datetime.strptime(date_from, "%Y-%m-%d").timestamp())
95+
if date_to:
96+
# Include the entire day
97+
tone_filter.append("cr.start_epoch_s < ?")
98+
params.append(datetime.strptime(date_to, "%Y-%m-%d").timestamp() + 86400)
99+
100+
where_extra = f"WHERE {' AND '.join(tone_filter)}" if tone_filter else "WHERE 1=1"
69101

70102
sql = f"""
71103
SELECT cr.call_id,
72104
cr.start_epoch_s,
73105
cr.duration_s,
74106
cr.talkgroup,
75107
cr.file_path,
108+
cr.radio_system_id,
109+
rs.system_name,
76110
MAX(cte.matches_trigger) AS has_trigger,
77111
COUNT(cte.tone_event_id) AS tone_count,
78112
CASE
@@ -93,16 +127,14 @@ def list_calls():
93127
ELSE 0
94128
END
95129
) AS has_address_geocoded,
96-
97-
-- simple summary fields for the list
98130
MAX(ct.text_full) AS transcript_text,
99131
MAX(ct.address_geocoded_json) AS address_geocoded_json,
100132
MAX(ct.incident_category) AS incident_category
101-
133+
102134
FROM call_records cr
103135
JOIN call_tone_events cte USING(call_id)
104136
LEFT JOIN call_transcripts ct USING(call_id)
105-
WHERE cr.radio_system_id = ?
137+
LEFT JOIN radio_systems rs ON cr.radio_system_id = rs.radio_system_id
106138
{where_extra}
107139
GROUP BY cr.call_id
108140
ORDER BY cr.start_epoch_s DESC
@@ -162,8 +194,9 @@ def list_calls():
162194
"has_transcript": bool(r["has_transcript"]),
163195
"has_address_extracted": bool(r["has_address_extracted"]),
164196
"has_address_geocoded" : bool(r["has_address_geocoded"]),
197+
"system_name" : r.get("system_name") or "",
165198

166-
# new simple fields for the table
199+
# new "simple" fields for the table
167200
"transcript_simple": transcript_simple,
168201
"location_label" : location_label,
169202
"location_lat" : location_lat,

static/js/dashboard/tone_finder.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -898,17 +898,40 @@ async function loadCalls(ev) {
898898
const startLocal = new Date(row.start_epoch * 1000).toLocaleString();
899899
const duration = Number(row.duration_s ?? 0).toFixed(1) + " s";
900900
const systemName = row.system_name ?? "";
901+
const talkgroup = row.talkgroup ?? "";
902+
const toneCount = row.tone_count ?? 0;
903+
904+
// System badge
905+
const systemBadge = systemName ? `<span class="badge bg-primary fs-6">${systemName}</span>` : "";
906+
907+
// Talkgroup - smaller/slimmer display
908+
const talkgroupDisplay = talkgroup ? `<span class="text-muted small">${talkgroup}</span>` : "";
909+
910+
// Tones - smaller display
911+
const tonesDisplay = toneCount > 0 ? `<span class="badge bg-success">${toneCount}</span>` : `<span class="text-muted small">0</span>`;
912+
913+
// Trigger badges - check if this call has triggers
914+
const hasTriggers = row.fired_triggers && row.fired_triggers.length > 0;
915+
let triggerBadges = "";
916+
if (hasTriggers) {
917+
triggerBadges = row.fired_triggers.slice(0, 3).map(t =>
918+
`<span class="badge bg-warning text-dark me-1">${t.trigger_name || t.alert_trigger_id}</span>`
919+
).join("");
920+
if (row.fired_triggers.length > 3) {
921+
triggerBadges += `<span class="badge bg-secondary">+${row.fired_triggers.length - 3}</span>`;
922+
}
923+
}
901924

902925
callMeta.set(String(callId), {src: audioSrc, label});
903926

904927
return [
905928
checkboxCell(callId),
906929
startLocal,
907-
systemName,
908-
row.talkgroup ?? "",
930+
systemBadge,
931+
talkgroupDisplay,
909932
duration,
910-
row.tone_count,
911-
buildStatusIcons(row),
933+
tonesDisplay,
934+
triggerBadges,
912935
"",
913936
`<div class="btn-group btn-group-sm" role="group">
914937
<button class="btn btn-success js-play"

var/icad_dispatch.db-shm

-32 KB
Binary file not shown.

var/icad_dispatch.db-wal

Whitespace-only changes.

0 commit comments

Comments
 (0)