Spaces:
Sleeping
Sleeping
| # app/catalog.py | |
| from __future__ import annotations | |
| import json, os | |
| from typing import Dict, Any, List, Optional | |
| _CATALOG: Dict[str, Any] | None = None | |
| def get_catalog_path() -> str: | |
| here = os.path.dirname(os.path.abspath(__file__)) | |
| root = os.path.dirname(here) | |
| return os.path.join(root, "data", "menu_catalog.json") | |
| def load_catalog() -> Dict[str, Any]: | |
| global _CATALOG | |
| if _CATALOG is not None: | |
| return _CATALOG | |
| path = get_catalog_path() | |
| with open(path, "r", encoding="utf-8") as f: | |
| _CATALOG = json.load(f) | |
| return _CATALOG | |
| def find_item_by_name(name: str) -> Optional[Dict[str, Any]]: | |
| c = load_catalog() | |
| name_l = (name or "").strip().lower() | |
| for it in c["items"]: | |
| if it["name"].lower() == name_l: | |
| return it | |
| # lightweight alias match | |
| if name_l in it["name"].lower(): | |
| return it | |
| return None | |
| def find_item_by_sku(sku: str) -> Optional[Dict[str, Any]]: | |
| c = load_catalog() | |
| for it in c["items"]: | |
| if it["sku"] == sku: | |
| return it | |
| return None | |
| def required_fields_for_category(category: str) -> List[str]: | |
| c = load_catalog() | |
| schema = c["schema"].get(category) or {} | |
| return list(schema.get("required_fields") or []) | |
| def optional_fields_for_category(category: str) -> List[str]: | |
| c = load_catalog() | |
| schema = c["schema"].get(category) or {} | |
| return list(schema.get("optional_fields") or []) | |
| def compute_missing_fields(order_item: Dict[str, Any]) -> List[str]: | |
| """ | |
| order_item: {"name": "...", "sku": optional, "qty": int, "<opts>": ...} | |
| Uses catalog schema to see which fields are missing. | |
| """ | |
| it = None | |
| if "sku" in order_item: | |
| it = find_item_by_sku(order_item["sku"]) | |
| if not it and "name" in order_item: | |
| it = find_item_by_name(order_item["name"]) | |
| if not it: | |
| return ["name"] # we don’t even know the item yet | |
| category = it["category"] | |
| req = set(required_fields_for_category(category)) | |
| present = set([k for k in order_item.keys() if k in req or k == "qty" or k == "name" or k == "sku"]) | |
| # qty normalization: consider qty present if >=1 | |
| if "qty" in req and (order_item.get("qty") is None or int(order_item.get("qty", 0)) < 1): | |
| # keep qty “missing” | |
| pass | |
| else: | |
| present.add("qty") | |
| missing = [f for f in req if f not in present] | |
| return missing | |
| def friendly_requirements_prompt(order_item: Dict[str, Any]) -> str: | |
| it = None | |
| if "sku" in order_item: | |
| it = find_item_by_sku(order_item["sku"]) | |
| if not it and "name" in order_item: | |
| it = find_item_by_name(order_item["name"]) | |
| if not it: | |
| return "Which item would you like to order?" | |
| category = it["category"] | |
| req = required_fields_for_category(category) | |
| opt = optional_fields_for_category(category) | |
| parts = [] | |
| opt_txt = "" | |
| if opt: | |
| opt_txt = f" Optional: {', '.join(opt)}." | |
| if req: | |
| parts.append(f"I need {', '.join(req)} for {it['name']}.{opt_txt}") | |
| else: | |
| parts.append(f"Please specify quantity for {it['name']}.{opt_txt}") | |
| # Also list choices for required options | |
| # e.g., size choices | |
| opts = it.get("options") or {} | |
| choice_bits = [] | |
| for k, spec in opts.items(): | |
| if spec.get("required"): | |
| choices = spec.get("choices") or [] | |
| if choices: | |
| choice_bits.append(f"{k}: {', '.join(choices)}") | |
| if choice_bits: | |
| parts.append("Choices → " + " | ".join(choice_bits)) | |
| return " ".join(parts) |