from fastapi import APIRouter, Depends, HTTPException, UploadFile, File from fastapi.responses import StreamingResponse from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func import io import numpy as np from app.database import get_db from app.models import Commune, Household, AdminUser from app.schemas import HouseholdOut, HouseholdStats, ImportPreview, ImportResult from app.services.auth_service import get_current_admin from app.services.import_service import parse_import_file, import_households, generate_template_csv router = APIRouter() @router.get("/communes/{slug}/households/template") async def download_template(): content = generate_template_csv() return StreamingResponse( io.BytesIO(content), media_type="text/csv", headers={"Content-Disposition": "attachment; filename=template_foyers.csv"}, ) @router.get("/communes/{slug}/households/stats", response_model=HouseholdStats) async def household_stats(slug: str, db: AsyncSession = Depends(get_db)): result = await db.execute(select(Commune).where(Commune.slug == slug)) commune = result.scalar_one_or_none() if not commune: raise HTTPException(status_code=404, detail="Commune introuvable") hh_result = await db.execute( select(Household).where(Household.commune_id == commune.id) ) households = hh_result.scalars().all() if not households: return HouseholdStats( total=0, rs_count=0, rp_count=0, pro_count=0, total_volume=0, avg_volume=0, median_volume=0, voted_count=0, ) volumes = [h.volume_m3 for h in households] return HouseholdStats( total=len(households), rs_count=sum(1 for h in households if h.status == "RS"), rp_count=sum(1 for h in households if h.status == "RP"), pro_count=sum(1 for h in households if h.status == "PRO"), total_volume=sum(volumes), avg_volume=float(np.mean(volumes)), median_volume=float(np.median(volumes)), voted_count=sum(1 for h in households if h.has_voted), ) @router.post("/communes/{slug}/households/import/preview", response_model=ImportPreview) async def preview_import( slug: str, file: UploadFile = File(...), db: AsyncSession = Depends(get_db), admin: AdminUser = Depends(get_current_admin), ): result = await db.execute(select(Commune).where(Commune.slug == slug)) commune = result.scalar_one_or_none() if not commune: raise HTTPException(status_code=404, detail="Commune introuvable") content = await file.read() df, errors = parse_import_file(content, file.filename) if df is None: return ImportPreview(valid_rows=0, errors=errors, sample=[]) valid_rows = len(df) - len(errors) sample = df.head(5).to_dict(orient="records") return ImportPreview(valid_rows=valid_rows, errors=errors, sample=sample) @router.post("/communes/{slug}/households/import", response_model=ImportResult) async def do_import( slug: str, file: UploadFile = File(...), db: AsyncSession = Depends(get_db), admin: AdminUser = Depends(get_current_admin), ): result = await db.execute(select(Commune).where(Commune.slug == slug)) commune = result.scalar_one_or_none() if not commune: raise HTTPException(status_code=404, detail="Commune introuvable") content = await file.read() df, parse_errors = parse_import_file(content, file.filename) if df is None or parse_errors: raise HTTPException(status_code=400, detail={"errors": parse_errors}) created, import_errors = await import_households(db, commune.id, df) return ImportResult(created=created, errors=import_errors) @router.get("/communes/{slug}/households", response_model=list[HouseholdOut]) async def list_households( slug: str, db: AsyncSession = Depends(get_db), admin: AdminUser = Depends(get_current_admin), ): result = await db.execute(select(Commune).where(Commune.slug == slug)) commune = result.scalar_one_or_none() if not commune: raise HTTPException(status_code=404, detail="Commune introuvable") hh_result = await db.execute( select(Household).where(Household.commune_id == commune.id) ) return hh_result.scalars().all()