#!/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()