feat: Refactor API structure and implement core functionalities
- Updated requirements to include asyncpg, pyyaml, aiofiles, and httpx. - Modified schema.sql to add 'tipo_pasto' column in 'pasti' table. - Created .gitignore file to exclude unnecessary files and directories. - Added README.md for API implementation details and directory structure. - Introduced config.yaml for centralized configuration management. - Implemented core modules for database management, authentication, and exception handling. - Developed models for 'pietanze', 'pasti', and 'prenotazioni' with validation. - Created routes for CRUD operations on 'pietanze' with pagination and filtering. - Established logging and error handling mechanisms throughout the application. - Set up FastAPI application with CORS and health check endpoint.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Routes package initialization
|
||||
@@ -0,0 +1,265 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from typing import List, Optional, Dict, Any
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from ..core.database import get_database, DatabaseManager
|
||||
from ..models.pietanze import PietanzaCreate, PietanzaUpdate, PietanzaResponse
|
||||
from ..models.common import ErrorResponse
|
||||
from ..core.auth import get_current_user, get_current_admin_user
|
||||
from ..core.exceptions import PietanzaNotFoundError, DatabaseError
|
||||
|
||||
router = APIRouter(prefix="/pietanze", tags=["Pietanze"])
|
||||
|
||||
@router.get("/", response_model=List[PietanzaResponse])
|
||||
async def list_pietanze(
|
||||
skip: int = Query(0, ge=0, description="Number of items to skip"),
|
||||
limit: int = Query(20, ge=1, le=100, description="Number of items to return"),
|
||||
search: Optional[str] = Query(None, description="Search in nome and descrizione"),
|
||||
allergeni: Optional[str] = Query(None, description="Filter by allergens (comma-separated)"),
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Get list of available pietanze with optional filtering"""
|
||||
try:
|
||||
# Build query with filters
|
||||
where_conditions = []
|
||||
params = []
|
||||
param_count = 0
|
||||
|
||||
if search:
|
||||
param_count += 1
|
||||
where_conditions.append(f"(nome ILIKE ${param_count} OR descrizione ILIKE ${param_count})")
|
||||
params.append(f"%{search}%")
|
||||
|
||||
if allergeni:
|
||||
allergen_list = [a.strip() for a in allergeni.split(",")]
|
||||
param_count += 1
|
||||
where_conditions.append(f"allergeni ?| ${param_count}")
|
||||
params.append(allergen_list)
|
||||
|
||||
where_clause = ""
|
||||
if where_conditions:
|
||||
where_clause = "WHERE " + " AND ".join(where_conditions)
|
||||
|
||||
# Count total items
|
||||
count_query = f"SELECT COUNT(*) FROM pietanze {where_clause}"
|
||||
count_result = await db.execute_one(count_query, *params)
|
||||
total = count_result[0] if count_result else 0
|
||||
|
||||
# Get items with pagination
|
||||
param_count += 1
|
||||
limit_param = param_count
|
||||
param_count += 1
|
||||
offset_param = param_count
|
||||
|
||||
query = f"""
|
||||
SELECT id, nome, descrizione, allergeni, created_at, updated_at
|
||||
FROM pietanze
|
||||
{where_clause}
|
||||
ORDER BY nome
|
||||
LIMIT ${limit_param} OFFSET ${offset_param}
|
||||
"""
|
||||
params.extend([limit, skip])
|
||||
|
||||
rows = await db.execute_query(query, *params)
|
||||
|
||||
pietanze = []
|
||||
for row in rows:
|
||||
pietanze.append(PietanzaResponse(
|
||||
id=row['id'],
|
||||
nome=row['nome'],
|
||||
descrizione=row['descrizione'],
|
||||
allergeni=row['allergeni'] or [],
|
||||
created_at=row['created_at'],
|
||||
updated_at=row['updated_at']
|
||||
))
|
||||
|
||||
return pietanze
|
||||
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to retrieve pietanze: {str(e)}")
|
||||
|
||||
@router.get("/{pietanza_id}", response_model=PietanzaResponse)
|
||||
async def get_pietanza(
|
||||
pietanza_id: int,
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Get specific pietanza by ID"""
|
||||
try:
|
||||
query = """
|
||||
SELECT id, nome, descrizione, allergeni, created_at, updated_at
|
||||
FROM pietanze
|
||||
WHERE id = $1
|
||||
"""
|
||||
row = await db.execute_one(query, pietanza_id)
|
||||
|
||||
if not row:
|
||||
raise PietanzaNotFoundError(pietanza_id)
|
||||
|
||||
return PietanzaResponse(
|
||||
id=row['id'],
|
||||
nome=row['nome'],
|
||||
descrizione=row['descrizione'],
|
||||
allergeni=row['allergeni'] or [],
|
||||
created_at=row['created_at'],
|
||||
updated_at=row['updated_at']
|
||||
)
|
||||
|
||||
except PietanzaNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to retrieve pietanza: {str(e)}")
|
||||
|
||||
@router.post("/", response_model=PietanzaResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_pietanza(
|
||||
pietanza: PietanzaCreate,
|
||||
current_user: Dict[str, Any] = Depends(get_current_admin_user),
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Create new pietanza (admin only)"""
|
||||
try:
|
||||
query = """
|
||||
INSERT INTO pietanze (nome, descrizione, allergeni, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $4)
|
||||
RETURNING id, nome, descrizione, allergeni, created_at, updated_at
|
||||
"""
|
||||
|
||||
now = datetime.utcnow()
|
||||
allergeni_json = json.dumps(pietanza.allergeni) if pietanza.allergeni else None
|
||||
|
||||
row = await db.execute_one(
|
||||
query,
|
||||
pietanza.nome,
|
||||
pietanza.descrizione,
|
||||
allergeni_json,
|
||||
now
|
||||
)
|
||||
|
||||
if not row:
|
||||
raise DatabaseError("Failed to create pietanza")
|
||||
|
||||
return PietanzaResponse(
|
||||
id=row['id'],
|
||||
nome=row['nome'],
|
||||
descrizione=row['descrizione'],
|
||||
allergeni=row['allergeni'] or [],
|
||||
created_at=row['created_at'],
|
||||
updated_at=row['updated_at']
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to create pietanza: {str(e)}")
|
||||
|
||||
@router.put("/{pietanza_id}", response_model=PietanzaResponse)
|
||||
async def update_pietanza(
|
||||
pietanza_id: int,
|
||||
pietanza_update: PietanzaUpdate,
|
||||
current_user: Dict[str, Any] = Depends(get_current_admin_user),
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Update existing pietanza (admin only)"""
|
||||
try:
|
||||
# Check if pietanza exists
|
||||
existing = await db.execute_one("SELECT id FROM pietanze WHERE id = $1", pietanza_id)
|
||||
if not existing:
|
||||
raise PietanzaNotFoundError(pietanza_id)
|
||||
|
||||
# Build update query dynamically
|
||||
update_fields = []
|
||||
params = []
|
||||
param_count = 0
|
||||
|
||||
if pietanza_update.nome is not None:
|
||||
param_count += 1
|
||||
update_fields.append(f"nome = ${param_count}")
|
||||
params.append(pietanza_update.nome)
|
||||
|
||||
if pietanza_update.descrizione is not None:
|
||||
param_count += 1
|
||||
update_fields.append(f"descrizione = ${param_count}")
|
||||
params.append(pietanza_update.descrizione)
|
||||
|
||||
if pietanza_update.allergeni is not None:
|
||||
param_count += 1
|
||||
update_fields.append(f"allergeni = ${param_count}")
|
||||
params.append(json.dumps(pietanza_update.allergeni))
|
||||
|
||||
if not update_fields:
|
||||
# No fields to update, return current pietanza
|
||||
return await get_pietanza(pietanza_id, db)
|
||||
|
||||
# Add updated_at and pietanza_id
|
||||
param_count += 1
|
||||
update_fields.append(f"updated_at = ${param_count}")
|
||||
params.append(datetime.utcnow())
|
||||
|
||||
param_count += 1
|
||||
params.append(pietanza_id)
|
||||
|
||||
query = f"""
|
||||
UPDATE pietanze
|
||||
SET {', '.join(update_fields)}
|
||||
WHERE id = ${param_count}
|
||||
RETURNING id, nome, descrizione, allergeni, created_at, updated_at
|
||||
"""
|
||||
|
||||
row = await db.execute_one(query, *params)
|
||||
|
||||
return PietanzaResponse(
|
||||
id=row['id'],
|
||||
nome=row['nome'],
|
||||
descrizione=row['descrizione'],
|
||||
allergeni=row['allergeni'] or [],
|
||||
created_at=row['created_at'],
|
||||
updated_at=row['updated_at']
|
||||
)
|
||||
|
||||
except PietanzaNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to update pietanza: {str(e)}")
|
||||
|
||||
@router.delete("/{pietanza_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_pietanza(
|
||||
pietanza_id: int,
|
||||
current_user: Dict[str, Any] = Depends(get_current_admin_user),
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Delete pietanza (admin only)"""
|
||||
try:
|
||||
# Check if pietanza exists
|
||||
existing = await db.execute_one("SELECT id FROM pietanze WHERE id = $1", pietanza_id)
|
||||
if not existing:
|
||||
raise PietanzaNotFoundError(pietanza_id)
|
||||
|
||||
# Delete the pietanza
|
||||
result = await db.execute_command("DELETE FROM pietanze WHERE id = $1", pietanza_id)
|
||||
|
||||
# Check if deletion was successful
|
||||
if not result or not result.endswith("1"):
|
||||
raise DatabaseError("Failed to delete pietanza")
|
||||
|
||||
except PietanzaNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to delete pietanza: {str(e)}")
|
||||
|
||||
@router.get("/{pietanza_id}/allergeni", response_model=List[str])
|
||||
async def get_pietanza_allergeni(
|
||||
pietanza_id: int,
|
||||
db: DatabaseManager = Depends(get_database)
|
||||
):
|
||||
"""Get allergen information for specific pietanza"""
|
||||
try:
|
||||
query = "SELECT allergeni FROM pietanze WHERE id = $1"
|
||||
row = await db.execute_one(query, pietanza_id)
|
||||
|
||||
if not row:
|
||||
raise PietanzaNotFoundError(pietanza_id)
|
||||
|
||||
return row['allergeni'] or []
|
||||
|
||||
except PietanzaNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise DatabaseError(f"Failed to retrieve allergeni: {str(e)}")
|
||||
Reference in New Issue
Block a user