2026-02-15 16:13:41 +08:00

150 lines
4.1 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import Optional, List
from datetime import datetime, timezone
from app.database import get_db
from app.models.todo import Todo
from app.schemas.todo import TodoCreate, TodoUpdate, TodoResponse
from app.routers.auth import get_current_session
from app.models.settings import Settings
router = APIRouter()
@router.get("/", response_model=List[TodoResponse])
async def get_todos(
completed: Optional[bool] = Query(None),
priority: Optional[str] = Query(None),
category: Optional[str] = Query(None),
search: Optional[str] = Query(None),
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Get all todos with optional filters."""
query = select(Todo)
if completed is not None:
query = query.where(Todo.completed == completed)
if priority:
query = query.where(Todo.priority == priority)
if category:
query = query.where(Todo.category == category)
if search:
query = query.where(Todo.title.ilike(f"%{search}%"))
query = query.order_by(Todo.created_at.desc())
result = await db.execute(query)
todos = result.scalars().all()
return todos
@router.post("/", response_model=TodoResponse, status_code=201)
async def create_todo(
todo: TodoCreate,
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Create a new todo."""
new_todo = Todo(**todo.model_dump())
db.add(new_todo)
await db.commit()
await db.refresh(new_todo)
return new_todo
@router.get("/{todo_id}", response_model=TodoResponse)
async def get_todo(
todo_id: int,
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Get a specific todo by ID."""
result = await db.execute(select(Todo).where(Todo.id == todo_id))
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
return todo
@router.put("/{todo_id}", response_model=TodoResponse)
async def update_todo(
todo_id: int,
todo_update: TodoUpdate,
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Update a todo."""
result = await db.execute(select(Todo).where(Todo.id == todo_id))
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
update_data = todo_update.model_dump(exclude_unset=True)
# Handle completion timestamp
if "completed" in update_data:
if update_data["completed"] and not todo.completed:
update_data["completed_at"] = datetime.now(timezone.utc)
elif not update_data["completed"]:
update_data["completed_at"] = None
for key, value in update_data.items():
setattr(todo, key, value)
await db.commit()
await db.refresh(todo)
return todo
@router.delete("/{todo_id}", status_code=204)
async def delete_todo(
todo_id: int,
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Delete a todo."""
result = await db.execute(select(Todo).where(Todo.id == todo_id))
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
await db.delete(todo)
await db.commit()
return None
@router.patch("/{todo_id}/toggle", response_model=TodoResponse)
async def toggle_todo(
todo_id: int,
db: AsyncSession = Depends(get_db),
current_user: Settings = Depends(get_current_session)
):
"""Toggle todo completion status."""
result = await db.execute(select(Todo).where(Todo.id == todo_id))
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
todo.completed = not todo.completed
todo.completed_at = datetime.now(timezone.utc) if todo.completed else None
await db.commit()
await db.refresh(todo)
return todo