from __future__ import annotations from typing import Optional from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from models.market import Product, ProductLike def massage_full_product(product: Product) -> dict: """ Convert a Product ORM model to a dictionary with all fields. Used for rendering product detail pages. """ from bp.browse.services import _massage_product gallery = [] if product.image: gallery.append(product.image) d = { "id": product.id, "slug": product.slug, "title": product.title, "brand": product.brand, "image": product.image, "description_short": product.description_short, "description_html": product.description_html or "", "suma_href": product.suma_href, "rrp": float(product.rrp) if product.rrp else None, "special_price": float(product.special_price) if product.special_price else None, "regular_price": float(product.regular_price) if product.regular_price else None, "images": gallery or [img.url for img in product.images], "all_image_urls": gallery or [img.url for img in product.images], "sections": [{"title": s.title, "html": s.html} for s in product.sections], "stickers": [s.name.lower() for s in product.stickers], "labels": [l.name for l in product.labels], "nutrition": [{"key": n.key, "value": n.value, "unit": n.unit} for n in product.nutrition], "allergens": [{"name": a.name, "contains": a.contains} for a in product.allergens], "is_liked": False, } return _massage_product(d) async def toggle_product_like( session: AsyncSession, user_id: int, product_slug: str, ) -> tuple[bool, Optional[str]]: """ Toggle a product like for a given user using soft deletes. Returns (liked_state, error_message). - If error_message is not None, an error occurred. - liked_state indicates whether product is now liked (True) or unliked (False). """ from sqlalchemy import func, update # Get product_id from slug product_id = await session.scalar( select(Product.id).where(Product.slug == product_slug, Product.deleted_at.is_(None)) ) if not product_id: return False, "Product not found" # Check if like exists (not deleted) existing = await session.scalar( select(ProductLike).where( ProductLike.user_id == user_id, ProductLike.product_slug == product_slug, ProductLike.deleted_at.is_(None), ) ) if existing: # Unlike: soft delete the like await session.execute( update(ProductLike) .where( ProductLike.user_id == user_id, ProductLike.product_slug == product_slug, ProductLike.deleted_at.is_(None), ) .values(deleted_at=func.now()) ) return False, None else: # Like: add a new like new_like = ProductLike( user_id=user_id, product_slug=product_slug, ) session.add(new_like) return True, None