88 lines
2.5 KiB
Python
88 lines
2.5 KiB
Python
"""Steps API — CRUD for task steps (checklist)."""
|
|
|
|
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import select, func
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ..database import get_db
|
|
from ..models import Step, Task
|
|
from .schemas import StepOut
|
|
from .converters import step_out
|
|
|
|
router = APIRouter(tags=["steps"])
|
|
|
|
|
|
class StepCreate(BaseModel):
|
|
title: str
|
|
|
|
|
|
class StepUpdate(BaseModel):
|
|
title: str | None = None
|
|
done: bool | None = None
|
|
|
|
|
|
@router.get("/tasks/{task_id}/steps", response_model=list[StepOut])
|
|
async def list_steps(task_id: str, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(
|
|
select(Step).where(Step.task_id == uuid.UUID(task_id)).order_by(Step.position)
|
|
)
|
|
return [step_out(s) for s in result.scalars()]
|
|
|
|
|
|
@router.post("/tasks/{task_id}/steps", response_model=StepOut)
|
|
async def create_step(task_id: str, req: StepCreate, db: AsyncSession = Depends(get_db)):
|
|
task = await db.get(Task, uuid.UUID(task_id))
|
|
if not task:
|
|
raise HTTPException(404, "Task not found")
|
|
|
|
result = await db.execute(
|
|
select(func.coalesce(func.max(Step.position), -1)).where(Step.task_id == task.id)
|
|
)
|
|
max_pos = result.scalar()
|
|
|
|
step = Step(
|
|
task_id=task.id,
|
|
title=req.title,
|
|
done=False,
|
|
position=max_pos + 1,
|
|
)
|
|
db.add(step)
|
|
await db.commit()
|
|
await db.refresh(step)
|
|
return step_out(step)
|
|
|
|
|
|
@router.patch("/tasks/{task_id}/steps/{step_id}", response_model=StepOut)
|
|
async def update_step(task_id: str, step_id: str, req: StepUpdate, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(
|
|
select(Step).where(Step.id == uuid.UUID(step_id), Step.task_id == uuid.UUID(task_id))
|
|
)
|
|
step = result.scalar_one_or_none()
|
|
if not step:
|
|
raise HTTPException(404, "Step not found")
|
|
|
|
if req.title is not None:
|
|
step.title = req.title
|
|
if req.done is not None:
|
|
step.done = req.done
|
|
|
|
await db.commit()
|
|
await db.refresh(step)
|
|
return step_out(step)
|
|
|
|
|
|
@router.delete("/tasks/{task_id}/steps/{step_id}")
|
|
async def delete_step(task_id: str, step_id: str, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(
|
|
select(Step).where(Step.id == uuid.UUID(step_id), Step.task_id == uuid.UUID(task_id))
|
|
)
|
|
step = result.scalar_one_or_none()
|
|
if not step:
|
|
raise HTTPException(404, "Step not found")
|
|
await db.delete(step)
|
|
await db.commit()
|
|
return {"ok": True}
|