146 lines
5.3 KiB
Python
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) |