feat(coord): add todos system with per-user/machine/project scoping
New coord_todos table and API endpoints (GET/POST/PUT/DELETE /api/coord/todos) supporting manual and auto-created items, sub-tasks via parent_id, and inclusive for_user/for_machine filters (OR-null) for sync/save display. sync.sh Phase 7 now shows pending todos grouped by project after every sync. CLAUDE.md documents auto-creation behavior for unresolved follow-up. Web/email pricing doc updated: block time rate clarified, INKY reference removed, dates updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
81
api/routers/coord_todos.py
Normal file
81
api/routers/coord_todos.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""Coordination to-do items router."""
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from api.database import get_db
|
||||
from api.schemas.coord_todo import CoordTodoCreate, CoordTodoResponse, CoordTodoUpdate
|
||||
from api.services import coord_todo_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[CoordTodoResponse], status_code=status.HTTP_200_OK)
|
||||
def list_todos(
|
||||
project_key: str | None = Query(default=None),
|
||||
assigned_to_user: str | None = Query(default=None),
|
||||
assigned_to_machine: str | None = Query(default=None),
|
||||
for_user: str | None = Query(default=None, description="Return items for this user OR unassigned"),
|
||||
for_machine: str | None = Query(default=None, description="Return items for this machine OR any machine"),
|
||||
status_filter: str = Query(default="pending"),
|
||||
include_subtasks: bool = Query(default=True),
|
||||
skip: int = Query(default=0, ge=0),
|
||||
limit: int = Query(default=100, ge=1, le=1000),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""List to-do items with optional filters. Pass status_filter=all to include every status."""
|
||||
todos = coord_todo_service.get_todos(
|
||||
db,
|
||||
project_key=project_key,
|
||||
assigned_to_user=assigned_to_user,
|
||||
assigned_to_machine=assigned_to_machine,
|
||||
for_user=for_user,
|
||||
for_machine=for_machine,
|
||||
status_filter=status_filter,
|
||||
include_subtasks=include_subtasks,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
)
|
||||
return [CoordTodoResponse.model_validate(t) for t in todos]
|
||||
|
||||
|
||||
@router.post("", response_model=CoordTodoResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_todo(
|
||||
data: CoordTodoCreate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Create a new to-do item."""
|
||||
todo = coord_todo_service.create_todo(db, data)
|
||||
return CoordTodoResponse.model_validate(todo)
|
||||
|
||||
|
||||
@router.get("/{todo_id}", response_model=CoordTodoResponse, status_code=status.HTTP_200_OK)
|
||||
def get_todo(
|
||||
todo_id: UUID,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get a single to-do item including its sub-tasks."""
|
||||
todo = coord_todo_service.get_todo_by_id(db, todo_id)
|
||||
return CoordTodoResponse.model_validate(todo)
|
||||
|
||||
|
||||
@router.put("/{todo_id}", response_model=CoordTodoResponse, status_code=status.HTTP_200_OK)
|
||||
def update_todo(
|
||||
todo_id: UUID,
|
||||
data: CoordTodoUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update a to-do item. Setting status to 'done' records completed_at automatically."""
|
||||
todo = coord_todo_service.update_todo(db, todo_id, data)
|
||||
return CoordTodoResponse.model_validate(todo)
|
||||
|
||||
|
||||
@router.delete("/{todo_id}", response_model=dict, status_code=status.HTTP_200_OK)
|
||||
def delete_todo(
|
||||
todo_id: UUID,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Delete a to-do item and all its sub-tasks."""
|
||||
return coord_todo_service.delete_todo(db, todo_id)
|
||||
Reference in New Issue
Block a user