Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion references/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,4 @@ tmx login
```
OAuth login flow with Cookidoo account. Opens browser for authentication.

Credentials stored in `secrets/cookidoo.env` (COOKIDOO_EMAIL, COOKIDOO_PASSWORD).
Credentials can be provided interactively or via environment variables (`COOKIDOO_EMAIL`, `COOKIDOO_PASSWORD`).
80 changes: 40 additions & 40 deletions tmx_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,15 @@ def sync_categories(progress_callback=None) -> tuple[dict[str, str], list[str]]:
return categories, errors


# Alias for backward compatibility
CATEGORIES, _ = load_categories()
CATEGORY_NAMES = {v: k for k, v in CATEGORIES.items()} # Reverse lookup
_categories_cache = None


def get_categories() -> dict:
Comment on lines +232 to +235
"""Get categories, loading from cache/fallback on first access."""
global _categories_cache
if _categories_cache is None:
_categories_cache, _ = load_categories()
Comment on lines +235 to +239
return _categories_cache


# ─────────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -714,7 +720,7 @@ def search_recipes(
if tm_version:
filters.append(f"tmversion:{tm_version}")
if category:
cat_id = CATEGORIES.get(category.lower())
cat_id = get_categories().get(category.lower())
if cat_id:
Comment on lines 722 to 724
filters.append(f"categories.id:{cat_id}")

Expand Down Expand Up @@ -773,6 +779,17 @@ def format_time(seconds: Optional[int]) -> str:
return f"{hours}h"


def format_qty(qty) -> str:
"""Format a quantity value safely, handling None/string/float."""
if qty is None or qty == "":
return ""
try:
qty = float(qty)
except (TypeError, ValueError):
return str(qty)
return str(int(qty)) if qty == int(qty) else f"{qty:.1f}"


def seconds_to_minutes(seconds: Optional[int]) -> Optional[int]:
"""Convert seconds to minutes, return None if input is None/0."""
if not seconds:
Expand Down Expand Up @@ -1740,9 +1757,8 @@ def cmd_recipe_show(args):
# Servings
serving_size = data.get("servingSize", {})
servings = serving_size.get("quantity", {}).get("value", "")
if servings and servings == int(servings):
servings = int(servings)

servings_str = format_qty(servings) if servings else ""

# Nutrition data - parse the complex structure
nutrition = {}
for ng in data.get("nutritionGroups", []):
Expand Down Expand Up @@ -1783,8 +1799,8 @@ def cmd_recipe_show(args):
print(f"⏱ {' | '.join(time_parts)}")

# Servings
if servings:
print(f"👥 {servings} Portionen")
if servings_str:
print(f"👥 {servings_str} Portionen")

# Difficulty and TM version
info_parts = []
Expand Down Expand Up @@ -1844,13 +1860,7 @@ def cmd_recipe_show(args):
optional = ing.get("optional", False)

# Format quantity
if qty:
if qty == int(qty):
qty_str = str(int(qty))
else:
qty_str = f"{qty:.1f}"
else:
qty_str = ""
qty_str = format_qty(qty)

# Build ingredient line
parts = []
Expand Down Expand Up @@ -1920,10 +1930,9 @@ def progress(msg):
print(f"✅ {len(categories)} Kategorien synchronisiert!")
print(f" Gespeichert in: {CATEGORIES_CACHE_FILE}")

# Reload global CATEGORIES
global CATEGORIES, CATEGORY_NAMES
CATEGORIES = categories
CATEGORY_NAMES = {v: k for k, v in CATEGORIES.items()}
# Reload global categories cache
global _categories_cache
_categories_cache = categories
else:
print("❌ Keine Kategorien synchronisiert.")

Expand Down Expand Up @@ -2055,8 +2064,6 @@ def cmd_status(args):

def cmd_cache_clear(args):
"""Clear cached data files."""
import os

files = [
("Wochenplan", WEEKPLAN_JSON),
("Such-Token", SEARCH_TOKEN_FILE),
Expand All @@ -2073,7 +2080,7 @@ def cmd_cache_clear(args):
deleted = 0
for name, path in files:
if path.exists():
os.remove(path)
path.unlink()
print(f" ✅ {name} gelöscht")
deleted += 1
else:
Expand Down Expand Up @@ -2228,11 +2235,8 @@ def cmd_shopping_show(args):
prep_str = f" ({prep})" if prep else ""
opt_str = " (optional)" if optional else ""

if qty == int(qty):
qty_str = str(int(qty))
else:
qty_str = f"{qty:.1f}"

qty_str = format_qty(qty)

check = "✓" if is_owned else " "
print(f" [{check}] {qty_str} {unit} {name}{prep_str}{opt_str}")

Expand Down Expand Up @@ -2270,11 +2274,8 @@ def cmd_shopping_show(args):
opt = " (optional)" if ing["optional"] else ""

# Format quantity nicely
if qty == int(qty):
qty_str = str(int(qty))
else:
qty_str = f"{qty:.1f}"

qty_str = format_qty(qty)

print(f" [ ] {qty_str} {unit} {name}{prep}{opt}")

if owned:
Expand Down Expand Up @@ -2370,8 +2371,7 @@ def cmd_shopping_export(args):
lines = []

if fmt == "json":
import json as json_module
output = json_module.dumps(data, indent=2, ensure_ascii=False)
output = json.dumps(data, indent=2, ensure_ascii=False)
elif fmt == "markdown":
if by_recipe:
for recipe in recipes:
Expand All @@ -2383,7 +2383,7 @@ def cmd_shopping_export(args):
name = ing.get("ingredientNotation", "")
qty = ing.get("quantity", {}).get("value", 0)
unit = ing.get("unitNotation", "")
qty_str = str(int(qty)) if qty == int(qty) else f"{qty:.1f}"
qty_str = format_qty(qty)
is_owned = ing.get("isOwned", False)
check = "x" if is_owned else " "
lines.append(f"- [{check}] {qty_str} {unit} {name}")
Expand All @@ -2396,7 +2396,7 @@ def cmd_shopping_export(args):
if ing["is_owned"]:
continue
qty = ing["quantity"]
qty_str = str(int(qty)) if qty == int(qty) else f"{qty:.1f}"
qty_str = format_qty(qty)
lines.append(f"- [ ] {qty_str} {ing['unit']} {ing['name']}")

# Additional items
Expand All @@ -2419,7 +2419,7 @@ def cmd_shopping_export(args):
name = ing.get("ingredientNotation", "")
qty = ing.get("quantity", {}).get("value", 0)
unit = ing.get("unitNotation", "")
qty_str = str(int(qty)) if qty == int(qty) else f"{qty:.1f}"
qty_str = format_qty(qty)
lines.append(f" {qty_str} {unit} {name}")
lines.append("")
else:
Expand All @@ -2428,7 +2428,7 @@ def cmd_shopping_export(args):
if ing["is_owned"]:
continue
qty = ing["quantity"]
qty_str = str(int(qty)) if qty == int(qty) else f"{qty:.1f}"
qty_str = format_qty(qty)
lines.append(f"{qty_str} {ing['unit']} {ing['name']}")

# Additional items
Expand Down Expand Up @@ -2885,7 +2885,7 @@ def build_parser():
search_parser.add_argument("-t", "--time", type=int, help="Max. Zubereitungszeit in Minuten")
search_parser.add_argument("-d", "--difficulty", choices=["easy", "medium", "advanced"], help="Schwierigkeitsgrad")
search_parser.add_argument("--tm", choices=["TM5", "TM6", "TM7"], help="Thermomix-Version")
search_parser.add_argument("-c", "--category", choices=list(CATEGORIES.keys()), help="Kategorie")
search_parser.add_argument("-c", "--category", choices=list(get_categories().keys()), help="Kategorie")
search_parser.set_defaults(func=cmd_search)

# recipe command with subcommands
Expand Down
Loading