from __future__ import annotations import time import re from typing import Dict, List, Tuple, Optional from urllib.parse import urlparse, urljoin from config import config from . import db_backend as cb from .blacklist.category import is_category_blocked # Reverse map: slug -> label # ------------------ Caches ------------------ _nav_cache: Dict = {} _nav_cache_ts: float = 0.0 _nav_ttl_seconds = 60 * 60 * 6 # 6 hours def _now() -> float: try: return now() # type: ignore[name-defined] except Exception: return time.time() def extract_sub_slug(href: str, top_slug: str) -> Optional[str]: p = urlparse(href) parts = [x for x in (p.path or "").split("/") if x] if len(parts) >= 2 and parts[0].lower() == top_slug.lower(): sub = parts[1] if sub.lower().endswith((".html", ".htm")): sub = re.sub(r"\.(html?|HTML?)$", "", sub) return sub return None def group_by_category(slug_to_links: Dict[str, List[Tuple[str, str]]]) -> Dict[str, Dict]: nav = {"cats": {}} for label, slug in config()["categories"]["allow"].items(): top_href = urljoin(config()["base_url"], f"/{slug}") subs = [] for text, href in slug_to_links.get(slug, []): sub_slug = extract_sub_slug(href, slug) if sub_slug: subs.append({ "name": text, "href": href, "slug": sub_slug, # no count here yet in this path }) subs.sort(key=lambda x: x["name"].lower()) nav["cats"][label] = {"href": top_href, "slug": slug, "subs": subs} nav = _apply_category_blacklist(nav) return nav async def get_nav(session) -> Dict[str, Dict]: """ Return navigation structure; annotate each sub with product counts. Uses snapshot for offline behaviour. """ global _nav_cache, _nav_cache_ts now_ts = _now() # load from snapshot nav = await cb.db_nav(session) # inject counts for each subcategory (and for top-level too if you like) for label, cat in (nav.get("cats") or {}).items(): top_slug = cat.get("slug") if not top_slug: continue # Counts for subs new_subs = [] for s in cat.get("subs", []): s.get("slug") #if not sub_slug: # s_count = 0 #else: # s_count = await cb.db_count_products_in_sub(session,top_slug, sub_slug) #print('sub', s_count) new_subs.append({ **s, #"count": s_count, }) cat["subs"] = new_subs _nav_cache = nav _nav_cache_ts = now_ts nav = _apply_category_blacklist(nav) return nav def category_context(top_slug: Optional[str], sub_slug: Optional[str], nav: Dict[str, Dict]): """Build template context for a category/subcategory page.""" def _order_subs_selected_first(subs, sub_slug: str | None): """Return subs with the selected subcategory (by slug) first.""" if not subs or not sub_slug: return subs head = [s for s in subs if sub_slug and sub_slug.lower() == s['slug']] tail = [s for s in subs if not (sub_slug and sub_slug.lower() == s['slug'])] return head + tail REVERSE_CATEGORY = {v: k for k, v in config()["categories"]["allow"].items()} label = REVERSE_CATEGORY.get(top_slug) cat = nav["cats"].get(label) or {} top_suma_href = cat.get("href") or urljoin(config()["base_url"], f"/{top_slug}") top_local_href = f"{top_slug}" # total products in this top-level category (all subs combined / top-level listing) top_count = cat.get("count", 0) subs = [] for s in cat.get("subs", []): subs.append({ "name": s["name"], "slug": s.get("slug"), "local_href": f"{top_slug}/{s.get('slug')}", "suma_href": s["href"], "count": s.get("count", 0), # per-subcategory product count }) current_local_href = ( f"{top_slug}/{sub_slug}" if sub_slug else f"{top_slug}" if top_slug else "" ) return { "category_label": label, "top_slug": top_slug, "sub_slug": sub_slug, "top_suma_href": top_suma_href, "top_local_href": top_local_href, # 👇 expose total count for the parent category "top_count": top_count, # list of subcategories, each with its own count "subs_local": _order_subs_selected_first(subs, sub_slug), #"current_local_href": current_local_href, } def _apply_category_blacklist(nav: Dict[str, Dict]) -> Dict[str, Dict]: cats = nav.get("cats", {}) out = {"cats": {}} for label, data in cats.items(): top = (data or {}).get("slug") if not top or is_category_blocked(top): continue # filter subs subs = [] for s in (data.get("subs") or []): sub_slug = s.get("slug") if sub_slug and not is_category_blocked(top, sub_slug): subs.append(s) # keep everything else (including counts) out["cats"][label] = {**data, "subs": subs} return out