Initial commit: locoutil — gestion de location d'outils
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user