Public Access
feat: migration 0007 - seed canonical ingredients with aliases (recipes follow in P4-14)
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
"""seed canonical ingredients (and 30 starter recipes in P4-14)
|
||||
|
||||
Revision ID: 0007
|
||||
Revises: 0006
|
||||
Create Date: 2026-05-05
|
||||
"""
|
||||
from decimal import Decimal
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "0007"
|
||||
down_revision = "0006"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
# Canonical ingredients used by the 30 starter recipes (P4-14 references them
|
||||
# by name_lower so this list stays the source of truth for both seed steps).
|
||||
INGREDIENTS = [
|
||||
# (name, aliases, aisle, unit, typical_price)
|
||||
("Chicken Thighs, Boneless Skinless", ["chicken thigh", "BSL chicken thighs"], "meat_seafood", "lb", Decimal("4.99")),
|
||||
("Chicken Breast, Boneless Skinless", ["chicken breast", "BSL chicken breast"], "meat_seafood", "lb", Decimal("5.99")),
|
||||
("Ground Beef, 85/15", ["ground beef", "hamburger"], "meat_seafood", "lb", Decimal("6.99")),
|
||||
("Ground Turkey", ["turkey mince"], "meat_seafood", "lb", Decimal("5.49")),
|
||||
("Pork Chops, Bone-In", ["pork chop"], "meat_seafood", "lb", Decimal("4.49")),
|
||||
("Salmon Fillet", ["salmon"], "meat_seafood", "lb", Decimal("12.99")),
|
||||
("Shrimp, Peeled", ["shrimp"], "meat_seafood", "lb", Decimal("9.99")),
|
||||
("Eggs, Large", ["egg"], "dairy", "ea", Decimal("0.40")),
|
||||
("Black Beans, Canned", ["black bean"], "pantry", "can", Decimal("1.29")),
|
||||
("Chickpeas, Canned", ["garbanzo", "chickpea"], "pantry", "can", Decimal("1.49")),
|
||||
|
||||
("Yellow Onion", ["onion"], "produce", "ea", Decimal("0.99")),
|
||||
("Garlic", ["garlic clove"], "produce", "clove", Decimal("0.10")),
|
||||
("Carrot", ["carrots"], "produce", "ea", Decimal("0.50")),
|
||||
("Celery", [], "produce", "stalk", Decimal("0.30")),
|
||||
("Bell Pepper, Red", ["red pepper"], "produce", "ea", Decimal("1.49")),
|
||||
("Bell Pepper, Green", ["green pepper"], "produce", "ea", Decimal("0.99")),
|
||||
("Tomato, Roma", ["tomato"], "produce", "ea", Decimal("0.79")),
|
||||
("Lemon", [], "produce", "ea", Decimal("0.79")),
|
||||
("Lime", [], "produce", "ea", Decimal("0.50")),
|
||||
("Cilantro", ["coriander leaf"], "produce", "bunch", Decimal("0.99")),
|
||||
("Parsley, Italian", ["parsley"], "produce", "bunch", Decimal("0.99")),
|
||||
("Spinach, Fresh", ["spinach"], "produce", "oz", Decimal("0.40")),
|
||||
("Broccoli", [], "produce", "lb", Decimal("2.49")),
|
||||
("Zucchini", [], "produce", "ea", Decimal("0.99")),
|
||||
("Sweet Potato", [], "produce", "ea", Decimal("1.29")),
|
||||
|
||||
("Olive Oil", ["EVOO", "extra virgin olive oil"], "pantry", "tbsp", Decimal("0.20")),
|
||||
("Soy Sauce", [], "pantry", "tbsp", Decimal("0.15")),
|
||||
("Rice, Long-Grain White", ["white rice", "rice"], "pantry", "cup", Decimal("0.40")),
|
||||
("Pasta, Penne", ["penne"], "pantry", "lb", Decimal("1.29")),
|
||||
("Pasta, Spaghetti", ["spaghetti"], "pantry", "lb", Decimal("1.29")),
|
||||
("Tortilla, Flour", ["flour tortilla"], "pantry", "ea", Decimal("0.30")),
|
||||
("Tortilla, Corn", ["corn tortilla"], "pantry", "ea", Decimal("0.20")),
|
||||
("Diced Tomatoes, Canned", ["canned tomato"], "pantry", "can", Decimal("1.49")),
|
||||
("Chicken Broth", ["chicken stock"], "pantry", "cup", Decimal("0.30")),
|
||||
("Coconut Milk, Canned", [], "pantry", "can", Decimal("2.49")),
|
||||
("Salt, Kosher", ["kosher salt"], "pantry", "tsp", Decimal("0.01")),
|
||||
("Black Pepper", ["pepper"], "pantry", "tsp", Decimal("0.02")),
|
||||
("Cumin, Ground", ["cumin"], "pantry", "tsp", Decimal("0.10")),
|
||||
("Paprika, Smoked", ["smoked paprika"], "pantry", "tsp", Decimal("0.10")),
|
||||
("Italian Seasoning", [], "pantry", "tsp", Decimal("0.10")),
|
||||
("Curry Powder", [], "pantry", "tsp", Decimal("0.10")),
|
||||
("Ginger, Fresh", ["ginger root"], "produce", "tbsp", Decimal("0.30")),
|
||||
|
||||
("Cheddar Cheese, Sharp", ["cheddar"], "dairy", "oz", Decimal("0.40")),
|
||||
("Mozzarella, Shredded", ["mozzarella"], "dairy", "oz", Decimal("0.45")),
|
||||
("Parmesan, Grated", ["parm"], "dairy", "tbsp", Decimal("0.20")),
|
||||
("Sour Cream", [], "dairy", "tbsp", Decimal("0.10")),
|
||||
("Greek Yogurt, Plain", ["yogurt"], "dairy", "cup", Decimal("1.50")),
|
||||
("Butter, Unsalted", ["butter"], "dairy", "tbsp", Decimal("0.20")),
|
||||
("Milk, Whole", ["milk"], "dairy", "cup", Decimal("0.30")),
|
||||
|
||||
("Avocado", [], "produce", "ea", Decimal("1.49")),
|
||||
("Salsa, Jarred", ["salsa"], "pantry", "tbsp", Decimal("0.15")),
|
||||
]
|
||||
|
||||
|
||||
def _upsert_ingredients() -> None:
|
||||
bind = op.get_bind()
|
||||
for name, aliases, aisle, unit, typical_price in INGREDIENTS:
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
INSERT INTO ingredient (id, name, name_lower, aliases, aisle, unit, typical_price)
|
||||
VALUES (
|
||||
gen_random_uuid(), :name, :name_lower, :aliases, :aisle, :unit, :typical_price
|
||||
)
|
||||
ON CONFLICT (name_lower) DO UPDATE SET
|
||||
aliases = EXCLUDED.aliases,
|
||||
aisle = COALESCE(EXCLUDED.aisle, ingredient.aisle),
|
||||
unit = COALESCE(EXCLUDED.unit, ingredient.unit),
|
||||
typical_price = COALESCE(EXCLUDED.typical_price, ingredient.typical_price)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"name": name,
|
||||
"name_lower": name.lower(),
|
||||
"aliases": aliases,
|
||||
"aisle": aisle,
|
||||
"unit": unit,
|
||||
"typical_price": typical_price,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _insert_recipes() -> None:
|
||||
"""Populated in P4-14."""
|
||||
pass
|
||||
|
||||
|
||||
def _delete_recipes() -> None:
|
||||
"""Populated in P4-14."""
|
||||
pass
|
||||
|
||||
|
||||
def _delete_ingredients() -> None:
|
||||
"""Best-effort: only remove rows whose name_lower matches one of our seeds.
|
||||
Pre-existing rows from migration 0002 may share names; their aliases/aisle
|
||||
will revert to whatever was in 0002 (NULL aliases pre-0006). For a clean
|
||||
rollback in this scenario, run alembic downgrade past 0002 as well.
|
||||
"""
|
||||
bind = op.get_bind()
|
||||
name_lowers = [name.lower() for name, *_ in INGREDIENTS]
|
||||
bind.execute(
|
||||
sa.text("DELETE FROM ingredient WHERE name_lower = ANY(:nl)"),
|
||||
{"nl": name_lowers},
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_upsert_ingredients()
|
||||
_insert_recipes()
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_delete_recipes()
|
||||
_delete_ingredients()
|
||||
Reference in New Issue
Block a user