feat(backend): endpoint POST /api/identify PlantNet + YOLO fallback + cache

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 12:18:56 +01:00
parent 2e67e9cb02
commit 560fa63a45
2 changed files with 60 additions and 3 deletions

View File

@@ -11,7 +11,13 @@ from app.database import create_db_and_tables
@asynccontextmanager
async def lifespan(app: FastAPI):
os.makedirs(UPLOAD_DIR, exist_ok=True)
try:
os.makedirs("/data/skyfield", exist_ok=True)
except OSError:
pass
import app.models # noqa — enregistre tous les modèles avant create_all
from app.migrate import run_migrations
run_migrations()
create_db_and_tables()
from app.seed import run_seed
run_seed()
@@ -27,14 +33,35 @@ app.add_middleware(
allow_headers=["*"],
)
from app.routers import gardens, varieties, plantings, tasks, settings, media
from app.routers import ( # noqa
gardens,
plants,
plantings,
tasks,
settings,
media,
tools,
dictons,
astuces,
recoltes,
lunar,
meteo,
identify,
)
app.include_router(gardens.router, prefix="/api")
app.include_router(varieties.router, prefix="/api")
app.include_router(plants.router, prefix="/api")
app.include_router(plantings.router, prefix="/api")
app.include_router(tasks.router, prefix="/api")
app.include_router(settings.router, prefix="/api")
app.include_router(media.router, prefix="/api")
app.include_router(tools.router, prefix="/api")
app.include_router(dictons.router, prefix="/api")
app.include_router(astuces.router, prefix="/api")
app.include_router(recoltes.router, prefix="/api")
app.include_router(lunar.router, prefix="/api")
app.include_router(meteo.router, prefix="/api")
app.include_router(identify.router, prefix="/api")
@app.get("/api/health")
@@ -43,6 +70,7 @@ def health():
# Monter uploads seulement si le dossier existe
from fastapi.staticfiles import StaticFiles
from fastapi.staticfiles import StaticFiles # noqa
if os.path.isdir(UPLOAD_DIR):
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR, html=False), name="uploads")

View File

@@ -0,0 +1,29 @@
from fastapi import APIRouter, File, UploadFile
from app.services import plantnet, yolo_service, redis_cache
router = APIRouter(tags=["identification"])
@router.post("/identify")
async def identify_plant(file: UploadFile = File(...)):
image_bytes = await file.read()
# 1. Cache Redis
cached = redis_cache.get(image_bytes)
if cached is not None:
return {"source": "cache", "results": cached}
# 2. PlantNet (cloud)
results = await plantnet.identify(image_bytes, file.filename or "photo.jpg")
# 3. Fallback YOLO si PlantNet indisponible
if not results:
results = await yolo_service.identify(image_bytes)
source = "yolo"
else:
source = "plantnet"
# 4. Mettre en cache
redis_cache.set(image_bytes, results)
return {"source": source, "results": results}