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:
@@ -11,7 +11,13 @@ from app.database import create_db_and_tables
|
|||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
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
|
import app.models # noqa — enregistre tous les modèles avant create_all
|
||||||
|
from app.migrate import run_migrations
|
||||||
|
run_migrations()
|
||||||
create_db_and_tables()
|
create_db_and_tables()
|
||||||
from app.seed import run_seed
|
from app.seed import run_seed
|
||||||
run_seed()
|
run_seed()
|
||||||
@@ -27,14 +33,35 @@ app.add_middleware(
|
|||||||
allow_headers=["*"],
|
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(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(plantings.router, prefix="/api")
|
||||||
app.include_router(tasks.router, prefix="/api")
|
app.include_router(tasks.router, prefix="/api")
|
||||||
app.include_router(settings.router, prefix="/api")
|
app.include_router(settings.router, prefix="/api")
|
||||||
app.include_router(media.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")
|
@app.get("/api/health")
|
||||||
@@ -43,6 +70,7 @@ def health():
|
|||||||
|
|
||||||
|
|
||||||
# Monter uploads seulement si le dossier existe
|
# Monter uploads seulement si le dossier existe
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles # noqa
|
||||||
|
|
||||||
if os.path.isdir(UPLOAD_DIR):
|
if os.path.isdir(UPLOAD_DIR):
|
||||||
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR, html=False), name="uploads")
|
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR, html=False), name="uploads")
|
||||||
|
|||||||
29
backend/app/routers/identify.py
Normal file
29
backend/app/routers/identify.py
Normal 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}
|
||||||
Reference in New Issue
Block a user