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
This commit is contained in:
2026-05-24 16:04:21 +02:00
parent a97894437a
commit e9dfb6e293
4 changed files with 22 additions and 8 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ async def list_todos(
):
conditions = []
if domain:
conditions.append(TodoItem.domain == domain)
conditions.append(TodoItem.domains.contains([domain]))
if status:
conditions.append(TodoItem.status == status)
if priority:
+5 -1
View File
@@ -1,6 +1,6 @@
import uuid
from datetime import datetime
from sqlalchemy import String, Text, Integer, TIMESTAMP, text
from sqlalchemy import String, Text, Integer, Float, TIMESTAMP, text
from sqlalchemy.dialects.postgresql import UUID, ARRAY
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
@@ -15,8 +15,12 @@ class TodoItem(Base):
body: Mapped[str | None] = mapped_column(Text)
url: Mapped[str | None] = mapped_column(Text)
domain: Mapped[str | None] = mapped_column(String(50))
domains: Mapped[list[str]] = mapped_column(ARRAY(String(50)), server_default=text("'{}'::varchar[]"))
category: Mapped[str | None] = mapped_column(String(50))
tags: Mapped[list[str]] = mapped_column(ARRAY(String(50)), server_default=text("'{}'::varchar[]"))
photo_path: Mapped[str | None] = mapped_column(Text)
gps_lat: Mapped[float | None] = mapped_column(Float)
gps_lng: Mapped[float | None] = mapped_column(Float)
status: Mapped[str] = mapped_column(String(20), server_default="pending")
priority: Mapped[str] = mapped_column(String(10), server_default="medium")
due_date: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True))
+12 -3
View File
@@ -8,24 +8,30 @@ class TodoCreate(BaseModel):
title: str
body: str | None = None
url: str | None = None
domain: str | None = None
domains: list[str] = Field(default_factory=list)
category: str | None = None
tags: list[str] = Field(default_factory=list)
status: Literal["pending", "done", "cancelled"] = "pending"
priority: Literal["low", "medium", "high"] = "medium"
due_date: datetime | None = None
photo_path: str | None = None
gps_lat: float | None = None
gps_lng: float | None = None
class TodoUpdate(BaseModel):
title: str | None = None
body: str | None = None
url: str | None = None
domain: str | None = None
domains: list[str] | None = None
category: str | None = None
tags: list[str] | None = None
status: Literal["pending", "done", "cancelled"] | None = None
priority: Literal["low", "medium", "high"] | None = None
due_date: datetime | None = None
photo_path: str | None = None
gps_lat: float | None = None
gps_lng: float | None = None
class PostponeRequest(BaseModel):
@@ -39,7 +45,7 @@ class TodoResponse(BaseModel):
title: str
body: str | None
url: str | None
domain: str | None
domains: list[str]
category: str | None
tags: list[str]
status: str
@@ -49,3 +55,6 @@ class TodoResponse(BaseModel):
created_at: datetime
updated_at: datetime | None
owner_id: uuid.UUID | None
photo_path: str | None
gps_lat: float | None
gps_lng: float | None
+4 -3
View File
@@ -13,7 +13,7 @@ async def cleanup(db_session):
async def test_creer_todo(client):
resp = await client.post("/api/todos/", json={
"title": "TEST_tâche simple",
"domain": "informatique",
"domains": ["informatique"],
"priority": "high",
})
assert resp.status_code == 201
@@ -22,6 +22,7 @@ async def test_creer_todo(client):
assert data["status"] == "pending"
assert data["postponed_count"] == 0
assert data["tags"] == []
assert data["domains"] == ["informatique"]
async def test_lister_todos_filtre_status(client):
@@ -36,8 +37,8 @@ async def test_lister_todos_filtre_status(client):
async def test_lister_todos_filtre_domaine(client):
await client.post("/api/todos/", json={"title": "TEST_info", "domain": "informatique"})
await client.post("/api/todos/", json={"title": "TEST_jardin", "domain": "jardin"})
await client.post("/api/todos/", json={"title": "TEST_info", "domains": ["informatique"]})
await client.post("/api/todos/", json={"title": "TEST_jardin", "domains": ["jardin"]})
resp = await client.get("/api/todos/?domain=informatique&status=")
assert resp.status_code == 200