feat(backend): CRUD variétés, plantations, tâches + tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 21:22:58 +01:00
parent aa379aa1b4
commit 4787f044e5
6 changed files with 256 additions and 6 deletions

View File

@@ -1,2 +1,66 @@
from fastapi import APIRouter from datetime import datetime
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.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

View File

@@ -1,2 +1,61 @@
from fastapi import APIRouter from datetime import datetime
router = APIRouter() 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()

View File

@@ -1,2 +1,50 @@
from fastapi import APIRouter from typing import List
router = APIRouter() 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()

View File

@@ -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

View File

@@ -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"

View File

@@ -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