From 38273684c4909907dcaf90e3a99b45cf9e2cb8da Mon Sep 17 00:00:00 2001 From: Automated Action Date: Sat, 17 May 2025 23:43:11 +0000 Subject: [PATCH] Add advanced filtering, sorting, and pagination to Todo list endpoint --- README.md | 13 ++++++++++- app/api/endpoints/todo.py | 45 +++++++++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6bce725..23074a9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ A deadass simple Todo API built with FastAPI and SQLite. This project provides a ## Features - RESTful API endpoints for Todo resource (Create, Read, Update, Delete) +- Advanced filtering, sorting, and pagination capabilities - SQLite database with SQLAlchemy ORM - Database migrations with Alembic - Health check endpoint @@ -66,7 +67,17 @@ The API will be available at http://localhost:8000 - `GET /health` - Check API and database health ### Todo Management -- `GET /api/v1/todos` - Get all todos (with optional pagination and filtering) +- `GET /api/v1/todos` - Get all todos (with advanced filtering, sorting, and pagination) + - Filtering options: + - `completed`: Filter by completion status (true/false) + - `created_after`: Filter todos created on or after date (YYYY-MM-DD) + - `created_before`: Filter todos created on or before date (YYYY-MM-DD) + - Sorting options: + - `sort_by`: Field to sort by (id, title, created_at, updated_at) + - `sort_order`: Sort direction (asc, desc) + - Pagination: + - `skip`: Number of items to skip + - `limit`: Maximum number of items to return - `GET /api/v1/todos/{todo_id}` - Get a specific todo by ID - `POST /api/v1/todos` - Create a new todo - `PUT /api/v1/todos/{todo_id}` - Update an existing todo diff --git a/app/api/endpoints/todo.py b/app/api/endpoints/todo.py index 854cd1b..6c07ca9 100644 --- a/app/api/endpoints/todo.py +++ b/app/api/endpoints/todo.py @@ -1,6 +1,8 @@ from typing import List, Optional +from datetime import datetime, date from fastapi import APIRouter, Depends, HTTPException, status, Response, Query from sqlalchemy.orm import Session +from sqlalchemy import desc, asc from app.db.database import get_db from app.models.todo import Todo as TodoModel @@ -31,23 +33,58 @@ def read_todos( skip: int = 0, limit: int = 100, completed: Optional[bool] = Query(None, description="Filter by completion status"), + created_after: Optional[date] = Query(None, description="Filter todos created after this date (YYYY-MM-DD)"), + created_before: Optional[date] = Query(None, description="Filter todos created before this date (YYYY-MM-DD)"), + sort_by: str = Query("id", description="Field to sort by (id, title, created_at, updated_at)"), + sort_order: str = Query("asc", description="Sort order (asc or desc)"), db: Session = Depends(get_db) ): """ - Get all todo items with optional filtering. + Get all todo items with optional filtering, sorting, and pagination. - **skip**: Number of items to skip (pagination) - - **limit**: Maximum number of items to return - - **completed**: Optional filter by completion status + - **limit**: Maximum number of items to return (pagination) + - **completed**: Optional filter by completion status (true/false) + - **created_after**: Filter todos created on or after this date (format: YYYY-MM-DD) + - **created_before**: Filter todos created on or before this date (format: YYYY-MM-DD) + - **sort_by**: Field to sort by (options: id, title, created_at, updated_at) + - **sort_order**: Sort order (options: asc, desc) Returns a list of todo items. """ query = db.query(TodoModel) - # Apply filter if completed parameter is provided + # Apply filters if completed is not None: query = query.filter(TodoModel.completed == completed) + if created_after: + # Convert date to datetime with time at start of day (00:00:00) + start_datetime = datetime.combine(created_after, datetime.min.time()) + query = query.filter(TodoModel.created_at >= start_datetime) + + if created_before: + # Convert date to datetime with time at end of day (23:59:59) + end_datetime = datetime.combine(created_before, datetime.max.time()) + query = query.filter(TodoModel.created_at <= end_datetime) + + # Apply sorting + if sort_by not in ["id", "title", "created_at", "updated_at"]: + sort_by = "id" # Default to id if invalid field provided + + if sort_order.lower() not in ["asc", "desc"]: + sort_order = "asc" # Default to ascending if invalid order provided + + # Get the attribute to sort by + sort_attr = getattr(TodoModel, sort_by) + + # Apply sorting direction + if sort_order.lower() == "desc": + query = query.order_by(desc(sort_attr)) + else: + query = query.order_by(asc(sort_attr)) + + # Apply pagination todos = query.offset(skip).limit(limit).all() return todos