e9dfb6e293
- 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
112 lines
3.3 KiB
Python
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
|