feat(locations): show event count on vibration cards instead of sessions
For vibration projects, "Sessions: 0" on every location card was
misleading — monitoring sessions don't exist under the watcher-forward
pipeline. The relevant number is how many SFM events have been
attributed to the location.
get_project_locations now fans out events_for_location() concurrently
across all vibration locations in the project (via asyncio.gather) and
injects event_count into each item's payload. Sound locations are
unchanged — they still show session_count.
The template already had the conditional rendering ready from the
previous commit:
{% if item.event_count is defined and item.location.location_type == 'vibration' %}
<span><strong>{{ event_count }}</strong> events</span>
{% else %}
<span>Sessions: {{ session_count }}</span>
{% endif %}
so this commit is purely the data-layer change that activates it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,24 @@ async def get_project_locations(
|
|||||||
# Order by operator-set sort_order, then name as a stable tie-breaker.
|
# Order by operator-set sort_order, then name as a stable tie-breaker.
|
||||||
locations = query.order_by(MonitoringLocation.sort_order, MonitoringLocation.name).all()
|
locations = query.order_by(MonitoringLocation.sort_order, MonitoringLocation.name).all()
|
||||||
|
|
||||||
|
# For vibration locations, fan out event counts via SFM concurrently
|
||||||
|
# so the card layout can show "{N} events" instead of "Sessions: 0"
|
||||||
|
# (sessions don't really exist for the watcher-forward pipeline).
|
||||||
|
# Sound locations skip this and keep showing session counts.
|
||||||
|
event_counts: dict[str, int] = {}
|
||||||
|
vibration_locations = [l for l in locations if l.location_type == "vibration"]
|
||||||
|
if vibration_locations:
|
||||||
|
import asyncio
|
||||||
|
from backend.services.sfm_events import events_for_location
|
||||||
|
results = await asyncio.gather(
|
||||||
|
*(events_for_location(db, l.id, limit=1) for l in vibration_locations),
|
||||||
|
return_exceptions=True,
|
||||||
|
)
|
||||||
|
for loc, res in zip(vibration_locations, results):
|
||||||
|
if isinstance(res, Exception):
|
||||||
|
continue # leave event_counts[loc.id] unset → template falls back
|
||||||
|
event_counts[loc.id] = (res.get("stats") or {}).get("event_count", 0) or 0
|
||||||
|
|
||||||
# Enrich with assignment info, splitting active vs removed.
|
# Enrich with assignment info, splitting active vs removed.
|
||||||
active_data: list = []
|
active_data: list = []
|
||||||
removed_data: list = []
|
removed_data: list = []
|
||||||
@@ -184,6 +202,8 @@ async def get_project_locations(
|
|||||||
"assigned_unit": assigned_unit,
|
"assigned_unit": assigned_unit,
|
||||||
"session_count": session_count,
|
"session_count": session_count,
|
||||||
}
|
}
|
||||||
|
if location.id in event_counts:
|
||||||
|
item["event_count"] = event_counts[location.id]
|
||||||
if location.removed_at is None:
|
if location.removed_at is None:
|
||||||
active_data.append(item)
|
active_data.append(item)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user