page the analysis run listings

Detection and segmentation run listings returned every run a project had ever
produced. Runs accumulate with every analysis while the panel only ever draws
the recent ones, so the response grew without bound for no benefit.

Both take limit and offset now and report total, limit, offset and truncated,
matching the result listings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jens
2026-08-22 22:18:08 +02:00
co-authored by Claude Opus 5
parent 6572e4ad5f
commit c4d873149b
7 changed files with 55 additions and 4 deletions
+13 -1
View File
@@ -281,6 +281,8 @@ class DetectionService:
*,
project_id: uuid.UUID | None = None,
dataset_id: uuid.UUID | None = None,
limit: int | None = None,
offset: int = 0,
) -> DetectionRunListResponse:
query = db.query(AnalysisRun).filter(AnalysisRun.analysis_type == "detection")
if project_id is not None:
@@ -288,7 +290,16 @@ class DetectionService:
if dataset_id is not None:
query = query.filter(AnalysisRun.dataset_id == dataset_id)
rows = query.order_by(AnalysisRun.created_at.desc()).all()
return DetectionRunListResponse(items=[DetectionRunRead.model_validate(row) for row in rows], total=len(rows))
# Runs accumulate with every analysis; the panel draws the recent ones.
resolved_limit = DetectionService.DEFAULT_RUN_LIST_LIMIT if limit is None else int(limit)
page, total, truncated = DetectionService.paginate(rows, limit=resolved_limit, offset=offset)
return DetectionRunListResponse(
items=[DetectionRunRead.model_validate(row) for row in page],
total=total,
limit=resolved_limit,
offset=max(0, int(offset)),
truncated=truncated,
)
@staticmethod
def list_detections(
@@ -774,6 +785,7 @@ class DetectionService:
# A regional run holds tens of thousands of detections; the results table
# and the map overlay both read them after every run.
DEFAULT_RESULT_LIMIT = 2_000
DEFAULT_RUN_LIST_LIMIT = 200
@staticmethod
def paginate(rows: list[Any], *, limit: int, offset: int) -> tuple[list[Any], int, bool]:
+12 -1
View File
@@ -244,6 +244,8 @@ class SegmentationService:
*,
project_id: uuid.UUID | None = None,
dataset_id: uuid.UUID | None = None,
limit: int | None = None,
offset: int = 0,
) -> SegmentationRunListResponse:
query = db.query(AnalysisRun).filter(AnalysisRun.analysis_type == "segmentation")
if project_id is not None:
@@ -251,7 +253,16 @@ class SegmentationService:
if dataset_id is not None:
query = query.filter(AnalysisRun.dataset_id == dataset_id)
rows = query.order_by(AnalysisRun.created_at.desc()).all()
return SegmentationRunListResponse(items=[SegmentationRunRead.model_validate(row) for row in rows], total=len(rows))
# Runs accumulate with every analysis; the panel draws the recent ones.
resolved_limit = DetectionService.DEFAULT_RUN_LIST_LIMIT if limit is None else int(limit)
page, total, truncated = DetectionService.paginate(rows, limit=resolved_limit, offset=offset)
return SegmentationRunListResponse(
items=[SegmentationRunRead.model_validate(row) for row in page],
total=total,
limit=resolved_limit,
offset=max(0, int(offset)),
truncated=truncated,
)
@staticmethod
def list_segmentations(