""" 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()}")