avant codex

This commit is contained in:
2026-02-22 15:05:40 +01:00
parent fed449c784
commit 20af00d653
291 changed files with 51868 additions and 424 deletions

View File

@@ -0,0 +1,59 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlmodel import Session, select
from app.database import get_session
from app.models.astuce import Astuce
router = APIRouter(tags=["astuces"])
@router.get("/astuces", response_model=List[Astuce])
def list_astuces(
entity_type: Optional[str] = Query(None),
entity_id: Optional[int] = Query(None),
session: Session = Depends(get_session),
):
q = select(Astuce)
if entity_type:
q = q.where(Astuce.entity_type == entity_type)
if entity_id is not None:
q = q.where(Astuce.entity_id == entity_id)
return session.exec(q).all()
@router.post("/astuces", response_model=Astuce, status_code=status.HTTP_201_CREATED)
def create_astuce(a: Astuce, session: Session = Depends(get_session)):
session.add(a)
session.commit()
session.refresh(a)
return a
@router.get("/astuces/{id}", response_model=Astuce)
def get_astuce(id: int, session: Session = Depends(get_session)):
a = session.get(Astuce, id)
if not a:
raise HTTPException(404, "Astuce introuvable")
return a
@router.put("/astuces/{id}", response_model=Astuce)
def update_astuce(id: int, data: Astuce, session: Session = Depends(get_session)):
a = session.get(Astuce, id)
if not a:
raise HTTPException(404, "Astuce introuvable")
for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items():
setattr(a, k, v)
session.add(a)
session.commit()
session.refresh(a)
return a
@router.delete("/astuces/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_astuce(id: int, session: Session = Depends(get_session)):
a = session.get(Astuce, id)
if not a:
raise HTTPException(404, "Astuce introuvable")
session.delete(a)
session.commit()

View File

@@ -0,0 +1,18 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select
from app.database import get_session
from app.models.dicton import Dicton
router = APIRouter(tags=["dictons"])
@router.get("/dictons", response_model=List[Dicton])
def list_dictons(
mois: Optional[int] = Query(None),
session: Session = Depends(get_session),
):
q = select(Dicton)
if mois:
q = q.where(Dicton.mois == mois)
return session.exec(q).all()

View File

@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
@@ -38,7 +38,7 @@ def update_garden(id: int, data: Garden, session: Session = Depends(get_session)
raise HTTPException(status_code=404, detail="Jardin introuvable")
for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items():
setattr(g, k, v)
g.updated_at = datetime.utcnow()
g.updated_at = datetime.now(timezone.utc)
session.add(g)
session.commit()
session.refresh(g)

View File

@@ -0,0 +1,36 @@
import calendar
from datetime import date
from typing import Any
from fastapi import APIRouter, HTTPException, Query
router = APIRouter(tags=["lunaire"])
# Cache en mémoire : {mois_str: list[dict]}
_CACHE: dict[str, list[dict]] = {}
@router.get("/lunar")
def get_lunar(
month: str = Query(..., description="Format YYYY-MM"),
) -> list[dict[str, Any]]:
if month in _CACHE:
return _CACHE[month]
try:
year, mon = int(month[:4]), int(month[5:7])
except (ValueError, IndexError):
raise HTTPException(400, "Format attendu : YYYY-MM")
last_day = calendar.monthrange(year, mon)[1]
start = date(year, mon, 1)
end = date(year, mon, last_day)
try:
from app.services.lunar import build_calendar
from dataclasses import asdict
result = [asdict(d) for d in build_calendar(start, end)]
except ImportError:
raise HTTPException(503, "Service lunaire non disponible (skyfield non installé)")
except Exception as e:
raise HTTPException(500, f"Erreur calcul lunaire : {e}")
_CACHE[month] = result
return result

View File

@@ -2,13 +2,20 @@ import os
import uuid
from typing import List, Optional
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi import APIRouter, Body, Depends, File, HTTPException, Query, UploadFile, status
from pydantic import BaseModel
from sqlmodel import Session, select
from app.config import UPLOAD_DIR
from app.database import get_session
from app.models.media import Attachment, Media
class MediaPatch(BaseModel):
entity_type: Optional[str] = None
entity_id: Optional[int] = None
titre: Optional[str] = None
router = APIRouter(tags=["media"])
@@ -81,6 +88,19 @@ def create_media(m: Media, session: Session = Depends(get_session)):
return m
@router.patch("/media/{id}", response_model=Media)
def update_media(id: int, payload: MediaPatch, session: Session = Depends(get_session)):
m = session.get(Media, id)
if not m:
raise HTTPException(404, "Media introuvable")
for k, v in payload.model_dump(exclude_none=True).items():
setattr(m, k, v)
session.add(m)
session.commit()
session.refresh(m)
return m
@router.delete("/media/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_media(id: int, session: Session = Depends(get_session)):
m = session.get(Media, id)

View File

@@ -1,9 +1,9 @@
from datetime import datetime
from datetime import datetime, timezone
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
from app.models.planting import Planting, PlantingCreate, PlantingEvent
router = APIRouter(tags=["plantations"])
@@ -14,7 +14,8 @@ def list_plantings(session: Session = Depends(get_session)):
@router.post("/plantings", response_model=Planting, status_code=status.HTTP_201_CREATED)
def create_planting(p: Planting, session: Session = Depends(get_session)):
def create_planting(data: PlantingCreate, session: Session = Depends(get_session)):
p = Planting(**data.model_dump())
session.add(p)
session.commit()
session.refresh(p)
@@ -30,13 +31,13 @@ def get_planting(id: int, session: Session = Depends(get_session)):
@router.put("/plantings/{id}", response_model=Planting)
def update_planting(id: int, data: Planting, session: Session = Depends(get_session)):
def update_planting(id: int, data: PlantingCreate, 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():
for k, v in data.model_dump(exclude_unset=True).items():
setattr(p, k, v)
p.updated_at = datetime.utcnow()
p.updated_at = datetime.now(timezone.utc)
session.add(p)
session.commit()
session.refresh(p)

View File

@@ -0,0 +1,56 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlmodel import Session, select
from app.database import get_session
from app.models.plant import Plant
router = APIRouter(tags=["plantes"])
@router.get("/plants", response_model=List[Plant])
def list_plants(
categorie: Optional[str] = Query(None),
session: Session = Depends(get_session),
):
q = select(Plant)
if categorie:
q = q.where(Plant.categorie == categorie)
return session.exec(q).all()
@router.post("/plants", response_model=Plant, status_code=status.HTTP_201_CREATED)
def create_plant(p: Plant, session: Session = Depends(get_session)):
session.add(p)
session.commit()
session.refresh(p)
return p
@router.get("/plants/{id}", response_model=Plant)
def get_plant(id: int, session: Session = Depends(get_session)):
p = session.get(Plant, id)
if not p:
raise HTTPException(404, "Plante introuvable")
return p
@router.put("/plants/{id}", response_model=Plant)
def update_plant(id: int, data: Plant, session: Session = Depends(get_session)):
p = session.get(Plant, id)
if not p:
raise HTTPException(404, "Plante introuvable")
for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items():
setattr(p, k, v)
session.add(p)
session.commit()
session.refresh(p)
return p
@router.delete("/plants/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_plant(id: int, session: Session = Depends(get_session)):
p = session.get(Plant, id)
if not p:
raise HTTPException(404, "Plante introuvable")
session.delete(p)
session.commit()

View File

@@ -0,0 +1,84 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlmodel import Session, select
from app.database import get_session
from app.models.recolte import Observation, ObservationCreate, Recolte, RecolteCreate
router = APIRouter(tags=["récoltes"])
# ── Récoltes (nested sous plantings) ──────────────────────────────────────────
@router.get("/plantings/{planting_id}/recoltes", response_model=List[Recolte])
def list_recoltes(planting_id: int, session: Session = Depends(get_session)):
return session.exec(
select(Recolte).where(Recolte.plantation_id == planting_id)
).all()
@router.post(
"/plantings/{planting_id}/recoltes",
response_model=Recolte,
status_code=status.HTTP_201_CREATED,
)
def create_recolte(
planting_id: int,
data: RecolteCreate,
session: Session = Depends(get_session),
):
r = Recolte(plantation_id=planting_id, **data.model_dump())
session.add(r)
session.commit()
session.refresh(r)
return r
@router.delete("/recoltes/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_recolte(id: int, session: Session = Depends(get_session)):
r = session.get(Recolte, id)
if not r:
raise HTTPException(404, "Récolte introuvable")
session.delete(r)
session.commit()
# ── Observations ───────────────────────────────────────────────────────────────
@router.get("/observations", response_model=List[Observation])
def list_observations(
plantation_id: Optional[int] = Query(None),
garden_id: Optional[int] = Query(None),
session: Session = Depends(get_session),
):
q = select(Observation)
if plantation_id is not None:
q = q.where(Observation.plantation_id == plantation_id)
if garden_id is not None:
q = q.where(Observation.garden_id == garden_id)
return session.exec(q).all()
@router.post("/observations", response_model=Observation, status_code=status.HTTP_201_CREATED)
def create_observation(data: ObservationCreate, session: Session = Depends(get_session)):
o = Observation(**data.model_dump())
session.add(o)
session.commit()
session.refresh(o)
return o
@router.get("/observations/{id}", response_model=Observation)
def get_observation(id: int, session: Session = Depends(get_session)):
o = session.get(Observation, id)
if not o:
raise HTTPException(404, "Observation introuvable")
return o
@router.delete("/observations/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_observation(id: int, session: Session = Depends(get_session)):
o = session.get(Observation, id)
if not o:
raise HTTPException(404, "Observation introuvable")
session.delete(o)
session.commit()

View File

@@ -1,8 +1,7 @@
from datetime import date
from fastapi import APIRouter, Depends
from sqlmodel import Session, select
from app.database import get_session
from app.models.settings import UserSettings, LunarCalendarEntry
from app.models.settings import UserSettings
router = APIRouter(tags=["réglages"])
@@ -24,16 +23,3 @@ def update_settings(data: dict, session: Session = Depends(get_session)):
session.add(row)
session.commit()
return {"ok": True}
@router.get("/lunar")
def get_lunar(month: str, session: Session = Depends(get_session)):
year, m = map(int, month.split("-"))
first = date(year, m, 1)
last_m, last_y = (m + 1, year) if m < 12 else (1, year + 1)
last = date(last_y, last_m, 1)
return session.exec(
select(LunarCalendarEntry)
.where(LunarCalendarEntry.jour >= first)
.where(LunarCalendarEntry.jour < last)
).all()

View File

@@ -1,9 +1,9 @@
from datetime import datetime
from datetime import datetime, timezone
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
from app.models.task import Task, TaskCreate
router = APIRouter(tags=["tâches"])
@@ -23,7 +23,8 @@ def list_tasks(
@router.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
def create_task(t: Task, session: Session = Depends(get_session)):
def create_task(data: TaskCreate, session: Session = Depends(get_session)):
t = Task(**data.model_dump())
session.add(t)
session.commit()
session.refresh(t)
@@ -39,13 +40,26 @@ def get_task(id: int, session: Session = Depends(get_session)):
@router.put("/tasks/{id}", response_model=Task)
def update_task(id: int, data: Task, session: Session = Depends(get_session)):
def update_task(id: int, data: TaskCreate, 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():
for k, v in data.model_dump(exclude_unset=True).items():
setattr(t, k, v)
t.updated_at = datetime.utcnow()
t.updated_at = datetime.now(timezone.utc)
session.add(t)
session.commit()
session.refresh(t)
return t
@router.put("/tasks/{id}/statut", response_model=Task)
def update_statut(id: int, statut: str, session: Session = Depends(get_session)):
t = session.get(Task, id)
if not t:
raise HTTPException(status_code=404, detail="Tâche introuvable")
t.statut = statut
t.updated_at = datetime.now(timezone.utc)
session.add(t)
session.commit()
session.refresh(t)

View File

@@ -0,0 +1,56 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlmodel import Session, select
from app.database import get_session
from app.models.tool import Tool
router = APIRouter(tags=["outils"])
@router.get("/tools", response_model=List[Tool])
def list_tools(
categorie: Optional[str] = Query(None),
session: Session = Depends(get_session),
):
q = select(Tool)
if categorie:
q = q.where(Tool.categorie == categorie)
return session.exec(q).all()
@router.post("/tools", response_model=Tool, status_code=status.HTTP_201_CREATED)
def create_tool(t: Tool, session: Session = Depends(get_session)):
session.add(t)
session.commit()
session.refresh(t)
return t
@router.get("/tools/{id}", response_model=Tool)
def get_tool(id: int, session: Session = Depends(get_session)):
t = session.get(Tool, id)
if not t:
raise HTTPException(404, "Outil introuvable")
return t
@router.put("/tools/{id}", response_model=Tool)
def update_tool(id: int, data: Tool, session: Session = Depends(get_session)):
t = session.get(Tool, id)
if not t:
raise HTTPException(404, "Outil introuvable")
for k, v in data.model_dump(exclude_unset=True, exclude={"id", "created_at"}).items():
setattr(t, k, v)
session.add(t)
session.commit()
session.refresh(t)
return t
@router.delete("/tools/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_tool(id: int, session: Session = Depends(get_session)):
t = session.get(Tool, id)
if not t:
raise HTTPException(404, "Outil introuvable")
session.delete(t)
session.commit()