From 51101cbd579ceac7e8572f9def451c2beb6e7037 Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Thu, 21 May 2026 18:59:33 -0400 Subject: [PATCH] converge: auto-commit at 2026-05-21 22:59:33 from zendo.brechtel --- bin/categorize.py | 409 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 bin/categorize.py diff --git a/bin/categorize.py b/bin/categorize.py new file mode 100644 index 0000000..e42bf7f --- /dev/null +++ b/bin/categorize.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +"""Categorize recipes with category and tags based on content.""" + +import re +from pathlib import Path +import yaml + +WORKDIR = Path("/home/jbrechtel/recipes") + +# Recipe classifications: (filename_pattern, {category, tags}) +CLASSIFICATIONS = [ + ("Alain-Ducase-Bread.cook", { + "category": "appetizer", + "tags": ["vegetarian", "french", "cheese"] + }), + ("Apple Cider Vinegar Dressing.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "gluten-free"] + }), + ("Arugula Pesto Chicken with Pecans & Roasted Butternut.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "pasta"] + }), + ("Arugula Pesto Wheat Berries Recipe.cook", { + "category": "lunch", + "tags": ["vegetarian", "grain-bowl"] + }), + ("Asparagus Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "gluten-free", "spring"] + }), + ("Authentic Cacio e Pepe Recipe.cook", { + "category": "dinner", + "tags": ["vegetarian", "italian", "pasta"] + }), + ("Baked Chile Relleno with Black Beans.cook", { + "category": "dinner", + "tags": ["vegetarian", "mexican", "beans"] + }), + ("Bang Bang Tofu.cook", { + "category": "dinner", + "tags": ["vegetarian", "vegan-option", "asian", "tofu"] + }), + ("Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "southern"] + }), + ("Black Bean and Corn Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "gluten-free", "mexican"] + }), + ("Black Pepper Tofu (Ottolenghi).cook", { + "category": "dinner", + "tags": ["vegetarian", "asian", "tofu", "vegan-option"] + }), + ("Black Pepper Tofu with Bok Choy.cook", { + "category": "dinner", + "tags": ["vegetarian", "asian", "tofu", "vegan-option"] + }), + ("Blackened Shrimp and Gouda Grits.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "southern"] + }), + ("Butter Tofu.cook", { + "category": "dinner", + "tags": ["vegetarian", "indian", "tofu"] + }), + ("butternut-turkey-chili.cook", { + "category": "dinner", + "tags": ["meat", "turkey", "chili", "soup"] + }), + ("Butternut Turkey Chili.cook", { + "category": "dinner", + "tags": ["meat", "turkey", "chili", "soup"] + }), + ("BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook", { + "category": "dinner", + "tags": ["meat", "turkey", "chili", "soup"] + }), + ("Cabbage_Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "gluten-free"] + }), + ("Cajun Shrimp and Sausage Vegetable Foil Packets.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "cajun", "sausage"] + }), + ("Cajun Spice Mix (for pecans, oysters, etc).cook", { + "category": "appetizer", + "tags": ["vegetarian", "vegan", "spice-blend"] + }), + ("Caramel Cake.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert"] + }), + ("Caramel Icing.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert"] + }), + ("cauliflower-milanese.cook", { + "category": "dinner", + "tags": ["vegetarian", "italian", "vegetable"] + }), + ("Chana Chickpea Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "indian", "gluten-free"] + }), + ("Chana Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "indian", "gluten-free"] + }), + ("Chicken and Pumpkin Gnocchi with Ricotta.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "pasta", "fall"] + }), + ("Chickpea Butter Curry.cook", { + "category": "dinner", + "tags": ["vegetarian", "indian", "curry", "vegan-option"] + }), + ("Chickpea_Butter_Curry.cook", { + "category": "dinner", + "tags": ["vegetarian", "indian", "curry", "vegan-option"] + }), + ("Chinese Orange Shrimp.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "asian"] + }), + ("Chocolate_Mousse_Cake_with_Raspberries.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert", "chocolate"] + }), + ("Cilantro Pesto Shrimp Over Orzo.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "pasta"] + }), + ("Crispy Roasted Chickpeas (Garbanzo Beans).cook", { + "category": "appetizer", + "tags": ["vegetarian", "vegan", "gluten-free", "snack"] + }), + ("Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook", { + "category": "appetizer", + "tags": ["vegetarian", "vegan", "gluten-free", "snack"] + }), + ("Crispy Tofu With Cashews and Blistered Snap Peas.cook", { + "category": "dinner", + "tags": ["vegetarian", "asian", "tofu", "vegan-option"] + }), + ("Dried Fava Soup.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "mexican"] + }), + ("Easy Lemon Rosemary White Bean Soup.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "gluten-free"] + }), + ("Easy_Lemon_Rosemary_White_Bean_Soup.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "gluten-free"] + }), + ("Easy Vegetarian Chili.cook", { + "category": "dinner", + "tags": ["vegetarian", "vegan", "chili", "soup"] + }), + ("example-fried-rice.cook", { + "category": "dinner", + "tags": ["meat", "asian", "rice"] + }), + ("Fettucine with spiced cherry tomato sauce.cook", { + "category": "dinner", + "tags": ["vegetarian", "italian", "pasta"] + }), + ("Gaja's Bulgogi Lettuce Wraps.cook", { + "category": "dinner", + "tags": ["meat", "asian", "korean"] + }), + ("Gochugaru Honey Salmon.cook", { + "category": "dinner", + "tags": ["seafood", "salmon", "asian", "korean"] + }), + ("Green_Beans_with_Lemon_and_Garlic.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "gluten-free", "side"] + }), + ("indian-spiced-chicken.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "indian"] + }), + ("Indian Spiced Chicken.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "indian"] + }), + ("Italian Sausage and Delicata Rigatoni.cook", { + "category": "dinner", + "tags": ["meat", "sausage", "pasta", "italian"] + }), + ("Kale and Chestnut Stew.cook", { + "category": "dinner", + "tags": ["vegetarian", "vegan-option", "soup", "fall"] + }), + ("Kkanpoong Tofu.cook", { + "category": "dinner", + "tags": ["vegetarian", "asian", "korean", "tofu"] + }), + ("Korean Shrimp Tacos.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "asian", "korean", "mexican"] + }), + ("Kung Pao Shrimp with Sichuan Roasted Green Beans.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "asian", "chinese"] + }), + ("Lactation cookies.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert", "snack"] + }), + ("Lactation_cookies.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert", "snack"] + }), + ("Lentil Soup with Lemon and Turmeric.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "gluten-free", "indian"] + }), + ("Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "moroccan"] + }), + ("Moroccan Spiced Lentil & Chickpea Soup.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "moroccan"] + }), + ("Orange Rolls.cook", { + "category": "lunch", + "tags": ["vegetarian", "dessert", "bread"] + }), + ("Palak Paneer.cook", { + "category": "dinner", + "tags": ["vegetarian", "indian", "paneer"] + }), + ("Passion Fruit Curd Recipe.cook", { + "category": "lunch", + "tags": ["vegetarian", "gluten-free", "dessert"] + }), + ("Pecan-Brown Butter Trout.cook", { + "category": "dinner", + "tags": ["seafood", "fish"] + }), + ("Ribollita.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "italian"] + }), + ("Roasted Pumpkin Sage.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "soup", "fall"] + }), + ("Saag Paneer.cook", { + "category": "dinner", + "tags": ["vegetarian", "indian", "paneer"] + }), + ("Sauteed Asparagus with Lemon.cook", { + "category": "lunch", + "tags": ["vegetarian", "vegan", "gluten-free", "side"] + }), + ("sheet-pan-miso-chicken-with-radishes-and-lime.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "asian", "japanese"] + }), + ("sheet-pan-shrimp-oreganata.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "italian"] + }), + ("shrimp-feta.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "mediterranean", "pasta"] + }), + ("Slow Cooker Chicken Shawarma.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "mediterranean", "middle-eastern"] + }), + ("Southern_Macaroni_and_Cheese.cook", { + "category": "lunch", + "tags": ["vegetarian", "southern", "pasta"] + }), + ("Spaghetti Carbonara.cook", { + "category": "dinner", + "tags": ["meat", "italian", "pasta"] + }), + ("Spaghetti_Squash_w_Chickpeas_and_Kale.cook", { + "category": "dinner", + "tags": ["vegetarian", "vegan", "gluten-free", "vegetable"] + }), + ("Spanakopita.cook", { + "category": "dinner", + "tags": ["vegetarian", "mediterranean", "greek"] + }), + ("Spicy Shrimp Paella.cook", { + "category": "dinner", + "tags": ["seafood", "shrimp", "spanish", "rice"] + }), + ("Strawberry and Wild Rice Tartlets.cook", { + "category": "appetizer", + "tags": ["vegetarian", "gluten-free-option", "dessert"] + }), + ("Thai_Veggie_Pad_See_Ew.cook", { + "category": "dinner", + "tags": ["vegetarian", "vegan-option", "asian", "thai", "noodles"] + }), + ("Tiramisu.cook", { + "category": "lunch", + "tags": ["vegetarian", "italian", "dessert"] + }), + ("Warm Up With a Quick Lemony Chicken Soup.cook", { + "category": "lunch", + "tags": ["meat", "chicken", "soup", "gluten-free"] + }), + ("West African Peanut Chicken.cook", { + "category": "dinner", + "tags": ["meat", "chicken", "african", "stew"] + }), + ("Wheat Berry Salad.cook", { + "category": "salad", + "tags": ["vegetarian", "vegan", "grain-bowl"] + }), +] + + +def update_frontmatter(filepath, classification): + """Update category and tags in the YAML frontmatter.""" + content = filepath.read_text(encoding="utf-8") + + # Find the YAML frontmatter + m = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not m: + print(f" No frontmatter in {filepath.name}") + return False + + fm_text = m.group(1) + lines = fm_text.split('\n') + + new_lines = [] + in_tags = False + category_updated = False + tags_updated = False + + for line in lines: + stripped = line.strip() + + if stripped.startswith('category:'): + new_lines.append(f"category: {classification['category']}") + category_updated = True + in_tags = False + elif stripped.startswith('tags:'): + # Skip old tags, we'll add new ones + in_tags = True + tags_updated = True + new_lines.append("tags:") + for tag in classification['tags']: + new_lines.append(f" - {tag}") + elif in_tags: + # Skip old tag lines (indented with -) + if stripped.startswith('-') or stripped == '': + continue + else: + in_tags = False + new_lines.append(line) + else: + new_lines.append(line) + + if not category_updated: + # Add category before tags or at end of frontmatter + print(f" No category field in {filepath.name}") + + if not tags_updated: + # Add tags at end of frontmatter + new_lines.append("tags:") + for tag in classification['tags']: + new_lines.append(f" - {tag}") + + new_fm = '\n'.join(new_lines) + new_content = f"---\n{new_fm}\n---" + content[m.end():] + filepath.write_text(new_content, encoding="utf-8") + return True + + +def main(): + updated = 0 + errors = 0 + + for filename, classification in CLASSIFICATIONS: + fp = WORKDIR / filename + if not fp.exists(): + print(f"NOT FOUND: {filename}") + errors += 1 + continue + + try: + update_frontmatter(fp, classification) + print(f"UPDATED: {filename} → {classification['category']} [{', '.join(classification['tags'])}]") + updated += 1 + except Exception as e: + print(f"ERROR: {filename}: {e}") + errors += 1 + + print(f"\n{'='*50}") + print(f"Total: {len(CLASSIFICATIONS)} | Updated: {updated} | Errors: {errors}") + + +if __name__ == "__main__": + main()