from __future__ import annotations from urllib.parse import urljoin from quart import ( g, request, ) from config import config from .products import products, products_nocounts from .blacklist.product_details import is_blacklisted_heading from utils import host_url from sqlalchemy import select from models import ProductLike from ...market.filters.qs import decode def _hx_fragment_request() -> bool: return request.headers.get("HX-Request", "").lower() == "true" async def _productInfo(top_slug=None, sub_slug=None): """ Shared query logic for home / category / subcategory pages. Pulls filters from qs.decode(), queries products(), and orders brands/stickers/etc. """ q = decode() page, search, sort = q.page, q.search, q.sort selected_brands, selected_stickers, selected_labels = q.selected_brands, q.selected_stickers, q.selected_labels liked = q.liked if top_slug is not None and sub_slug is not None: list_url = urljoin(config()["base_url"], f"/{top_slug}/{sub_slug}") else: if top_slug is not None: list_url = top_slug else: list_url = "" if not _hx_fragment_request() or page==1: items, brands, stickers, labels, total_pages, liked_count, search_count = await products( list_url, selected_brands=selected_brands, selected_stickers=selected_stickers, selected_labels=selected_labels, page=page, search=search, sort=sort, user_id=g.user.id if g.user else None, liked = liked, ) brands_ordered = _order_brands_selected_first(brands, selected_brands) return { "products": items, "page": page, "search": search, "sort": sort, "total_pages": int(total_pages or 1), "brands": brands_ordered, "selected_brands": selected_brands, "stickers": stickers, "selected_stickers": selected_stickers, "labels": labels, "selected_labels": selected_labels, "liked": liked, "liked_count": liked_count, "search_count": search_count } else: items, total_pages = await products_nocounts( g.s, list_url, selected_brands=selected_brands, selected_stickers=selected_stickers, selected_labels=selected_labels, page=page, search=search, sort=sort, user_id=g.user.id if g.user else None, liked = liked, ) return { "products": items, "page": page, "search": search, "sort": sort, "total_pages": int(total_pages or 1), } def _order_brands_selected_first(brands, selected): """Return brands with the selected brand(s) first.""" if not brands or not selected: return brands sel = [(s or "").strip() for s in selected] head = [s for s in brands if (s.get("name") or "").strip() in sel] tail = [s for s in brands if (s.get("name") or "").strip() not in sel] return head + tail def _order_stickers_selected_first( stickers: list[dict], selected_stickers: list[str] | None ): if not stickers or not selected_stickers: return stickers sel = [(s or "").strip().lower() for s in selected_stickers] head = [s for s in stickers if (s.get("name") or "").strip().lower() in sel] tail = [ s for s in stickers if (s.get("name") or "").strip().lower() not in sel ] return head + tail def _order_labels_selected_first( labels: list[dict], selected_labels: list[str] | None ): if not labels or not selected_labels: return labels sel = [(s or "").strip().lower() for s in selected_labels] head = [s for s in labels if (s.get("name") or "").strip().lower() in sel] tail = [ s for s in labels if (s.get("name") or "").strip().lower() not in sel ] return head + tail def _massage_product(d): """ Normalise the product dict for templates: - inject APP_ROOT into HTML - drop blacklisted sections """ massaged = { **d, "description_html": d["description_html"].replace( "[**__APP_ROOT__**]", g.root ), "sections": [ { **section, "html": section["html"].replace( "[**__APP_ROOT__**]", g.root ), } for section in d["sections"] if not is_blacklisted_heading(section["title"]) ], } return massaged # Re-export from canonical shared location from shared.http_utils import vary as _vary, current_url_without_page as _current_url_without_page async def _is_liked(user_id: int | None, slug: str) -> bool: """ Check if this user has liked this product. """ if not user_id: return False # because ProductLike has composite PK (user_id, product_slug), # we can fetch it by primary key dict: row = await g.s.execute( select(ProductLike).where( ProductLike.user_id == user_id, ProductLike.product_slug == slug, ) ) row.scalar_one_or_none() return row is not None