munch-ease-backend/meals/models.py

63 lines
2.2 KiB
Python

from __future__ import annotations
import datetime
from typing import ClassVar, List, Optional
from pydantic import Field, model_validator
from common import ApiModel
from ingredients import Ingredient
from api.dtos import MemberRef
from recipes import Recipe
class MealRecipe(ApiModel):
meal_id: int
recipe_id: int
servings: float
recipe: Optional[Recipe] = None
class Meal(ApiModel):
KEYS: ClassVar[List[str]] = ["id", "suggested_date", "consumed_date", "purchase_date"]
id: int = -1
suggested_date: datetime.datetime
consumed_date: Optional[datetime.datetime] = None
chefs: List[MemberRef] = Field(default_factory=list)
cleanup: List[MemberRef] = Field(default_factory=list)
consumers: List[MemberRef] = Field(default_factory=list)
recipes: List[MealRecipe] = Field(default_factory=list)
extra_ingredients: List[Ingredient] = Field(default_factory=list)
# Set from shopping list
purchase_date: Optional[datetime.datetime] = None
@model_validator(mode="before")
@classmethod
def _coerce_members(cls, data: dict) -> dict:
# Accept legacy Person objects in tests by coercing to MemberRef
def to_member_ref(x):
# Already a mapping suitable for MemberRef or an instance with id/display_name
if isinstance(x, dict):
if "id" in x and ("display_name" in x or "displayName" in x or "name" in x):
# normalize display_name key
if "display_name" not in x:
dn = x.get("displayName") or x.get("name")
x = {**x, "display_name": dn}
return x
return x
# Object with attributes
pid = getattr(x, "id", None)
dname = getattr(x, "display_name", None) or getattr(x, "name", None)
if isinstance(pid, int) and dname:
return {"id": pid, "display_name": dname}
return x
if isinstance(data, dict):
for key in ("chefs", "cleanup", "consumers"):
val = data.get(key)
if isinstance(val, list):
data[key] = [to_member_ref(v) for v in val]
return data