diff --git a/backend/app/main.py b/backend/app/main.py index b694c1b..43d1bbf 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -36,9 +36,13 @@ app.include_router(tasks.router, prefix="/api") app.include_router(settings.router, prefix="/api") app.include_router(media.router, prefix="/api") -# Note: le mount StaticFiles sera ajouté ici dans Task 6 - @app.get("/api/health") def health(): return {"status": "ok"} + + +# Monter uploads seulement si le dossier existe +from fastapi.staticfiles import StaticFiles +if os.path.isdir(UPLOAD_DIR): + app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR, html=False), name="uploads") diff --git a/backend/app/routers/media.py b/backend/app/routers/media.py index da060ab..946dcef 100644 --- a/backend/app/routers/media.py +++ b/backend/app/routers/media.py @@ -1,2 +1,29 @@ -from fastapi import APIRouter -router = APIRouter() +import os +import uuid +from fastapi import APIRouter, File, HTTPException, UploadFile +from app.config import UPLOAD_DIR + +router = APIRouter(tags=["media"]) + +ALLOWED_EXT = {".jpg", ".jpeg", ".png", ".webp", ".gif"} + + +@router.post("/upload") +async def upload_file(file: UploadFile = File(...)): + ext = os.path.splitext(file.filename or "")[-1].lower() + if ext not in ALLOWED_EXT: + raise HTTPException(status_code=400, detail="Format non supporté") + filename = f"{uuid.uuid4().hex}{ext}" + dest = os.path.join(UPLOAD_DIR, filename) + os.makedirs(UPLOAD_DIR, exist_ok=True) + content = await file.read() + with open(dest, "wb") as f: + f.write(content) + return {"filename": filename, "url": f"/uploads/{filename}"} + + +@router.delete("/upload/{filename}", status_code=204) +def delete_file(filename: str): + path = os.path.join(UPLOAD_DIR, filename) + if os.path.exists(path): + os.remove(path) diff --git a/backend/app/routers/settings.py b/backend/app/routers/settings.py index da060ab..37c93a4 100644 --- a/backend/app/routers/settings.py +++ b/backend/app/routers/settings.py @@ -1,2 +1,39 @@ -from fastapi import APIRouter -router = APIRouter() +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 + +router = APIRouter(tags=["réglages"]) + + +@router.get("/settings") +def get_settings(session: Session = Depends(get_session)): + rows = session.exec(select(UserSettings)).all() + return {r.cle: r.valeur for r in rows} + + +@router.put("/settings") +def update_settings(data: dict, session: Session = Depends(get_session)): + for cle, valeur in data.items(): + row = session.exec(select(UserSettings).where(UserSettings.cle == cle)).first() + if row: + row.valeur = str(valeur) + else: + row = UserSettings(cle=cle, valeur=str(valeur)) + 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() diff --git a/backend/app/seed.py b/backend/app/seed.py index 1e0ab33..1c5be8d 100644 --- a/backend/app/seed.py +++ b/backend/app/seed.py @@ -1,2 +1,78 @@ +from datetime import date +from sqlmodel import Session, select +from app.database import engine +import app.models # noqa + + def run_seed(): - pass + from app.models.garden import Garden, GardenCell, Measurement + from app.models.plant import PlantVariety + from app.models.planting import Planting, PlantingEvent + from app.models.task import Task + + with Session(engine) as session: + if session.exec(select(Garden)).first(): + return # déjà seedé + + jardin = Garden( + nom="Mon potager", + description="Potager principal plein sud", + type="plein_air", + exposition="S", + ombre="plein_soleil", + sol_type="limoneux", + grille_largeur=6, + grille_hauteur=4, + ) + session.add(jardin) + session.flush() + + for row in range(4): + for col in range(6): + session.add(GardenCell( + garden_id=jardin.id, + col=col, row=row, + libelle=f"{chr(65 + row)}{col + 1}", + )) + + session.add(Measurement(garden_id=jardin.id, temp_air=18.0, humidite_air=65.0)) + + tomate = PlantVariety( + nom_commun="Tomate", variete="Andine Cornue", + famille="Solanacées", type_plante="legume", + besoin_eau="fort", espacement_cm=60, + plantation_mois="4,5", recolte_mois="7,8,9", + ) + courgette = PlantVariety( + nom_commun="Courgette", variete="Verte", + famille="Cucurbitacées", type_plante="legume", + besoin_eau="moyen", espacement_cm=80, + plantation_mois="5,6", recolte_mois="7,8", + ) + salade = PlantVariety( + nom_commun="Laitue", variete="Batavia", + famille="Astéracées", type_plante="legume", + besoin_eau="moyen", espacement_cm=25, + ) + session.add_all([tomate, courgette, salade]) + session.flush() + + p1 = Planting( + garden_id=jardin.id, variety_id=tomate.id, + date_plantation=date(2026, 5, 1), quantite=6, statut="en_cours", + ) + p2 = Planting( + garden_id=jardin.id, variety_id=courgette.id, + date_plantation=date(2026, 5, 15), quantite=3, statut="prevu", + ) + session.add_all([p1, p2]) + session.flush() + + session.add(PlantingEvent(planting_id=p1.id, type="arrosage", note="Arrosage du matin")) + + session.add(Task(titre="Arroser les tomates", priorite="haute", + statut="a_faire", garden_id=jardin.id)) + session.add(Task(titre="Traiter contre les pucerons", priorite="normale", statut="a_faire")) + session.add(Task(titre="Préparer le compost", priorite="basse", statut="en_cours")) + + session.commit()