Calls: store every radio heard on a call; call_end writes srcaddr (server-26#304)
Build & Deploy / Build & push images (push) Successful in 4m36s
Build & Deploy / Deploy Firestore rules & indexes (push) Successful in 27s
Build & Deploy / Deploy to VM (push) Successful in 1m32s
Build & Deploy / Report a failed deploy (push) Skipped

P1 of the radio-ID plan. The node now reports `radios`, one
{radio, start, end} per transmission, on /upload and on call_end. Both go
through app/internal/radios.py into `radios` + `radio_ids` on the call doc.
/upload writes them before the pipeline starts, because call_end arrives
after it.

call_end also writes srcaddr now. Only call_start used to, and the node
usually learns the talker after call_start, so most calls kept None and the
correlator's srcaddr match rarely fired. /upload fills srcaddr from the
latest radio when the doc has none.

Older nodes send neither field; nothing is blanked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-11-08 23:03:08 -05:00
co-authored by Claude Opus 5.5
parent cbf45ff004
commit 5cdde85d55
4 changed files with 181 additions and 0 deletions
+10
View File
@@ -5,6 +5,7 @@ from fastapi import APIRouter, BackgroundTasks, UploadFile, File, Form, HTTPExce
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.internal.storage import upload_audio
from app.internal import dedup
from app.internal import radios
from app.internal import firestore as fstore
from app.internal import clock
from app.internal.logger import logger
@@ -24,6 +25,7 @@ async def upload_call_audio(
talkgroup_id: Optional[int] = Form(None),
talkgroup_name: Optional[str] = Form(None),
system_id: Optional[str] = Form(None),
radios_json: Optional[str] = Form(None, alias="radios"),
credentials: Optional[HTTPAuthorizationCredentials] = Security(_bearer),
):
"""
@@ -101,6 +103,14 @@ async def upload_call_audio(
updates = {"audio_gcs_uri": gcs_uri}
if node and node.get("org_id"):
updates["org_id"] = node["org_id"]
# Every radio heard on the call (server-26#304). Written here, not
# only on call_end, because the pipeline below starts before the
# node publishes call_end.
radio_fields = radios.call_fields(radios_json)
updates.update(radio_fields)
if radio_fields and existing.get("srcaddr") is None:
# Same meaning call_end gives it: the latest talker.
updates["srcaddr"] = radio_fields["radios"][-1]["radio"]
await fstore.doc_set("calls", call_id, updates)
except Exception as e:
logger.warning(f"Could not update call {call_id} with audio_gcs_uri: {e}")