From 4787f044e55cf5636860ed310a6737e18fd3f89c Mon Sep 17 00:00:00 2001 From: gilles Date: Sat, 21 Feb 2026 21:22:58 +0100 Subject: [PATCH] =?UTF-8?q?feat(backend):=20CRUD=20vari=C3=A9t=C3=A9s,=20p?= =?UTF-8?q?lantations,=20t=C3=A2ches=20+=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- backend/app/routers/plantings.py | 68 +++++++++++++++++++++++++++++++- backend/app/routers/tasks.py | 63 ++++++++++++++++++++++++++++- backend/app/routers/varieties.py | 52 +++++++++++++++++++++++- backend/tests/test_plantings.py | 25 ++++++++++++ backend/tests/test_tasks.py | 28 +++++++++++++ backend/tests/test_varieties.py | 26 ++++++++++++ 6 files changed, 256 insertions(+), 6 deletions(-) create mode 100644 backend/tests/test_plantings.py create mode 100644 backend/tests/test_tasks.py create mode 100644 backend/tests/test_varieties.py diff --git a/backend/app/routers/plantings.py b/backend/app/routers/plantings.py index da060ab..f4e263b 100644 --- a/backend/app/routers/plantings.py +++ b/backend/app/routers/plantings.py @@ -1,2 +1,66 @@ -from fastapi import APIRouter -router = APIRouter() +from datetime import datetime +from typing import List +from fastapi import APIRouter, Depends, HTTPException, status +from sqlmodel import Session, select +from app.database import get_session +from app.models.planting import Planting, PlantingEvent + +router = APIRouter(tags=["plantations"]) + + +@router.get("/plantings", response_model=List[Planting]) +def list_plantings(session: Session = Depends(get_session)): + return session.exec(select(Planting)).all() + + +@router.post("/plantings", response_model=Planting, status_code=status.HTTP_201_CREATED) +def create_planting(p: Planting, session: Session = Depends(get_session)): + session.add(p) + session.commit() + session.refresh(p) + return p + + +@router.get("/plantings/{id}", response_model=Planting) +def get_planting(id: int, session: Session = Depends(get_session)): + p = session.get(Planting, id) + if not p: + raise HTTPException(status_code=404, detail="Plantation introuvable") + return p + + +@router.put("/plantings/{id}", response_model=Planting) +def update_planting(id: int, data: Planting, session: Session = Depends(get_session)): + p = session.get(Planting, id) + if not p: + raise HTTPException(status_code=404, detail="Plantation introuvable") + for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items(): + setattr(p, k, v) + p.updated_at = datetime.utcnow() + session.add(p) + session.commit() + session.refresh(p) + return p + + +@router.delete("/plantings/{id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_planting(id: int, session: Session = Depends(get_session)): + p = session.get(Planting, id) + if not p: + raise HTTPException(status_code=404, detail="Plantation introuvable") + session.delete(p) + session.commit() + + +@router.get("/plantings/{id}/events", response_model=List[PlantingEvent]) +def list_events(id: int, session: Session = Depends(get_session)): + return session.exec(select(PlantingEvent).where(PlantingEvent.planting_id == id)).all() + + +@router.post("/plantings/{id}/events", response_model=PlantingEvent, status_code=status.HTTP_201_CREATED) +def create_event(id: int, e: PlantingEvent, session: Session = Depends(get_session)): + e.planting_id = id + session.add(e) + session.commit() + session.refresh(e) + return e diff --git a/backend/app/routers/tasks.py b/backend/app/routers/tasks.py index da060ab..5e6b5d7 100644 --- a/backend/app/routers/tasks.py +++ b/backend/app/routers/tasks.py @@ -1,2 +1,61 @@ -from fastapi import APIRouter -router = APIRouter() +from datetime import datetime +from typing import List, Optional +from fastapi import APIRouter, Depends, HTTPException, status +from sqlmodel import Session, select +from app.database import get_session +from app.models.task import Task + +router = APIRouter(tags=["tâches"]) + + +@router.get("/tasks", response_model=List[Task]) +def list_tasks( + statut: Optional[str] = None, + garden_id: Optional[int] = None, + session: Session = Depends(get_session), +): + q = select(Task) + if statut: + q = q.where(Task.statut == statut) + if garden_id: + q = q.where(Task.garden_id == garden_id) + return session.exec(q).all() + + +@router.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED) +def create_task(t: Task, session: Session = Depends(get_session)): + session.add(t) + session.commit() + session.refresh(t) + return t + + +@router.get("/tasks/{id}", response_model=Task) +def get_task(id: int, session: Session = Depends(get_session)): + t = session.get(Task, id) + if not t: + raise HTTPException(status_code=404, detail="Tâche introuvable") + return t + + +@router.put("/tasks/{id}", response_model=Task) +def update_task(id: int, data: Task, session: Session = Depends(get_session)): + t = session.get(Task, id) + if not t: + raise HTTPException(status_code=404, detail="Tâche introuvable") + for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items(): + setattr(t, k, v) + t.updated_at = datetime.utcnow() + session.add(t) + session.commit() + session.refresh(t) + return t + + +@router.delete("/tasks/{id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_task(id: int, session: Session = Depends(get_session)): + t = session.get(Task, id) + if not t: + raise HTTPException(status_code=404, detail="Tâche introuvable") + session.delete(t) + session.commit() diff --git a/backend/app/routers/varieties.py b/backend/app/routers/varieties.py index da060ab..6d988de 100644 --- a/backend/app/routers/varieties.py +++ b/backend/app/routers/varieties.py @@ -1,2 +1,50 @@ -from fastapi import APIRouter -router = APIRouter() +from typing import List +from fastapi import APIRouter, Depends, HTTPException, status +from sqlmodel import Session, select +from app.database import get_session +from app.models.plant import PlantVariety + +router = APIRouter(tags=["variétés"]) + + +@router.get("/varieties", response_model=List[PlantVariety]) +def list_varieties(session: Session = Depends(get_session)): + return session.exec(select(PlantVariety)).all() + + +@router.post("/varieties", response_model=PlantVariety, status_code=status.HTTP_201_CREATED) +def create_variety(v: PlantVariety, session: Session = Depends(get_session)): + session.add(v) + session.commit() + session.refresh(v) + return v + + +@router.get("/varieties/{id}", response_model=PlantVariety) +def get_variety(id: int, session: Session = Depends(get_session)): + v = session.get(PlantVariety, id) + if not v: + raise HTTPException(status_code=404, detail="Variété introuvable") + return v + + +@router.put("/varieties/{id}", response_model=PlantVariety) +def update_variety(id: int, data: PlantVariety, session: Session = Depends(get_session)): + v = session.get(PlantVariety, id) + if not v: + raise HTTPException(status_code=404, detail="Variété introuvable") + for k, val in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items(): + setattr(v, k, val) + session.add(v) + session.commit() + session.refresh(v) + return v + + +@router.delete("/varieties/{id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_variety(id: int, session: Session = Depends(get_session)): + v = session.get(PlantVariety, id) + if not v: + raise HTTPException(status_code=404, detail="Variété introuvable") + session.delete(v) + session.commit() diff --git a/backend/tests/test_plantings.py b/backend/tests/test_plantings.py new file mode 100644 index 0000000..304be2e --- /dev/null +++ b/backend/tests/test_plantings.py @@ -0,0 +1,25 @@ +def test_create_planting(client): + g = client.post("/api/gardens", json={"nom": "Potager", "type": "plein_air"}).json() + v = client.post("/api/varieties", json={"nom_commun": "Tomate"}).json() + r = client.post("/api/plantings", json={ + "garden_id": g["id"], "variety_id": v["id"], "quantite": 3 + }) + assert r.status_code == 201 + assert r.json()["statut"] == "prevu" + + +def test_list_plantings(client): + g = client.post("/api/gardens", json={"nom": "Potager", "type": "plein_air"}).json() + v = client.post("/api/varieties", json={"nom_commun": "Tomate"}).json() + client.post("/api/plantings", json={"garden_id": g["id"], "variety_id": v["id"]}) + r = client.get("/api/plantings") + assert r.status_code == 200 + assert len(r.json()) >= 1 + + +def test_add_planting_event(client): + g = client.post("/api/gardens", json={"nom": "Potager", "type": "plein_air"}).json() + v = client.post("/api/varieties", json={"nom_commun": "Tomate"}).json() + p = client.post("/api/plantings", json={"garden_id": g["id"], "variety_id": v["id"]}).json() + r = client.post(f"/api/plantings/{p['id']}/events", json={"type": "arrosage", "note": "Bien arrosé"}) + assert r.status_code == 201 diff --git a/backend/tests/test_tasks.py b/backend/tests/test_tasks.py new file mode 100644 index 0000000..8eb7c7d --- /dev/null +++ b/backend/tests/test_tasks.py @@ -0,0 +1,28 @@ +def test_create_task(client): + r = client.post("/api/tasks", json={"titre": "Arroser les tomates", "priorite": "haute"}) + assert r.status_code == 201 + assert r.json()["statut"] == "a_faire" + + +def test_list_tasks(client): + client.post("/api/tasks", json={"titre": "Tâche 1"}) + client.post("/api/tasks", json={"titre": "Tâche 2"}) + r = client.get("/api/tasks") + assert r.status_code == 200 + assert len(r.json()) == 2 + + +def test_filter_tasks_by_statut(client): + client.post("/api/tasks", json={"titre": "À faire", "statut": "a_faire"}) + client.post("/api/tasks", json={"titre": "Fait", "statut": "fait"}) + r = client.get("/api/tasks?statut=a_faire") + assert r.status_code == 200 + assert all(t["statut"] == "a_faire" for t in r.json()) + + +def test_update_task_statut(client): + r = client.post("/api/tasks", json={"titre": "À faire"}) + id = r.json()["id"] + r2 = client.put(f"/api/tasks/{id}", json={"titre": "À faire", "statut": "fait"}) + assert r2.status_code == 200 + assert r2.json()["statut"] == "fait" diff --git a/backend/tests/test_varieties.py b/backend/tests/test_varieties.py new file mode 100644 index 0000000..8735910 --- /dev/null +++ b/backend/tests/test_varieties.py @@ -0,0 +1,26 @@ +def test_create_variety(client): + r = client.post("/api/varieties", json={"nom_commun": "Tomate", "famille": "Solanacées"}) + assert r.status_code == 201 + assert r.json()["nom_commun"] == "Tomate" + + +def test_list_varieties(client): + client.post("/api/varieties", json={"nom_commun": "Tomate"}) + client.post("/api/varieties", json={"nom_commun": "Courgette"}) + r = client.get("/api/varieties") + assert r.status_code == 200 + assert len(r.json()) == 2 + + +def test_get_variety(client): + r = client.post("/api/varieties", json={"nom_commun": "Basilic"}) + id = r.json()["id"] + r2 = client.get(f"/api/varieties/{id}") + assert r2.status_code == 200 + + +def test_delete_variety(client): + r = client.post("/api/varieties", json={"nom_commun": "Test"}) + id = r.json()["id"] + r2 = client.delete(f"/api/varieties/{id}") + assert r2.status_code == 204