Files
locoutil/app/routers/tools.py
T

129 lines
5.8 KiB
Python

import os, uuid, shutil
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from typing import Optional
from ..database import get_db
router = APIRouter(prefix="/api/tools", tags=["tools"])
UPLOAD_DIR = os.environ.get("UPLOAD_DIR", "/uploads")
class ToolUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
category: Optional[str] = None
daily_price: Optional[float] = None
weekend_price: Optional[float] = None
deposit: Optional[float] = None
notes: Optional[str] = None
def tool_with_images(conn, row):
t = dict(row)
imgs = conn.execute("SELECT * FROM tool_images WHERE tool_id = ? ORDER BY is_main DESC, id", (t["id"],)).fetchall()
t["images"] = [dict(i) for i in imgs]
t["main_image"] = next((i["filename"] for i in t["images"] if i["is_main"]), (t["images"][0]["filename"] if t["images"] else None))
return t
@router.get("")
def list_tools():
with get_db() as conn:
rows = conn.execute("SELECT * FROM tools ORDER BY name").fetchall()
return [tool_with_images(conn, r) for r in rows]
@router.get("/{tool_id}")
def get_tool(tool_id: int):
with get_db() as conn:
row = conn.execute("SELECT * FROM tools WHERE id = ?", (tool_id,)).fetchone()
if not row:
raise HTTPException(404, "Outil introuvable")
return tool_with_images(conn, row)
@router.post("", status_code=201)
async def create_tool(
name: str = Form(...),
description: str = Form(""),
category: str = Form(""),
daily_price: float = Form(0),
weekend_price: float = Form(0),
deposit: float = Form(0),
notes: str = Form(""),
images: list[UploadFile] = File(default=[])
):
with get_db() as conn:
cur = conn.execute(
"INSERT INTO tools (name, description, category, daily_price, weekend_price, deposit, notes) VALUES (?,?,?,?,?,?,?)",
(name.strip(), description.strip(), category.strip(), daily_price, weekend_price, deposit, notes.strip())
)
tool_id = cur.lastrowid
for i, img in enumerate(images):
if img.filename:
ext = os.path.splitext(img.filename)[1].lower()
fname = f"{uuid.uuid4().hex}{ext}"
path = os.path.join(UPLOAD_DIR, "tools", fname)
with open(path, "wb") as f:
shutil.copyfileobj(img.file, f)
conn.execute("INSERT INTO tool_images (tool_id, filename, is_main) VALUES (?,?,?)", (tool_id, fname, 1 if i == 0 else 0))
row = conn.execute("SELECT * FROM tools WHERE id = ?", (tool_id,)).fetchone()
return tool_with_images(conn, row)
@router.put("/{tool_id}")
def update_tool(tool_id: int, data: ToolUpdate):
with get_db() as conn:
row = conn.execute("SELECT * FROM tools WHERE id = ?", (tool_id,)).fetchone()
if not row:
raise HTTPException(404, "Outil introuvable")
fields, values = [], []
for field, val in data.model_dump(exclude_none=True).items():
fields.append(f"{field} = ?")
values.append(val.strip() if isinstance(val, str) else val)
if fields:
values.append(tool_id)
conn.execute(f"UPDATE tools SET {', '.join(fields)} WHERE id = ?", values)
return tool_with_images(conn, conn.execute("SELECT * FROM tools WHERE id = ?", (tool_id,)).fetchone())
@router.delete("/{tool_id}", status_code=204)
def delete_tool(tool_id: int):
with get_db() as conn:
imgs = conn.execute("SELECT filename FROM tool_images WHERE tool_id = ?", (tool_id,)).fetchall()
for img in imgs:
path = os.path.join(UPLOAD_DIR, "tools", img["filename"])
if os.path.exists(path):
os.remove(path)
conn.execute("DELETE FROM tools WHERE id = ?", (tool_id,))
@router.post("/{tool_id}/images", status_code=201)
async def add_image(tool_id: int, image: UploadFile = File(...), is_main: bool = Form(False)):
with get_db() as conn:
if not conn.execute("SELECT id FROM tools WHERE id = ?", (tool_id,)).fetchone():
raise HTTPException(404, "Outil introuvable")
ext = os.path.splitext(image.filename)[1].lower()
fname = f"{uuid.uuid4().hex}{ext}"
path = os.path.join(UPLOAD_DIR, "tools", fname)
with open(path, "wb") as f:
shutil.copyfileobj(image.file, f)
if is_main:
conn.execute("UPDATE tool_images SET is_main = 0 WHERE tool_id = ?", (tool_id,))
conn.execute("INSERT INTO tool_images (tool_id, filename, is_main) VALUES (?,?,?)", (tool_id, fname, 1 if is_main else 0))
return {"filename": fname}
@router.delete("/{tool_id}/images/{image_id}", status_code=204)
def delete_image(tool_id: int, image_id: int):
with get_db() as conn:
img = conn.execute("SELECT * FROM tool_images WHERE id = ? AND tool_id = ?", (image_id, tool_id)).fetchone()
if not img:
raise HTTPException(404, "Image introuvable")
path = os.path.join(UPLOAD_DIR, "tools", img["filename"])
if os.path.exists(path):
os.remove(path)
conn.execute("DELETE FROM tool_images WHERE id = ?", (image_id,))
if img["is_main"]:
first = conn.execute("SELECT id FROM tool_images WHERE tool_id = ? LIMIT 1", (tool_id,)).fetchone()
if first:
conn.execute("UPDATE tool_images SET is_main = 1 WHERE id = ?", (first["id"],))
@router.post("/{tool_id}/images/{image_id}/main", status_code=204)
def set_main_image(tool_id: int, image_id: int):
with get_db() as conn:
conn.execute("UPDATE tool_images SET is_main = 0 WHERE tool_id = ?", (tool_id,))
conn.execute("UPDATE tool_images SET is_main = 1 WHERE id = ? AND tool_id = ?", (image_id, tool_id))