Add seed command: 29 ingredients, pantry from Pantry.md, 2 meta-recipes (stir fry + traybake)

This commit is contained in:
Caine
2026-04-01 22:50:04 +01:00
parent 999ca28b67
commit e76a8fe656
3 changed files with 247 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
"""
Seed the database with initial data:
- Tags
- Ingredients (from current pantry + recipe knowledge)
- Current pantry items (migrated from Food/Pantry.md)
- Meta-recipes: Stir Fry and Traybake
"""
from datetime import date
from decimal import Decimal
from django.core.management.base import BaseCommand
from kitchen.models import (
Tag,
Ingredient,
PantryItem,
MetaRecipe,
Slot,
SlotOption,
MetaRecipeBase,
)
class Command(BaseCommand):
help = "Seed database with initial ingredients, pantry, and meta-recipes"
def handle(self, *args, **options):
self.stdout.write("Creating tags...")
tags = {}
for name in ["protein", "carb", "veg", "seasoning", "dairy", "sauce", "staple", "frozen"]:
tags[name], _ = Tag.objects.get_or_create(name=name)
self.stdout.write("Creating ingredients...")
ingredients = {}
def make_ingredient(name, unit, tag_names, shelf_life=None, aliases=None):
obj, _ = Ingredient.objects.get_or_create(
name=name,
defaults={
"default_unit": unit,
"shelf_life_days": shelf_life,
"aliases": aliases or [],
},
)
for t in tag_names:
obj.tags.add(tags[t])
ingredients[name] = obj
return obj
# Proteins
make_ingredient("chicken mini fillets", "items", ["protein"], shelf_life=2, aliases=["chicken fillets", "mini fillets"])
make_ingredient("chicken thighs", "items", ["protein"], shelf_life=2, aliases=["chicken thigh"])
make_ingredient("pork mince", "g", ["protein"], shelf_life=2, aliases=["pork mince 500g"])
make_ingredient("sausages", "items", ["protein"], shelf_life=5, aliases=["pork sausages"])
# Carbs
make_ingredient("egg noodles", "nests", ["carb"], aliases=["noodles", "egg noodle nests"])
make_ingredient("microwave rice", "packets", ["carb"], aliases=["rice"])
make_ingredient("pasta", "g", ["carb"], aliases=["dried pasta"])
make_ingredient("bread", "loaves", ["carb"], shelf_life=5)
make_ingredient("frozen chips", "bags", ["carb", "frozen", "staple"], aliases=["chips", "McCain chips"])
# Veg
make_ingredient("onions", "items", ["veg", "staple"], aliases=["onion"])
make_ingredient("garlic", "bulbs", ["veg"], shelf_life=14, aliases=["garlic bulb"])
make_ingredient("frozen stir fry veg", "bags", ["veg", "frozen"], aliases=["stir fry veg", "Birds Eye stir fry veg"])
make_ingredient("broccoli", "heads", ["veg"], shelf_life=5)
make_ingredient("peppers", "items", ["veg"], shelf_life=7, aliases=["pepper", "bell pepper"])
make_ingredient("potatoes", "items", ["veg"], shelf_life=14, aliases=["Cyprus potatoes"])
make_ingredient("chopped tomatoes", "tins", ["veg"], aliases=["tinned tomatoes"])
# Dairy / Other
make_ingredient("eggs", "items", ["protein"], shelf_life=14, aliases=["free range eggs"])
make_ingredient("milk", "bottles", ["dairy"], shelf_life=7)
make_ingredient("Lurpak spreadable", "tubs", ["dairy"], aliases=["butter", "Lurpak"])
# Seasonings & Sauces
make_ingredient("salt", "n/a", ["seasoning", "staple"])
make_ingredient("black pepper", "n/a", ["seasoning", "staple"])
make_ingredient("olive oil", "bottles", ["seasoning", "staple"])
make_ingredient("paprika", "n/a", ["seasoning", "staple"])
make_ingredient("garlic powder", "n/a", ["seasoning", "staple"])
make_ingredient("soy sauce", "bottles", ["sauce"], aliases=["soy"])
make_ingredient("Lao Gan Ma", "jars", ["sauce"], aliases=["chilli crisp"])
make_ingredient("peanut butter", "jars", ["staple"], aliases=["Whole Earth crunchy"])
# Other
make_ingredient("baked beans", "tins", ["staple"])
make_ingredient("freezer bags", "boxes", ["staple"], aliases=["Tesco tie handle"])
self.stdout.write(f" Created {len(ingredients)} ingredients")
# --- Pantry Items (from current Pantry.md as of 31 Mar 2026) ---
self.stdout.write("Creating pantry items...")
def add_pantry(name, qty, unit, location, expiry=None, is_staple=False, notes=""):
PantryItem.objects.get_or_create(
ingredient=ingredients[name],
defaults={
"quantity": Decimal(str(qty)),
"unit": unit,
"location": location,
"expiry_date": expiry,
"is_staple": is_staple,
"notes": notes,
},
)
# Fridge
add_pantry("onions", 2, "items", "fridge", is_staple=True)
add_pantry("milk", 1, "bottles", "fridge", expiry=date(2026, 4, 3))
add_pantry("egg noodles", 1, "nests", "fridge")
add_pantry("garlic", 1, "bulbs", "fridge", expiry=date(2026, 4, 2), notes="minus 2 cloves")
add_pantry("bread", 1, "loaves", "fridge", expiry=date(2026, 4, 1))
add_pantry("pasta", 4, "portions", "cupboard")
add_pantry("microwave rice", 2, "packets", "cupboard")
add_pantry("peanut butter", 1, "jars", "cupboard")
add_pantry("Lurpak spreadable", 1, "tubs", "fridge")
add_pantry("Lao Gan Ma", 1, "jars", "cupboard")
add_pantry("sausages", 3, "items", "fridge", expiry=date(2026, 3, 30), notes="Likely expired — sniff test")
# Freezer
add_pantry("sausages", 3, "items", "freezer", notes="From frozen pack")
add_pantry("frozen chips", 1, "bags", "freezer", is_staple=True)
add_pantry("bread", 1, "loaves", "freezer", notes="Sliced")
# Staples (seasonings — always in cupboard)
for name in ["salt", "black pepper", "olive oil", "paprika", "garlic powder"]:
add_pantry(name, 1, "n/a", "cupboard", is_staple=True)
# Out of stock (for reference)
add_pantry("eggs", 0, "items", "fridge", notes="OUT — restock")
add_pantry("baked beans", 1, "tins", "cupboard")
add_pantry("freezer bags", 1, "boxes", "cupboard")
self.stdout.write(" Pantry seeded")
# --- Meta-Recipes ---
self.stdout.write("Creating meta-recipes...")
# === STIR FRY ===
stir_fry, _ = MetaRecipe.objects.get_or_create(
name="Stir Fry",
defaults={
"method": (
"1. Defrost protein if frozen (cold water, ~30 mins for fillets)\n"
"2. Slice protein thin, fry in oil til cooked through\n"
"3. Dice onion + mince garlic, add to pan\n"
"4. Add veg (frozen goes straight in)\n"
"5. Boil carb separately if needed (noodles 4 mins, rice per packet)\n"
"6. Toss everything together with sauce\n"
"7. Optional: push to one side, crack egg in, scramble, mix through"
),
"prep_time_mins": 10,
"cook_time_mins": 15,
"default_servings": 2,
"gear_needed": "frying pan, saucepan",
"tags": ["quick", "one-pan"],
},
)
# Stir fry slots
sf_protein, _ = Slot.objects.get_or_create(
meta_recipe=stir_fry, name="protein", defaults={"required": True, "max_choices": 1}
)
sf_carb, _ = Slot.objects.get_or_create(
meta_recipe=stir_fry, name="carb", defaults={"required": True, "max_choices": 1}
)
sf_veg, _ = Slot.objects.get_or_create(
meta_recipe=stir_fry, name="veg", defaults={"required": True, "max_choices": 3}
)
sf_sauce, _ = Slot.objects.get_or_create(
meta_recipe=stir_fry, name="sauce", defaults={"required": False, "max_choices": 2}
)
# Stir fry slot options
SlotOption.objects.get_or_create(slot=sf_protein, ingredient=ingredients["chicken mini fillets"], defaults={"quantity_per_serving": Decimal("2"), "unit": "items", "notes": "defrost first"})
SlotOption.objects.get_or_create(slot=sf_protein, ingredient=ingredients["pork mince"], defaults={"quantity_per_serving": Decimal("250"), "unit": "g"})
SlotOption.objects.get_or_create(slot=sf_carb, ingredient=ingredients["egg noodles"], defaults={"quantity_per_serving": Decimal("2"), "unit": "nests"})
SlotOption.objects.get_or_create(slot=sf_carb, ingredient=ingredients["microwave rice"], defaults={"quantity_per_serving": Decimal("1"), "unit": "packets"})
SlotOption.objects.get_or_create(slot=sf_veg, ingredient=ingredients["frozen stir fry veg"], defaults={"quantity_per_serving": Decimal("0.5"), "unit": "bags"})
SlotOption.objects.get_or_create(slot=sf_veg, ingredient=ingredients["broccoli"], defaults={"quantity_per_serving": Decimal("0.5"), "unit": "heads"})
SlotOption.objects.get_or_create(slot=sf_veg, ingredient=ingredients["peppers"], defaults={"quantity_per_serving": Decimal("1"), "unit": "items"})
SlotOption.objects.get_or_create(slot=sf_sauce, ingredient=ingredients["soy sauce"], defaults={"quantity_per_serving": Decimal("1"), "unit": "splash"})
SlotOption.objects.get_or_create(slot=sf_sauce, ingredient=ingredients["Lao Gan Ma"], defaults={"quantity_per_serving": Decimal("1"), "unit": "spoonful"})
# Stir fry base ingredients
MetaRecipeBase.objects.get_or_create(meta_recipe=stir_fry, ingredient=ingredients["onions"], defaults={"quantity_per_serving": Decimal("1"), "unit": "items"})
MetaRecipeBase.objects.get_or_create(meta_recipe=stir_fry, ingredient=ingredients["garlic"], defaults={"quantity_per_serving": Decimal("2"), "unit": "cloves"})
MetaRecipeBase.objects.get_or_create(meta_recipe=stir_fry, ingredient=ingredients["olive oil"], defaults={"quantity_per_serving": Decimal("1"), "unit": "splash"})
# === TRAYBAKE ===
traybake, _ = MetaRecipe.objects.get_or_create(
name="Traybake",
defaults={
"method": (
"1. Preheat oven to 200°C\n"
"2. Dice onion, chop veg into chunks\n"
"3. Toss protein + veg in oil and seasoning on baking tray\n"
"4. Spread out in single layer\n"
"5. Oven for 30-40 mins, turning halfway\n"
"6. If using chicken thighs: remove skin before cooking"
),
"prep_time_mins": 10,
"cook_time_mins": 35,
"default_servings": 2,
"gear_needed": "baking tray",
"tags": ["easy", "one-tray", "batch-cook"],
},
)
# Traybake slots
tb_protein, _ = Slot.objects.get_or_create(
meta_recipe=traybake, name="protein", defaults={"required": True, "max_choices": 1}
)
tb_veg, _ = Slot.objects.get_or_create(
meta_recipe=traybake, name="veg", defaults={"required": True, "max_choices": 3}
)
tb_seasoning, _ = Slot.objects.get_or_create(
meta_recipe=traybake, name="seasoning", defaults={"required": True, "max_choices": 1}
)
# Traybake slot options
SlotOption.objects.get_or_create(slot=tb_protein, ingredient=ingredients["sausages"], defaults={"quantity_per_serving": Decimal("3"), "unit": "items"})
SlotOption.objects.get_or_create(slot=tb_protein, ingredient=ingredients["chicken thighs"], defaults={"quantity_per_serving": Decimal("2"), "unit": "items", "notes": "skin off"})
SlotOption.objects.get_or_create(slot=tb_veg, ingredient=ingredients["peppers"], defaults={"quantity_per_serving": Decimal("1"), "unit": "items"})
SlotOption.objects.get_or_create(slot=tb_veg, ingredient=ingredients["broccoli"], defaults={"quantity_per_serving": Decimal("0.5"), "unit": "heads"})
SlotOption.objects.get_or_create(slot=tb_veg, ingredient=ingredients["potatoes"], defaults={"quantity_per_serving": Decimal("2"), "unit": "items"})
SlotOption.objects.get_or_create(slot=tb_veg, ingredient=ingredients["frozen chips"], defaults={"quantity_per_serving": Decimal("1"), "unit": "handful"})
SlotOption.objects.get_or_create(slot=tb_seasoning, ingredient=ingredients["paprika"], defaults={"quantity_per_serving": Decimal("1"), "unit": "tsp"})
# Traybake base ingredients
MetaRecipeBase.objects.get_or_create(meta_recipe=traybake, ingredient=ingredients["onions"], defaults={"quantity_per_serving": Decimal("1"), "unit": "items"})
MetaRecipeBase.objects.get_or_create(meta_recipe=traybake, ingredient=ingredients["olive oil"], defaults={"quantity_per_serving": Decimal("1"), "unit": "drizzle"})
MetaRecipeBase.objects.get_or_create(meta_recipe=traybake, ingredient=ingredients["salt"], defaults={"quantity_per_serving": Decimal("1"), "unit": "pinch"})
MetaRecipeBase.objects.get_or_create(meta_recipe=traybake, ingredient=ingredients["black pepper"], defaults={"quantity_per_serving": Decimal("1"), "unit": "pinch"})
self.stdout.write(self.style.SUCCESS("✅ Seed complete!"))
self.stdout.write(f" Tags: {Tag.objects.count()}")
self.stdout.write(f" Ingredients: {Ingredient.objects.count()}")
self.stdout.write(f" Pantry items: {PantryItem.objects.count()}")
self.stdout.write(f" Meta-recipes: {MetaRecipe.objects.count()}")
self.stdout.write(f" Slots: {Slot.objects.count()}")
self.stdout.write(f" Slot options: {SlotOption.objects.count()}")