fix(recipes): two bugs in recipe categories (v0.8.1)

Bug 1: update_recipe(category_ids=[]) didn't remove categories
- Fixed: send empty string "" to API to remove all categories
- Non-empty lists continue to work as expected
- Verified via FW_DEBUG=1 testing

Bug 2: get_recipe_categories() only showed used categories
- Fixed: always return 5 free-tier standard categories
- Added _get_family_id() helper to extract family ID
- Now returns all available category IDs even on new accounts
- Standard categories: category/<familyId>_2 through _6

Version: 0.8.0 → 0.8.1
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 06:30:09 +02:00
parent 4c60b5b5fa
commit 74a8b83fde
4 changed files with 71 additions and 39 deletions
+59 -35
View File
@@ -1699,6 +1699,27 @@ def like_post(post_id: str, like: bool = True) -> str:
# ---------------------------------------------------------------------------
def _get_family_id() -> str:
"""Extract the primary family ID from famlistfamily response.
Returns:
The family ID as a string (e.g. "23431854").
Raises:
RuntimeError: On credential or API errors.
"""
try:
families = _famlistfamily()
if families and isinstance(families, list) and len(families) > 0:
primary = families[0]
metaid = primary.get("metaId")
if isinstance(metaid, str) and metaid.startswith("family/"):
return metaid.split("/", 1)[1]
except RuntimeError:
pass
raise RuntimeError("Could not determine family ID")
def _get_raw_recipes() -> list[dict[str, Any]]:
"""Login, call metasync with id='recipe', logout and return the raw recipe list.
@@ -1725,50 +1746,53 @@ def get_recipe_categories() -> str:
"""Return all available recipe categories for the family.
Returns:
JSON list of recipe category objects with keys: id, name, emoji.
JSON list of category IDs (e.g. ["category/23431854_2", ...]).
Always includes the 5 free-tier standard categories if family ID is available.
Returns an error message string on failure.
"""
# Attempt to get categories via metasync call (similar to recipes).
# If the endpoint supports it, use that. Otherwise fallback to extracting
# categories from existing recipes.
# Get the family ID to construct standard category IDs
family_id: str | None = None
try:
data = _authenticated_call("metasync", {"id": "recipeCategory"})
family_id = _get_family_id()
except RuntimeError:
# Endpoint doesn't exist or failed. Fall back to extracting from recipes.
try:
raw_recipes = _get_raw_recipes()
except RuntimeError as exc:
return f"Error: {exc}"
pass
# Collect unique category IDs from all recipes.
seen_ids: set[str] = set()
category_ids: list[str] = []
for recipe in raw_recipes:
if not isinstance(recipe, dict):
continue
recipe_cat_ids = recipe.get("recipeCategoryIdList")
if not isinstance(recipe_cat_ids, list):
continue
for cat_id in recipe_cat_ids:
if isinstance(cat_id, str) and cat_id and cat_id not in seen_ids:
seen_ids.add(cat_id)
category_ids.append(cat_id)
return json.dumps(category_ids, ensure_ascii=False, indent=2)
# Collect all category IDs: standard categories + categories found in recipes
seen_ids: set[str] = set()
category_ids: list[str] = []
# If metasync succeeded, extract categories from response.
# Add the 5 free-tier standard categories if we have the family ID
if family_id:
standard_cat_ids = [
f"category/{family_id}_2", # Bei Kindern beliebt (KIDS_LOVE)
f"category/{family_id}_3", # Wirklich einfach (EASY)
f"category/{family_id}_4", # Nachspeisen (DESSERT)
f"category/{family_id}_5", # Schmeckt toll (DELICIOUS)
f"category/{family_id}_6", # Gemüse (VEGETABLES)
]
for cat_id in standard_cat_ids:
if cat_id not in seen_ids:
seen_ids.add(cat_id)
category_ids.append(cat_id)
# Add any additional categories found in recipes (e.g., premium categories)
try:
items: list[dict[str, Any]] = data["a00"]["r"]["r"]["updatedCreated"]
except (KeyError, TypeError):
items = []
raw_recipes = _get_raw_recipes()
except RuntimeError:
raw_recipes = []
if not isinstance(items, list):
return json.dumps({"error": "No recipe categories available"}, ensure_ascii=False)
for recipe in raw_recipes:
if not isinstance(recipe, dict):
continue
recipe_cat_ids = recipe.get("recipeCategoryIdList")
if not isinstance(recipe_cat_ids, list):
continue
for cat_id in recipe_cat_ids:
if isinstance(cat_id, str) and cat_id and cat_id not in seen_ids:
seen_ids.add(cat_id)
category_ids.append(cat_id)
result: list[str] = []
for cat_id in items:
if isinstance(cat_id, str):
result.append(cat_id)
return json.dumps(result, ensure_ascii=False, indent=2)
return json.dumps(category_ids, ensure_ascii=False, indent=2)
# ---------------------------------------------------------------------------