84 lines
2.3 KiB
Python
84 lines
2.3 KiB
Python
from sqlalchemy.orm import relationship, Mapped, mapped_column, joinedload
|
|
from sqlalchemy import ForeignKey, Integer
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.exc import IntegrityError
|
|
from typing import Optional
|
|
|
|
from .base import Base, CommonMixin
|
|
from .entry import Entry
|
|
|
|
|
|
class Meal(Base, CommonMixin):
|
|
"""Meal."""
|
|
|
|
name: Mapped[str]
|
|
order: Mapped[int]
|
|
diary_id: Mapped[int] = mapped_column(Integer, ForeignKey("diary.id"))
|
|
entries: Mapped[list[Entry]] = relationship(lazy="selectin")
|
|
|
|
@property
|
|
def calories(self) -> float:
|
|
"""calories.
|
|
|
|
:rtype: float
|
|
"""
|
|
return sum(entry.calories for entry in self.entries)
|
|
|
|
@property
|
|
def protein(self) -> float:
|
|
"""protein.
|
|
|
|
:rtype: float
|
|
"""
|
|
return sum(entry.protein for entry in self.entries)
|
|
|
|
@property
|
|
def carb(self) -> float:
|
|
"""carb.
|
|
|
|
:rtype: float
|
|
"""
|
|
return sum(entry.carb for entry in self.entries)
|
|
|
|
@property
|
|
def fat(self) -> float:
|
|
"""fat.
|
|
|
|
:rtype: float
|
|
"""
|
|
return sum(entry.fat for entry in self.entries)
|
|
|
|
@classmethod
|
|
async def create(
|
|
cls,
|
|
session: AsyncSession,
|
|
diary_id: int,
|
|
order: int = 0,
|
|
name: Optional[str] = None,
|
|
) -> "Meal":
|
|
# check if order already exists in diary
|
|
query = select(cls).where(cls.diary_id == diary_id).where(cls.order == order)
|
|
existing_meal = await session.scalar(query)
|
|
assert existing_meal is None, "order already exists in diary"
|
|
|
|
if name is None:
|
|
name = f"Meal {order}"
|
|
meal = Meal(diary_id=diary_id, name=name, order=order)
|
|
session.add(meal)
|
|
|
|
try:
|
|
await session.flush()
|
|
except IntegrityError:
|
|
raise AssertionError("diary does not exist")
|
|
|
|
meal = await cls.get_by_id(session, meal.id)
|
|
if not meal:
|
|
raise RuntimeError()
|
|
return meal
|
|
|
|
@classmethod
|
|
async def get_by_id(cls, session: AsyncSession, id: int) -> "Optional[Meal]":
|
|
"""get_by_id."""
|
|
query = select(cls).where(cls.id == id).options(joinedload(cls.entries))
|
|
return await session.scalar(query.order_by(cls.id))
|