- 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.
102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
from pydantic import BaseModel, Field, validator
|
|
from typing import List, Optional, Dict, Any
|
|
from datetime import datetime, date
|
|
from enum import Enum
|
|
|
|
class TipoPasto(str, Enum):
|
|
pranzo = "pranzo"
|
|
cena = "cena"
|
|
|
|
class PastoBase(BaseModel):
|
|
data_pasto: date
|
|
tipo_pasto: TipoPasto = TipoPasto.pranzo
|
|
portate: Optional[Dict[str, Dict[int, int]]] = Field(
|
|
default_factory=dict,
|
|
description="Portate structure: {'primo': {pietanza_id: max_prenotazioni}}"
|
|
)
|
|
turni: Optional[Dict[str, int]] = Field(
|
|
default_factory=dict,
|
|
description="Turni structure: {'12:45': max_posti}"
|
|
)
|
|
disponibile: bool = True
|
|
|
|
@validator('portate')
|
|
def validate_portate(cls, v):
|
|
if v is None:
|
|
return {}
|
|
# Validate structure: each portata should have pietanza_id -> max_count mapping
|
|
valid_portate = ['primo', 'secondo', 'contorno', 'dolce', 'frutta']
|
|
for portata, pietanze in v.items():
|
|
if portata not in valid_portate:
|
|
raise ValueError(f"Portata non valida: {portata}")
|
|
if not isinstance(pietanze, dict):
|
|
raise ValueError(f"Pietanze per {portata} deve essere un dizionario")
|
|
for pietanza_id, max_count in pietanze.items():
|
|
if not isinstance(int(pietanza_id), int) or int(pietanza_id) <= 0:
|
|
raise ValueError(f"ID pietanza non valido: {pietanza_id}")
|
|
if not isinstance(max_count, int) or max_count < 0:
|
|
raise ValueError(f"Numero massimo prenotazioni non valido per pietanza {pietanza_id}")
|
|
return v
|
|
|
|
@validator('turni')
|
|
def validate_turni(cls, v):
|
|
if v is None:
|
|
return {}
|
|
# Validate time format and capacity
|
|
import re
|
|
time_pattern = re.compile(r'^([01]\d|2[0-3]):([0-5]\d)$')
|
|
for turno, capacity in v.items():
|
|
if not time_pattern.match(turno):
|
|
raise ValueError(f"Formato orario non valido: {turno}. Usare HH:MM")
|
|
if not isinstance(capacity, int) or capacity < 0:
|
|
raise ValueError(f"Capacità non valida per turno {turno}: {capacity}")
|
|
return v
|
|
|
|
class PastoCreate(PastoBase):
|
|
pass
|
|
|
|
class PastoUpdate(BaseModel):
|
|
tipo_pasto: Optional[TipoPasto] = None
|
|
portate: Optional[Dict[str, Dict[int, int]]] = None
|
|
turni: Optional[Dict[str, int]] = None
|
|
disponibile: Optional[bool] = None
|
|
|
|
# Same validators as PastoBase for optional fields
|
|
@validator('portate')
|
|
def validate_portate(cls, v):
|
|
if v is None:
|
|
return v
|
|
valid_portate = ['primo', 'secondo', 'contorno', 'dolce', 'frutta']
|
|
for portata, pietanze in v.items():
|
|
if portata not in valid_portate:
|
|
raise ValueError(f"Portata non valida: {portata}")
|
|
if not isinstance(pietanze, dict):
|
|
raise ValueError(f"Pietanze per {portata} deve essere un dizionario")
|
|
for pietanza_id, max_count in pietanze.items():
|
|
if not isinstance(int(pietanza_id), int) or int(pietanza_id) <= 0:
|
|
raise ValueError(f"ID pietanza non valido: {pietanza_id}")
|
|
if not isinstance(max_count, int) or max_count < 0:
|
|
raise ValueError(f"Numero massimo prenotazioni non valido per pietanza {pietanza_id}")
|
|
return v
|
|
|
|
@validator('turni')
|
|
def validate_turni(cls, v):
|
|
if v is None:
|
|
return v
|
|
import re
|
|
time_pattern = re.compile(r'^([01]\d|2[0-3]):([0-5]\d)$')
|
|
for turno, capacity in v.items():
|
|
if not time_pattern.match(turno):
|
|
raise ValueError(f"Formato orario non valido: {turno}. Usare HH:MM")
|
|
if not isinstance(capacity, int) or capacity < 0:
|
|
raise ValueError(f"Capacità non valida per turno {turno}: {capacity}")
|
|
return v
|
|
|
|
class PastoResponse(PastoBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|