Fix some UI issues 2

This commit is contained in:
Urtzi Alfaro
2025-09-24 21:54:49 +02:00
parent d59b92a1b4
commit dc6c6f213f
16 changed files with 1036 additions and 141 deletions

View File

@@ -18,7 +18,9 @@ from ..schemas.recipes import (
RecipeSearchRequest,
RecipeDuplicateRequest,
RecipeFeasibilityResponse,
RecipeStatisticsResponse
RecipeStatisticsResponse,
RecipeQualityConfiguration,
RecipeQualityConfigurationUpdate
)
logger = logging.getLogger(__name__)
@@ -350,4 +352,136 @@ async def get_recipe_categories(
except Exception as e:
logger.error(f"Error getting recipe categories: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
# Quality Configuration Endpoints
@router.get("/{tenant_id}/recipes/{recipe_id}/quality-configuration", response_model=RecipeQualityConfiguration)
async def get_recipe_quality_configuration(
tenant_id: UUID,
recipe_id: UUID,
db: AsyncSession = Depends(get_db)
):
"""Get quality configuration for a specific recipe"""
try:
recipe_service = RecipeService(db)
# Get recipe with quality configuration
recipe = await recipe_service.get_recipe(tenant_id, recipe_id)
if not recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
# Return quality configuration or default structure
quality_config = recipe.get("quality_check_configuration")
if not quality_config:
quality_config = {
"stages": {},
"overall_quality_threshold": 7.0,
"critical_stage_blocking": True,
"auto_create_quality_checks": True,
"quality_manager_approval_required": False
}
return quality_config
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting recipe quality configuration: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.put("/{tenant_id}/recipes/{recipe_id}/quality-configuration", response_model=RecipeQualityConfiguration)
async def update_recipe_quality_configuration(
tenant_id: UUID,
recipe_id: UUID,
quality_config: RecipeQualityConfigurationUpdate,
user_id: UUID = Depends(get_user_id),
db: AsyncSession = Depends(get_db)
):
"""Update quality configuration for a specific recipe"""
try:
recipe_service = RecipeService(db)
# Verify recipe exists and belongs to tenant
recipe = await recipe_service.get_recipe(tenant_id, recipe_id)
if not recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
# Update recipe with quality configuration
updated_recipe = await recipe_service.update_recipe_quality_configuration(
tenant_id, recipe_id, quality_config.dict(exclude_unset=True), user_id
)
return updated_recipe["quality_check_configuration"]
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating recipe quality configuration: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/{tenant_id}/recipes/{recipe_id}/quality-configuration/stages/{stage}/templates")
async def add_quality_templates_to_stage(
tenant_id: UUID,
recipe_id: UUID,
stage: str,
template_ids: List[UUID],
user_id: UUID = Depends(get_user_id),
db: AsyncSession = Depends(get_db)
):
"""Add quality templates to a specific recipe stage"""
try:
recipe_service = RecipeService(db)
# Verify recipe exists
recipe = await recipe_service.get_recipe(tenant_id, recipe_id)
if not recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
# Add templates to stage
await recipe_service.add_quality_templates_to_stage(
tenant_id, recipe_id, stage, template_ids, user_id
)
return {"message": f"Added {len(template_ids)} templates to {stage} stage"}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error adding quality templates to recipe stage: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/{tenant_id}/recipes/{recipe_id}/quality-configuration/stages/{stage}/templates/{template_id}")
async def remove_quality_template_from_stage(
tenant_id: UUID,
recipe_id: UUID,
stage: str,
template_id: UUID,
user_id: UUID = Depends(get_user_id),
db: AsyncSession = Depends(get_db)
):
"""Remove a quality template from a specific recipe stage"""
try:
recipe_service = RecipeService(db)
# Verify recipe exists
recipe = await recipe_service.get_recipe(tenant_id, recipe_id)
if not recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
# Remove template from stage
await recipe_service.remove_quality_template_from_stage(
tenant_id, recipe_id, stage, template_id, user_id
)
return {"message": f"Removed template from {stage} stage"}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error removing quality template from recipe stage: {e}")
raise HTTPException(status_code=500, detail="Internal server error")

View File

@@ -12,6 +12,34 @@ from enum import Enum
from ..models.recipes import RecipeStatus, MeasurementUnit
# Quality Template Association Schemas
class QualityStageConfiguration(BaseModel):
"""Schema for quality checks configuration per production stage"""
template_ids: List[UUID] = Field(default_factory=list, description="Quality template IDs for this stage")
required_checks: List[str] = Field(default_factory=list, description="Required quality check types")
optional_checks: List[str] = Field(default_factory=list, description="Optional quality check types")
blocking_on_failure: bool = Field(default=True, description="Block stage progression on critical failures")
min_quality_score: Optional[float] = Field(None, ge=0, le=10, description="Minimum quality score to pass stage")
class RecipeQualityConfiguration(BaseModel):
"""Schema for recipe quality configuration across all stages"""
stages: Dict[str, QualityStageConfiguration] = Field(default_factory=dict, description="Quality configuration per stage")
overall_quality_threshold: float = Field(default=7.0, ge=0, le=10, description="Overall quality threshold for batch")
critical_stage_blocking: bool = Field(default=True, description="Block progression if critical checks fail")
auto_create_quality_checks: bool = Field(default=True, description="Automatically create quality checks for batches")
quality_manager_approval_required: bool = Field(default=False, description="Require quality manager approval")
class RecipeQualityConfigurationUpdate(BaseModel):
"""Schema for updating recipe quality configuration"""
stages: Optional[Dict[str, QualityStageConfiguration]] = None
overall_quality_threshold: Optional[float] = Field(None, ge=0, le=10)
critical_stage_blocking: Optional[bool] = None
auto_create_quality_checks: Optional[bool] = None
quality_manager_approval_required: Optional[bool] = None
class RecipeIngredientCreate(BaseModel):
"""Schema for creating recipe ingredients"""
ingredient_id: UUID
@@ -90,6 +118,7 @@ class RecipeCreate(BaseModel):
preparation_notes: Optional[str] = None
storage_instructions: Optional[str] = None
quality_standards: Optional[str] = None
quality_check_configuration: Optional[RecipeQualityConfiguration] = None
serves_count: Optional[int] = Field(None, ge=1)
nutritional_info: Optional[Dict[str, Any]] = None
allergen_info: Optional[Dict[str, Any]] = None
@@ -128,6 +157,7 @@ class RecipeUpdate(BaseModel):
preparation_notes: Optional[str] = None
storage_instructions: Optional[str] = None
quality_standards: Optional[str] = None
quality_check_configuration: Optional[RecipeQualityConfigurationUpdate] = None
serves_count: Optional[int] = Field(None, ge=1)
nutritional_info: Optional[Dict[str, Any]] = None
allergen_info: Optional[Dict[str, Any]] = None
@@ -175,6 +205,7 @@ class RecipeResponse(BaseModel):
preparation_notes: Optional[str] = None
storage_instructions: Optional[str] = None
quality_standards: Optional[str] = None
quality_check_configuration: Optional[RecipeQualityConfiguration] = None
serves_count: Optional[int] = None
nutritional_info: Optional[Dict[str, Any]] = None
allergen_info: Optional[Dict[str, Any]] = None

View File

@@ -261,4 +261,125 @@ class RecipeService:
return {
"success": False,
"error": str(e)
}
}
# Quality Configuration Methods
async def update_recipe_quality_configuration(
self,
tenant_id: UUID,
recipe_id: UUID,
quality_config_update: Dict[str, Any],
user_id: UUID
) -> Dict[str, Any]:
"""Update quality configuration for a recipe"""
try:
# Get current recipe
recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
if not recipe:
raise ValueError("Recipe not found")
# Get existing quality configuration or create default
current_config = recipe.get("quality_check_configuration", {
"stages": {},
"overall_quality_threshold": 7.0,
"critical_stage_blocking": True,
"auto_create_quality_checks": True,
"quality_manager_approval_required": False
})
# Merge with updates
if "stages" in quality_config_update:
current_config["stages"].update(quality_config_update["stages"])
for key in ["overall_quality_threshold", "critical_stage_blocking",
"auto_create_quality_checks", "quality_manager_approval_required"]:
if key in quality_config_update:
current_config[key] = quality_config_update[key]
# Update recipe with new configuration
recipe_update = RecipeUpdate(quality_check_configuration=current_config)
await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)
# Return updated recipe
updated_recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
return updated_recipe
except Exception as e:
logger.error(f"Error updating recipe quality configuration: {e}")
raise
async def add_quality_templates_to_stage(
self,
tenant_id: UUID,
recipe_id: UUID,
stage: str,
template_ids: List[UUID],
user_id: UUID
):
"""Add quality templates to a specific recipe stage"""
try:
# Get current recipe
recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
if not recipe:
raise ValueError("Recipe not found")
# Get existing quality configuration
quality_config = recipe.get("quality_check_configuration", {"stages": {}})
# Initialize stage if it doesn't exist
if stage not in quality_config["stages"]:
quality_config["stages"][stage] = {
"template_ids": [],
"required_checks": [],
"optional_checks": [],
"blocking_on_failure": True,
"min_quality_score": None
}
# Add template IDs (avoid duplicates)
stage_config = quality_config["stages"][stage]
existing_ids = set(stage_config.get("template_ids", []))
new_ids = [str(tid) for tid in template_ids if str(tid) not in existing_ids]
stage_config["template_ids"].extend(new_ids)
# Update recipe
recipe_update = RecipeUpdate(quality_check_configuration=quality_config)
await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)
except Exception as e:
logger.error(f"Error adding quality templates to stage: {e}")
raise
async def remove_quality_template_from_stage(
self,
tenant_id: UUID,
recipe_id: UUID,
stage: str,
template_id: UUID,
user_id: UUID
):
"""Remove a quality template from a specific recipe stage"""
try:
# Get current recipe
recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
if not recipe:
raise ValueError("Recipe not found")
# Get existing quality configuration
quality_config = recipe.get("quality_check_configuration", {"stages": {}})
# Remove template ID from stage
if stage in quality_config["stages"]:
stage_config = quality_config["stages"][stage]
template_ids = stage_config.get("template_ids", [])
template_ids = [tid for tid in template_ids if str(tid) != str(template_id)]
stage_config["template_ids"] = template_ids
# Update recipe
recipe_update = RecipeUpdate(quality_check_configuration=quality_config)
await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)
except Exception as e:
logger.error(f"Error removing quality template from stage: {e}")
raise