addon
This commit is contained in:
7
backend/app/api/endpoints/__init__.py
Executable file
7
backend/app/api/endpoints/__init__.py
Executable file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Linux BenchTools - API Endpoints
|
||||
"""
|
||||
|
||||
from . import peripherals, locations
|
||||
|
||||
__all__ = ["peripherals", "locations"]
|
||||
303
backend/app/api/endpoints/locations.py
Executable file
303
backend/app/api/endpoints/locations.py
Executable file
@@ -0,0 +1,303 @@
|
||||
"""
|
||||
Linux BenchTools - Locations API Endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from app.db.session import get_peripherals_db
|
||||
from app.services.peripheral_service import LocationService
|
||||
from app.schemas.peripheral import (
|
||||
LocationCreate, LocationUpdate, LocationSchema, LocationTreeNode
|
||||
)
|
||||
from app.models.location import Location
|
||||
from app.utils.image_processor import ImageProcessor
|
||||
from app.utils.qr_generator import QRCodeGenerator
|
||||
from app.core.config import settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ========================================
|
||||
# LOCATION CRUD
|
||||
# ========================================
|
||||
|
||||
@router.post("/", response_model=LocationSchema, status_code=201)
|
||||
def create_location(
|
||||
location: LocationCreate,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Create a new location"""
|
||||
# Check parent exists if specified
|
||||
if location.parent_id:
|
||||
parent = db.query(Location).filter(Location.id == location.parent_id).first()
|
||||
if not parent:
|
||||
raise HTTPException(status_code=404, detail="Parent location not found")
|
||||
|
||||
# Check for duplicate name
|
||||
existing = db.query(Location).filter(Location.nom == location.nom).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="Location with this name already exists")
|
||||
|
||||
db_location = Location(**location.model_dump())
|
||||
db.add(db_location)
|
||||
db.commit()
|
||||
db.refresh(db_location)
|
||||
|
||||
return db_location
|
||||
|
||||
|
||||
@router.get("/", response_model=List[LocationSchema])
|
||||
def list_locations(
|
||||
parent_id: Optional[int] = None,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""List all locations (optionally filtered by parent)"""
|
||||
query = db.query(Location)
|
||||
|
||||
if parent_id is not None:
|
||||
query = query.filter(Location.parent_id == parent_id)
|
||||
|
||||
return query.order_by(Location.ordre_affichage, Location.nom).all()
|
||||
|
||||
|
||||
@router.get("/tree", response_model=List[dict])
|
||||
def get_location_tree(db: Session = Depends(get_peripherals_db)):
|
||||
"""Get hierarchical location tree"""
|
||||
return LocationService.get_location_tree(db)
|
||||
|
||||
|
||||
@router.get("/{location_id}", response_model=LocationSchema)
|
||||
def get_location(
|
||||
location_id: int,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Get a location by ID"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
return location
|
||||
|
||||
|
||||
@router.get("/{location_id}/path", response_model=List[LocationSchema])
|
||||
def get_location_path(
|
||||
location_id: int,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Get full path from root to location"""
|
||||
path = LocationService.get_location_path(db, location_id)
|
||||
if not path:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
return path
|
||||
|
||||
|
||||
@router.put("/{location_id}", response_model=LocationSchema)
|
||||
def update_location(
|
||||
location_id: int,
|
||||
location_data: LocationUpdate,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Update a location"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
# Check parent exists if being changed
|
||||
update_dict = location_data.model_dump(exclude_unset=True)
|
||||
if "parent_id" in update_dict and update_dict["parent_id"]:
|
||||
parent = db.query(Location).filter(Location.id == update_dict["parent_id"]).first()
|
||||
if not parent:
|
||||
raise HTTPException(status_code=404, detail="Parent location not found")
|
||||
|
||||
# Prevent circular reference
|
||||
if update_dict["parent_id"] == location_id:
|
||||
raise HTTPException(status_code=400, detail="Location cannot be its own parent")
|
||||
|
||||
# Check for duplicate name if name is being changed
|
||||
if "nom" in update_dict and update_dict["nom"] != location.nom:
|
||||
existing = db.query(Location).filter(Location.nom == update_dict["nom"]).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="Location with this name already exists")
|
||||
|
||||
# Update fields
|
||||
for key, value in update_dict.items():
|
||||
setattr(location, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(location)
|
||||
|
||||
return location
|
||||
|
||||
|
||||
@router.delete("/{location_id}", status_code=204)
|
||||
def delete_location(
|
||||
location_id: int,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Delete a location"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
# Check if location has children
|
||||
children = db.query(Location).filter(Location.parent_id == location_id).count()
|
||||
if children > 0:
|
||||
raise HTTPException(status_code=400, detail="Cannot delete location with children")
|
||||
|
||||
# Check if location has peripherals
|
||||
count = LocationService.count_peripherals_in_location(db, location_id)
|
||||
if count > 0:
|
||||
raise HTTPException(status_code=400, detail="Cannot delete location with peripherals")
|
||||
|
||||
# Delete image and QR code files if they exist
|
||||
if location.image_path and os.path.exists(location.image_path):
|
||||
os.remove(location.image_path)
|
||||
if location.qr_code_path and os.path.exists(location.qr_code_path):
|
||||
os.remove(location.qr_code_path)
|
||||
|
||||
db.delete(location)
|
||||
db.commit()
|
||||
|
||||
|
||||
@router.get("/{location_id}/count")
|
||||
def count_peripherals(
|
||||
location_id: int,
|
||||
recursive: bool = False,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Count peripherals in a location"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
count = LocationService.count_peripherals_in_location(db, location_id, recursive)
|
||||
return {"location_id": location_id, "count": count, "recursive": recursive}
|
||||
|
||||
|
||||
# ========================================
|
||||
# LOCATION IMAGES
|
||||
# ========================================
|
||||
|
||||
@router.post("/{location_id}/image", response_model=LocationSchema)
|
||||
async def upload_location_image(
|
||||
location_id: int,
|
||||
file: UploadFile = File(...),
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Upload an image for a location"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
# Validate image
|
||||
temp_path = f"/tmp/{file.filename}"
|
||||
with open(temp_path, "wb") as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
|
||||
if not ImageProcessor.is_valid_image(temp_path):
|
||||
os.remove(temp_path)
|
||||
raise HTTPException(status_code=400, detail="Invalid image file")
|
||||
|
||||
# Create upload directory
|
||||
upload_dir = os.path.join(settings.PERIPHERALS_UPLOAD_DIR, "locations", "images")
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
|
||||
try:
|
||||
# Process image
|
||||
processed_path, _ = ImageProcessor.process_image(
|
||||
temp_path,
|
||||
upload_dir,
|
||||
max_width=800,
|
||||
max_height=600
|
||||
)
|
||||
|
||||
# Delete old image if exists
|
||||
if location.image_path and os.path.exists(location.image_path):
|
||||
os.remove(location.image_path)
|
||||
|
||||
# Update location
|
||||
location.image_path = processed_path
|
||||
db.commit()
|
||||
db.refresh(location)
|
||||
|
||||
return location
|
||||
|
||||
finally:
|
||||
if os.path.exists(temp_path):
|
||||
os.remove(temp_path)
|
||||
|
||||
|
||||
@router.delete("/{location_id}/image", status_code=204)
|
||||
def delete_location_image(
|
||||
location_id: int,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Delete location image"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
if location.image_path and os.path.exists(location.image_path):
|
||||
os.remove(location.image_path)
|
||||
|
||||
location.image_path = None
|
||||
db.commit()
|
||||
|
||||
|
||||
# ========================================
|
||||
# LOCATION QR CODES
|
||||
# ========================================
|
||||
|
||||
@router.post("/{location_id}/qr-code", response_model=LocationSchema)
|
||||
def generate_qr_code(
|
||||
location_id: int,
|
||||
base_url: str,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Generate QR code for a location"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
# Create QR code directory
|
||||
qr_dir = os.path.join(settings.PERIPHERALS_UPLOAD_DIR, "locations", "qrcodes")
|
||||
os.makedirs(qr_dir, exist_ok=True)
|
||||
|
||||
# Generate QR code
|
||||
qr_path = QRCodeGenerator.generate_location_qr(
|
||||
location_id=location.id,
|
||||
location_name=location.nom,
|
||||
base_url=base_url,
|
||||
output_dir=qr_dir
|
||||
)
|
||||
|
||||
# Delete old QR code if exists
|
||||
if location.qr_code_path and os.path.exists(location.qr_code_path):
|
||||
os.remove(location.qr_code_path)
|
||||
|
||||
# Update location
|
||||
location.qr_code_path = qr_path
|
||||
db.commit()
|
||||
db.refresh(location)
|
||||
|
||||
return location
|
||||
|
||||
|
||||
@router.delete("/{location_id}/qr-code", status_code=204)
|
||||
def delete_qr_code(
|
||||
location_id: int,
|
||||
db: Session = Depends(get_peripherals_db)
|
||||
):
|
||||
"""Delete location QR code"""
|
||||
location = db.query(Location).filter(Location.id == location_id).first()
|
||||
if not location:
|
||||
raise HTTPException(status_code=404, detail="Location not found")
|
||||
|
||||
if location.qr_code_path and os.path.exists(location.qr_code_path):
|
||||
os.remove(location.qr_code_path)
|
||||
|
||||
location.qr_code_path = None
|
||||
db.commit()
|
||||
1336
backend/app/api/endpoints/peripherals.py
Executable file
1336
backend/app/api/endpoints/peripherals.py
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user