410 lines
13 KiB
Python
410 lines
13 KiB
Python
#!/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()
|