fooder-api/fooder/domain/product.py

110 lines
3.1 KiB
Python
Raw Normal View History

2023-04-01 16:19:12 +02:00
from sqlalchemy.orm import Mapped
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
2023-04-01 20:13:11 +02:00
from typing import AsyncIterator, Optional
2023-04-01 16:19:12 +02:00
from .base import Base, CommonMixin
from .user import User
2023-04-01 16:19:12 +02:00
class Product(Base, CommonMixin):
"""Product."""
name: Mapped[str]
protein: Mapped[float]
carb: Mapped[float]
fat: Mapped[float]
2023-07-30 20:18:42 +02:00
fiber: Mapped[float]
2023-04-01 16:19:12 +02:00
@property
def calories(self) -> float:
"""calories.
:rtype: float
"""
2023-07-30 20:18:42 +02:00
return self.protein * 4 + self.carb * 4 + self.fat * 9 + self.fiber * 2
2023-04-01 16:19:12 +02:00
@classmethod
2023-04-01 20:13:11 +02:00
async def list_all(
cls, session: AsyncSession, offset: int, limit: int, q: Optional[str] = None
) -> AsyncIterator["Product"]:
query = select(cls)
if q:
query = query.filter(cls.name.ilike(f"%{q.lower()}%"))
query = query.offset(offset).limit(limit)
2023-04-01 16:19:12 +02:00
stream = await session.stream_scalars(query.order_by(cls.id))
async for row in stream:
yield row
@classmethod
async def list_all_for_user(
cls,
session: AsyncSession,
offset: int,
limit: int,
user: User,
q: Optional[str] = None,
) -> AsyncIterator["Product"]:
from .meal import Meal
from .diary import Diary
from .entry import Entry
query = (
select(cls)
.join(Entry, isouter=True)
.join(Meal, isouter=True)
.join(Diary, isouter=True)
.where(Diary.user_id == user.id)
)
if q:
query = query.filter(cls.name.ilike(f"%{q.lower()}%"))
query = query.order_by(Entry.last_changed.desc()).offset(offset).limit(limit)
stream = await session.stream_scalars(query.order_by(cls.id))
async for row in stream:
yield row
2023-04-01 16:19:12 +02:00
@classmethod
async def create(
2023-07-30 20:18:42 +02:00
cls,
session: AsyncSession,
name: str,
carb: float,
protein: float,
fat: float,
fiber: float,
2023-04-01 16:19:12 +02:00
) -> "Product":
# validation here
assert carb <= 100, "carb must be less than 100"
assert protein <= 100, "protein must be less than 100"
assert fat <= 100, "fat must be less than 100"
2023-07-30 20:18:42 +02:00
assert fiber <= 100, "fiber must be less than 100"
2023-04-01 16:19:12 +02:00
assert carb >= 0, "carb must be greater than 0"
assert protein >= 0, "protein must be greater than 0"
assert fat >= 0, "fat must be greater than 0"
2023-07-30 20:18:42 +02:00
assert fiber >= 0, "fiber must be greater than 0"
2023-04-01 16:19:12 +02:00
assert carb + protein + fat <= 100, "total must be less than 100"
# to avoid duplicates in the database keep name as lower
name = name.lower()
# check if product already exists
query = select(cls).where(cls.name == name)
existing_product = await session.scalar(query)
assert existing_product is None, "product already exists"
product = Product(
name=name,
protein=protein,
carb=carb,
fat=fat,
2023-07-30 20:44:06 +02:00
fiber=fiber,
2023-04-01 16:19:12 +02:00
)
session.add(product)
await session.flush()
return product