Date: 2026-05-28 Status: design approved, pending spec review Scope: first of several planned captures-list filters; this PR ships the processed filter only.
Add a "Processing status" filter to the Captures (SourceImage) list view, letting users narrow to captures that have been processed, not processed, or all (no filter). Lay the groundwork (a planned filter set) for additional filters in later PRs.
"Processed" = the image has been run through detection. Because PR #1093 writes a null Detection marker for the "processed, found nothing" case, the presence of any Detection row is an accurate signal of "was processed."
The filter already exists and is exercised by the list endpoint:
ami/main/api/views.py:630-636—SourceImageViewSet.filter_by_has_detectionshandles?has_detections=true|falseby annotatingExists(Detection.objects.filter(source_image=OuterRef("pk")))and filtering on it. (SourceImageViewSetatviews.py:528.)- Called from
get_querysetonly for thelistaction (views.py:600), which is what the captures list uses.
Decision: reuse the existing has_detections query param. Zero backend change, already
tested behavior. The param name (has_detections) means "was processed" because of the
null-marker convention; we surface it to users with the label "Processing status" and keep
has_detections as the internal query key. This name/meaning gap is the one known wart and
is documented here rather than fixed (a was_processed alias was considered and rejected to
avoid extra surface area).
-
New component
ui/src/components/filtering/filters/processing-status-filter.tsx. Model onverification-status-filter.tsx. Two options: "Processed" (true) / "Not processed" (false). WireonValueChange={onAdd}directly so both true and false are settable. (The genericBooleanFilteris unusable here: its "No" branch callsonClear()instead of filtering to false — seeboolean-filter.tsx:21-27.) Use a translated label string for the two options (add toutils/languageif needed). -
Register the component in
ui/src/components/filtering/filter-control.tsxComponentMap:has_detections: ProcessingStatusFilter. -
Register the filter in
ui/src/utils/useFilters.tsAVAILABLE_FILTERS:{ label: 'Processing status', field: 'has_detections', tooltip: { text: ... } }. -
Render it on the captures page
ui/src/pages/captures/captures.tsx(inside the existingFilterSection, alongsidedeploymentandcollections):<FilterControl field="has_detections" />.
State, URL params, page reset, and the clear-X ("All") behavior all come from the existing
useFilters machinery — no changes there.
UI select -> addFilter('has_detections', 'true'|'false') -> URL search param ->
useFilters -> useCaptures builds ?has_detections=... via getFetchUrl
(ui/src/data-services/utils.ts) -> DRF filter_by_has_detections -> filtered queryset.
Clear-X removes the param -> "All".
- Backend: verify existing coverage for
?has_detections=true|falseon the captures list endpoint; add a test if missing (both branches + absent param). - Frontend: manual verification against the running stack — select Processed, Not processed, and clear; confirm result counts change and the URL param round-trips.
To live in a collapsible "Advanced" FilterSection on the captures page later:
- Date range —
date_start/date_endalready in the FE registry with aDateFiltercomponent, but the SourceImage viewset needs backend support mapping them to atimestamprange (new work). - Station — already available via the existing
deploymentfilter. - Site — add
deployment__research_sitetofilterset_fields+ a Site filter component. - Device — add
deployment__devicetofilterset_fields+ a Device filter component.