From af34ce88dfadb0c288120995356605a3e70d3ec4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 17:31:56 +0000 Subject: [PATCH] feat(api): implement proper pagination for file list API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change default per_page from 50 to 25 - Rename total_items → total, total_pages → pages in pagination response - Add next/previous URL fields to pagination response - Update view and template to use new field names - Update tests and API docs Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/api/files.py | 22 +++++++++++++++------- app/views/files.py | 8 ++++---- docs/API.md | 10 ++++++---- frontend/templates/files.html | 12 ++++++------ tests/test_api_files_comprehensive.py | 6 +++--- tests/test_file_listing.py | 6 +++--- 6 files changed, 37 insertions(+), 27 deletions(-) diff --git a/app/api/files.py b/app/api/files.py index 937d117d..e0732252 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -49,7 +49,7 @@ def list_files_api( request: Request, db: DbSession, page: int = Query(1, ge=1, description="Page number"), - per_page: int = Query(50, ge=1, le=200, description="Items per page"), + per_page: int = Query(25, ge=1, le=200, description="Items per page"), sort_by: str = Query( "created_at", description="Sort field: id, original_filename, file_size, mime_type, created_at, status", @@ -69,7 +69,7 @@ def list_files_api( Query Parameters: - page: Page number (default: 1) - - per_page: Items per page (default: 50, max: 200) + - per_page: Items per page (default: 25, max: 200) - sort_by: Field to sort by (default: created_at) - sort_order: asc or desc (default: desc) - search: Search in filename @@ -85,9 +85,11 @@ def list_files_api( "files": [...], "pagination": { "page": 1, - "per_page": 50, - "total_items": 150, - "total_pages": 3 + "per_page": 25, + "total": 150, + "pages": 6, + "next": "http://host/api/files?page=2", + "previous": null } } """ @@ -205,13 +207,19 @@ def list_files_api( # Calculate pagination info total_pages = (total_items + per_page - 1) // per_page + # Build next / previous page URLs by replacing the page query parameter + next_url = str(request.url.include_query_params(page=page + 1)) if page < total_pages else None + previous_url = str(request.url.include_query_params(page=page - 1)) if page > 1 else None + return { "files": result, "pagination": { "page": page, "per_page": per_page, - "total_items": total_items, - "total_pages": total_pages, + "total": total_items, + "pages": total_pages, + "next": next_url, + "previous": previous_url, }, } diff --git a/app/views/files.py b/app/views/files.py index 92245809..a7ce116e 100644 --- a/app/views/files.py +++ b/app/views/files.py @@ -25,7 +25,7 @@ def files_page( request: Request, db: Session = Depends(get_db), page: int = Query(1, ge=1), - per_page: int = Query(50, ge=1, le=200), + per_page: int = Query(25, ge=1, le=200), sort_by: str = Query("created_at"), sort_order: str = Query("desc"), search: Optional[str] = Query(None), @@ -168,8 +168,8 @@ def files_page( "pagination": { "page": page, "per_page": per_page, - "total_items": total_items, - "total_pages": total_pages, + "total": total_items, + "pages": total_pages, }, "sort_by": sort_by, "sort_order": sort_order, @@ -196,7 +196,7 @@ def files_page( { "request": request, "files": [], - "pagination": {"page": 1, "per_page": per_page, "total_items": 0, "total_pages": 0}, + "pagination": {"page": 1, "per_page": per_page, "total": 0, "pages": 0}, "error": str(e), "upload_concurrency": settings.upload_concurrency, "upload_queue_delay_ms": settings.upload_queue_delay_ms, diff --git a/docs/API.md b/docs/API.md index a877dece..5751d788 100644 --- a/docs/API.md +++ b/docs/API.md @@ -285,7 +285,7 @@ Retrieve a paginated list of processed files with advanced filtering and sorting **Query Parameters**: - `page` (optional, default: 1): Page number -- `per_page` (optional, default: 50, max: 200): Items per page +- `per_page` (optional, default: 25, max: 200): Items per page - `sort_by` (optional, default: created_at): Sort field (`id`, `original_filename`, `file_size`, `mime_type`, `created_at`) - `sort_order` (optional, default: desc): Sort order (`asc` or `desc`) - `search` (optional): Search in filename (partial match) @@ -324,9 +324,11 @@ GET /api/files?status=completed&mime_type=application/pdf&tags=invoice&date_from ], "pagination": { "page": 1, - "per_page": 50, - "total_items": 150, - "total_pages": 3 + "per_page": 25, + "total": 150, + "pages": 6, + "next": "http://host/api/files?page=2", + "previous": null } } ``` diff --git a/frontend/templates/files.html b/frontend/templates/files.html index 6c2e373b..c483c0e1 100644 --- a/frontend/templates/files.html +++ b/frontend/templates/files.html @@ -783,12 +783,12 @@ - {% if pagination.total_pages > 1 %} + {% if pagination.pages > 1 %} diff --git a/tests/test_api_files_comprehensive.py b/tests/test_api_files_comprehensive.py index 43ffb2c0..3f9200e5 100644 --- a/tests/test_api_files_comprehensive.py +++ b/tests/test_api_files_comprehensive.py @@ -27,7 +27,7 @@ class TestListFilesAPI: assert "files" in data assert "pagination" in data assert len(data["files"]) == 0 - assert data["pagination"]["total_items"] == 0 + assert data["pagination"]["total"] == 0 def test_list_files_with_data(self, client: TestClient, db_session): """Test listing files with existing data.""" @@ -54,7 +54,7 @@ class TestListFilesAPI: assert response.status_code == 200 data = response.json() assert len(data["files"]) == 2 - assert data["pagination"]["total_items"] == 2 + assert data["pagination"]["total"] == 2 def test_list_files_with_pagination(self, client: TestClient, db_session): """Test pagination parameters.""" @@ -77,7 +77,7 @@ class TestListFilesAPI: assert len(data["files"]) == 5 assert data["pagination"]["page"] == 1 assert data["pagination"]["per_page"] == 5 - assert data["pagination"]["total_pages"] == 2 + assert data["pagination"]["pages"] == 2 # Request page 2 response = client.get("/api/files?page=2&per_page=5") diff --git a/tests/test_file_listing.py b/tests/test_file_listing.py index 0804f744..9c2fa1d0 100644 --- a/tests/test_file_listing.py +++ b/tests/test_file_listing.py @@ -22,7 +22,7 @@ class TestFileListingPagination: assert "pagination" in data assert isinstance(data["files"], list) assert len(data["files"]) == 0 - assert data["pagination"]["total_items"] == 0 + assert data["pagination"]["total"] == 0 def test_list_files_with_data(self, client: TestClient, db_session): """Test listing files with sample data.""" @@ -42,7 +42,7 @@ class TestFileListingPagination: assert response.status_code == 200 data = response.json() assert len(data["files"]) == 5 - assert data["pagination"]["total_items"] == 5 + assert data["pagination"]["total"] == 5 assert data["pagination"]["page"] == 1 def test_pagination_works(self, client: TestClient, db_session): @@ -65,7 +65,7 @@ class TestFileListingPagination: data = response.json() assert len(data["files"]) == 5 assert data["pagination"]["page"] == 1 - assert data["pagination"]["total_pages"] == 2 + assert data["pagination"]["pages"] == 2 # Get second page response = client.get("/api/files?page=2&per_page=5")