Files
home_hub/backend/app/api/todos.py
T
gilles e9dfb6e293 feat(todos): domains[], photo_path, gps_lat/lng — modèle, schemas, API, tri par date
- Modèle SQLAlchemy : ajout de domains (ARRAY), photo_path, gps_lat, gps_lng ; import Float
- Schemas Pydantic : domain → domains dans TodoCreate, TodoUpdate, TodoResponse ; ajout photo_path, gps_lat, gps_lng
- API GET /api/todos : filtre domain (param URL) redirigé vers domains.contains([domain]) sur le champ ARRAY
- Tests : domain → domains dans les payloads POST ; assertion domains == ["informatique"] dans test_creer_todo
2026-05-24 16:04:21 +02:00

112 lines
3.3 KiB
Python

import uuid
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import Response
from sqlalchemy import select, and_
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_session
from app.models.todos import TodoItem
from app.schemas.todos import TodoCreate, TodoUpdate, PostponeRequest, TodoResponse
router = APIRouter()
@router.get("/", response_model=list[TodoResponse])
async def list_todos(
domain: str | None = None,
status: str | None = "pending",
priority: str | None = None,
tag: str | None = None,
due_after: datetime | None = None,
due_before: datetime | None = None,
limit: int = 200,
session: AsyncSession = Depends(get_session),
):
conditions = []
if domain:
conditions.append(TodoItem.domains.contains([domain]))
if status:
conditions.append(TodoItem.status == status)
if priority:
conditions.append(TodoItem.priority == priority)
if tag:
conditions.append(TodoItem.tags.contains([tag]))
if due_after:
conditions.append(TodoItem.due_date >= due_after)
if due_before:
conditions.append(TodoItem.due_date <= due_before)
stmt = select(TodoItem)
if conditions:
stmt = stmt.where(and_(*conditions))
stmt = stmt.order_by(TodoItem.due_date.asc().nulls_last(), TodoItem.created_at.desc())
stmt = stmt.limit(limit)
result = await session.execute(stmt)
return result.scalars().all()
@router.post("/", response_model=TodoResponse, status_code=201)
async def create_todo(
payload: TodoCreate,
session: AsyncSession = Depends(get_session),
):
item = TodoItem(**payload.model_dump())
session.add(item)
await session.commit()
await session.refresh(item)
return item
@router.patch("/{item_id}", response_model=TodoResponse)
async def update_todo(
item_id: uuid.UUID,
payload: TodoUpdate,
session: AsyncSession = Depends(get_session),
):
item = await session.get(TodoItem, item_id)
if not item:
raise HTTPException(status_code=404, detail="Tâche introuvable")
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(item, field, value)
item.updated_at = datetime.now(timezone.utc)
await session.commit()
await session.refresh(item)
return item
@router.delete("/{item_id}", status_code=204)
async def delete_todo(
item_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
):
item = await session.get(TodoItem, item_id)
if not item:
raise HTTPException(status_code=404, detail="Tâche introuvable")
await session.delete(item)
await session.commit()
return Response(status_code=204)
@router.post("/{item_id}/postpone", response_model=TodoResponse)
async def postpone_todo(
item_id: uuid.UUID,
payload: PostponeRequest,
session: AsyncSession = Depends(get_session),
):
item = await session.get(TodoItem, item_id)
if not item:
raise HTTPException(status_code=404, detail="Tâche introuvable")
now = datetime.now(timezone.utc)
base = item.due_date if item.due_date else now
item.due_date = base + timedelta(days=payload.days)
item.postponed_count += 1
item.updated_at = now
await session.commit()
await session.refresh(item)
return item