146 lines
5.3 KiB
Python

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
from app.schemas.todo import Todo, TodoCreate, TodoUpdate
router = APIRouter()
@router.post("/todos", response_model=Todo, status_code=status.HTTP_201_CREATED, summary="Create a new todo")
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
"""
Create a new todo item.
- **title**: Required title of the todo
- **description**: Optional detailed description
- **completed**: Whether the todo is completed (defaults to False)
Returns the created todo item.
"""
db_todo = TodoModel(**todo.model_dump())
db.add(db_todo)
db.commit()
db.refresh(db_todo)
return db_todo
@router.get("/todos", response_model=List[Todo], summary="Get all todos")
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, sorting, and pagination.
- **skip**: Number of items to skip (pagination)
- **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 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
@router.get("/todos/{todo_id}", response_model=Todo, summary="Get a specific todo")
def read_todo(todo_id: int, db: Session = Depends(get_db)):
"""
Get a specific todo item by ID.
- **todo_id**: The ID of the todo to retrieve
Returns the todo item or raises a 404 if not found.
"""
db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
if db_todo is None:
raise HTTPException(status_code=404, detail="Todo not found")
return db_todo
@router.put("/todos/{todo_id}", response_model=Todo, summary="Update a todo")
def update_todo(todo_id: int, todo: TodoUpdate, db: Session = Depends(get_db)):
"""
Update a todo item.
- **todo_id**: The ID of the todo to update
- **todo**: The updated todo data
Returns the updated todo item or raises a 404 if not found.
"""
db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
if db_todo is None:
raise HTTPException(status_code=404, detail="Todo not found")
# Update fields if provided in the request
todo_data = todo.model_dump(exclude_unset=True)
for key, value in todo_data.items():
setattr(db_todo, key, value)
db.commit()
db.refresh(db_todo)
return db_todo
@router.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None, summary="Delete a todo")
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
"""
Delete a todo item.
- **todo_id**: The ID of the todo to delete
Returns no content on success or raises a 404 if not found.
"""
db_todo = db.query(TodoModel).filter(TodoModel.id == todo_id).first()
if db_todo is None:
raise HTTPException(status_code=404, detail="Todo not found")
db.delete(db_todo)
db.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)