2025-10-19 02:51:27 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import List, Set
|
|
|
|
|
|
2025-10-19 09:24:23 +00:00
|
|
|
from meals.models import Meal
|
2025-11-01 08:58:33 +00:00
|
|
|
from api.dtos import MemberRef
|
2025-10-19 02:51:27 +00:00
|
|
|
|
|
|
|
|
|
2025-11-01 08:58:33 +00:00
|
|
|
def get_duplicates(items: List[MemberRef]) -> Set[str]:
|
2025-10-19 02:51:27 +00:00
|
|
|
"""Return the set of duplicate person names based on repeated ids."""
|
|
|
|
|
seen: set[int] = set()
|
|
|
|
|
duplicates: set[str] = set()
|
|
|
|
|
for item in items:
|
|
|
|
|
if item.id in seen:
|
2025-11-01 08:58:33 +00:00
|
|
|
# prefer display_name; fall back to best-effort repr
|
|
|
|
|
name = (
|
|
|
|
|
getattr(item, "display_name", None) or getattr(item, "name", None) or str(item.id)
|
|
|
|
|
)
|
|
|
|
|
duplicates.add(name)
|
2025-10-19 02:51:27 +00:00
|
|
|
seen.add(item.id)
|
|
|
|
|
return duplicates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_meal(meal: Meal) -> str | None:
|
|
|
|
|
"""Validate a Meal domain model.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
None if valid, otherwise a human-readable error message.
|
|
|
|
|
"""
|
|
|
|
|
if not meal.chefs:
|
|
|
|
|
return "Meal must have at least one chef"
|
|
|
|
|
|
|
|
|
|
if not meal.cleanup:
|
|
|
|
|
return "Meal must have at least one cleanup person"
|
|
|
|
|
|
|
|
|
|
if not meal.consumers:
|
|
|
|
|
return "Meal must have at least one consumer"
|
|
|
|
|
|
|
|
|
|
if len(meal.recipes) == 0 and len(meal.extra_ingredients) == 0:
|
|
|
|
|
return "Meal must have at least one recipe or ingredient"
|
|
|
|
|
|
|
|
|
|
duplicates = get_duplicates(meal.chefs)
|
|
|
|
|
if duplicates:
|
|
|
|
|
return f"Duplicate chef: {', '.join(duplicates)}"
|
|
|
|
|
|
|
|
|
|
duplicates = get_duplicates(meal.cleanup)
|
|
|
|
|
if duplicates:
|
|
|
|
|
return f"Duplicate cleanup person: {', '.join(duplicates)}"
|
|
|
|
|
|
|
|
|
|
duplicates = get_duplicates(meal.consumers)
|
|
|
|
|
if duplicates:
|
|
|
|
|
return f"Duplicate consumer: {', '.join(duplicates)}"
|
|
|
|
|
|
|
|
|
|
zero_servings = [r for r in meal.recipes if r.servings == 0]
|
|
|
|
|
if zero_servings:
|
|
|
|
|
return "Recipe servings must be greater than 0"
|
|
|
|
|
|
|
|
|
|
return None
|