feat: on-watch stats scope selector (page vs all)
Adds an icon selector to the "On watch" provider deck letting users choose between page-scoped stats (legacy) and full-corpus stats that aggregate across every event matching the current filters. Backend returns a new provider_event_counts map alongside the paginated events.
This commit is contained in:
@@ -77,6 +77,32 @@ async def get_status(
|
||||
count_query = select(func.count()).select_from(events_query.subquery())
|
||||
total_events = (await session.exec(count_query)).one()
|
||||
|
||||
# Aggregate per-provider event counts across ALL matching events (ignoring
|
||||
# offset/limit) so the "On watch" deck can show full-corpus stats when the
|
||||
# user opts out of page-scoped stats. Sums ``assets_count`` (falling back to
|
||||
# 1 per event) to mirror the frontend's per-page derivation.
|
||||
provider_counts_query = (
|
||||
select(
|
||||
EventLog.provider_id,
|
||||
EventLog.provider_name,
|
||||
func.sum(func.coalesce(EventLog.assets_count, 1)).label("total"),
|
||||
)
|
||||
.where(EventLog.user_id == user.id)
|
||||
.group_by(EventLog.provider_id, EventLog.provider_name)
|
||||
)
|
||||
if event_type:
|
||||
provider_counts_query = provider_counts_query.where(EventLog.event_type == event_type)
|
||||
if provider_id is not None:
|
||||
provider_counts_query = provider_counts_query.where(EventLog.provider_id == provider_id)
|
||||
if search:
|
||||
provider_counts_query = provider_counts_query.where(
|
||||
EventLog.collection_name.contains(search)
|
||||
| EventLog.tracker_name.contains(search)
|
||||
| EventLog.action_name.contains(search)
|
||||
| EventLog.provider_name.contains(search)
|
||||
)
|
||||
provider_counts_rows = (await session.exec(provider_counts_query)).all()
|
||||
|
||||
# Sort
|
||||
if sort == "oldest":
|
||||
events_query = events_query.order_by(EventLog.created_at.asc())
|
||||
@@ -98,8 +124,11 @@ async def get_status(
|
||||
)).all()
|
||||
tracker_name_map = {tid: tname for tid, tname in tracker_rows}
|
||||
|
||||
# Resolve live provider names similarly
|
||||
# Resolve live provider names similarly. Includes IDs from the aggregated
|
||||
# provider counts so the "all events" deck shows up-to-date names even for
|
||||
# providers that don't appear on the current page.
|
||||
provider_ids = {e.provider_id for e in event_rows if e.provider_id is not None}
|
||||
provider_ids.update(pid for pid, _pname, _total in provider_counts_rows if pid is not None)
|
||||
provider_name_map: dict[int, str] = {}
|
||||
if provider_ids:
|
||||
provider_rows = (await session.exec(
|
||||
@@ -189,11 +218,26 @@ async def get_status(
|
||||
return _display_action_name(e) or e.collection_name
|
||||
return e.collection_name
|
||||
|
||||
# Build the provider event count map keyed by live provider name (matches
|
||||
# the frontend's keying scheme). Falls back to the stored snapshot name
|
||||
# when the provider has been deleted.
|
||||
provider_event_counts: dict[str, int] = {}
|
||||
for pid, pname, total in provider_counts_rows:
|
||||
display_name = (
|
||||
provider_name_map.get(pid) if pid is not None else None
|
||||
) or pname or ""
|
||||
if not display_name:
|
||||
continue
|
||||
provider_event_counts[display_name] = (
|
||||
provider_event_counts.get(display_name, 0) + int(total or 0)
|
||||
)
|
||||
|
||||
return {
|
||||
"providers": providers_count,
|
||||
"trackers": {"total": len(trackers), "active": active_count},
|
||||
"targets": targets_count,
|
||||
"total_events": total_events,
|
||||
"provider_event_counts": provider_event_counts,
|
||||
"recent_events": [
|
||||
{
|
||||
"id": e.id,
|
||||
|
||||
Reference in New Issue
Block a user