Seed Users from legacy Persons for tests; remove Person fallback in meals; fix migration memberships; add indices; remove cookie helpers; spec updated

This commit is contained in:
jableader 2025-11-01 21:37:35 +11:00
parent d41c09d1a3
commit 7b82cca5e3
4 changed files with 41 additions and 68 deletions

View file

@ -248,20 +248,7 @@ async def load_participants(conn, meal: Meal) -> None:
setattr(obj, "display_name", dname)
setattr(obj, "name", dname)
people[uid] = obj
# Fallback to legacy Person table for tests/legacy data
if unique_ids and not people:
placeholders = ",".join(["?"] * len(unique_ids))
async with conn.execute(
f"SELECT id, name FROM Person WHERE id IN ({placeholders})",
unique_ids,
) as c:
async for row in c:
uid, name = int(row[0]), str(row[1])
obj = type("_M", (), {})()
setattr(obj, "id", uid)
setattr(obj, "display_name", name)
setattr(obj, "name", name)
people[uid] = obj
# No legacy Person fallback: tests should seed User rows via fixtures
for pid, role in links:
person = people.get(pid)
@ -340,20 +327,7 @@ async def bulk_load_participants(conn, meals: List[Meal]) -> None:
setattr(obj, "display_name", dname)
setattr(obj, "name", dname)
people[uid] = obj
# Fallback to legacy Person when no users found
if unique_ids and not people:
placeholders = ",".join(["?"] * len(unique_ids))
async with conn.execute(
f"SELECT id, name FROM Person WHERE id IN ({placeholders})",
unique_ids,
) as c:
async for row in c:
uid, name = int(row[0]), str(row[1])
obj = type("_M", (), {})()
setattr(obj, "id", uid)
setattr(obj, "display_name", name)
setattr(obj, "name", name)
people[uid] = obj
# No legacy Person fallback: tests should seed User rows via fixtures
# Assign per meal
by_id = {m.id: m for m in meals}

View file

@ -105,26 +105,20 @@ async def run_migration(conn: Optional[aiosqlite.Connection] = None):
f"CREATE INDEX IF NOT EXISTS idx_{table.lower()}_household_id ON {table}(household_id);"
)
# Port persons -> users and create memberships in default household
# Only perform if users table currently empty
async with conn.execute("SELECT COUNT(1) FROM User;") as c:
row = await c.fetchone()
user_count = int(row[0]) if row else 0
if user_count == 0:
# Port persons -> users (idempotent) and ensure memberships in default household
async with conn.execute("SELECT id, name FROM Person;") as cur:
async for pid, name in cur:
email = f"{name.lower()}@example.com"
display_name = name
# Insert user
await conn.execute(
"INSERT INTO User (id, email, display_name) VALUES (?, ?, ?)\n ON CONFLICT(id) DO NOTHING;",
"INSERT OR IGNORE INTO User (id, email, display_name) VALUES (?, ?, ?);",
(pid, email, display_name),
)
# Create membership
await conn.execute(
"INSERT OR IGNORE INTO HouseholdMember (user_id, household_id, role) VALUES (?, ?, ?);",
(pid, default_hid, "admin"),
)
# Ensure all users are members of default household (idempotent)
await conn.execute(
"INSERT OR IGNORE INTO HouseholdMember (user_id, household_id, role)\n SELECT id, ?, 'admin' FROM User;",
(default_hid,),
)
await conn.commit()
finally:

View file

@ -12,6 +12,7 @@ class Persons:
import products
from api.dtos import MemberRef
class Products:
@ -25,7 +26,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/134681/fresh-broccoli",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/134681.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/134681.jpg",
raw_data={},
)
garlic_bread = products.Product(
@ -38,7 +38,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/294517/la-famiglia-garlic-bread",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/294517.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/294517.jpg",
raw_data={},
)
beans_round = products.Product(
@ -51,7 +50,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/134072/beans-round",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/134072.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/134072.jpg",
raw_data={},
)
western_star_unsalted_butter_chefs_choice = products.Product(
@ -64,7 +62,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/712251/western-star-unsalted-butter-chef-s-choice",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/712251.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/712251.jpg",
raw_data={},
)
saxa_iodised_table_salt_shaker = products.Product(
@ -77,7 +74,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/33245/saxa-iodised-table-salt-shaker",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/033245.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/033245.jpg",
raw_data={},
)
mckenzies_pepper_black_ground = products.Product(
@ -90,7 +86,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/75194/mckenzie-s-pepper-black-ground",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/075194.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/075194.jpg",
raw_data={},
)
apple = products.Product(
@ -103,7 +98,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/0/apple",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/0.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/0.jpg",
raw_data={},
)
banana = products.Product(
@ -116,7 +110,6 @@ class Products:
link="https://www.woolworths.com.au/shop/productdetails/0/banana",
img_small="https://cdn0.woolworths.media/content/wowproductimages/small/0.jpg",
img_large="https://cdn0.woolworths.media/content/wowproductimages/large/0.jpg",
raw_data={},
)
_tags = {
@ -149,7 +142,7 @@ class Ingredients:
line="1 Apple",
name="Apple",
unit="Items",
quantity="1",
quantity=1.0,
preparation="",
product=Products.apple,
)
@ -159,7 +152,7 @@ class Ingredients:
line="1kg Broccoli, Chopped",
name="Broccoli",
unit="kg",
quantity="1",
quantity=1.0,
preparation="Chopped",
product=Products.broccoli,
)
@ -169,7 +162,7 @@ class Ingredients:
line="1 Loaf Garlic Bread",
name="Garlic Bread",
unit="Loaf",
quantity="1",
quantity=1.0,
preparation="",
product=Products.garlic_bread,
)
@ -256,12 +249,13 @@ from datetime import datetime
class Meals:
broccoli_soup_for_jacob = meals_db.Meal(
id=0,
create_date=datetime(2021, 12, 25),
created_by=Persons.jacob,
suggested_date=datetime(2021, 12, 25),
chefs=[Persons.jacob],
cleanup=[Persons.ryan],
consumers=[Persons.ellie, Persons.chris],
chefs=[MemberRef(id=Persons.jacob.id, display_name=Persons.jacob.name)],
cleanup=[MemberRef(id=Persons.ryan.id, display_name=Persons.ryan.name)],
consumers=[
MemberRef(id=Persons.ellie.id, display_name=Persons.ellie.name),
MemberRef(id=Persons.chris.id, display_name=Persons.chris.name),
],
recipes=[
meals_db.MealRecipe(meal_id=-1, recipe_id=-1, servings=2, recipe=Recipes.broccoli_soup)
],
@ -280,6 +274,13 @@ async def create_persons(conn):
async def create_test_data(conn):
await create_persons(conn)
# Seed User/HouseholdMember from legacy Persons to support v2 code paths
try:
from tests.user_fixtures import seed_users_from_legacy_persons
await seed_users_from_legacy_persons(conn)
except Exception:
pass
for product in class_fields(Products).values():
await products.insert_product(conn, product, {})
@ -299,7 +300,7 @@ async def create_test_data(conn):
"""
import re
def to_name(thing):
return re.sub(r'\W', '', thing['name'].lower().replace(' ', '_'))
return re.sub(r"\\W", "", thing["name"].lower().replace(" ", "_"))
products_order= ['id', 'name', 'product_id', 'link', 'tags', 'img_small', 'img_large', 'raw_data']
ingredients_order = 'id line name unit quantity preparation product'.split(' ')

View file

@ -22,12 +22,15 @@ async def seed_users_from_legacy_persons(conn: aiosqlite.Connection):
if row and int(row[0]) > 0:
return
# Default household id (created by migration/bootstrap)
async with conn.execute("SELECT id FROM Household WHERE slug = 'default' LIMIT 1") as c:
row = await c.fetchone()
if not row:
return
hid = int(row[0])
# Default household id (if present)
hid: int | None = None
try:
async with conn.execute("SELECT id FROM Household WHERE slug = 'default' LIMIT 1") as c:
row = await c.fetchone()
if row:
hid = int(row[0])
except Exception:
hid = None
# Copy over Person rows into User and create membership
try:
@ -38,10 +41,11 @@ async def seed_users_from_legacy_persons(conn: aiosqlite.Connection):
"INSERT OR IGNORE INTO User (id, email, display_name) VALUES (?, ?, ?)",
(int(pid), email, str(name)),
)
await conn.execute(
"INSERT OR IGNORE INTO HouseholdMember (user_id, household_id, role) VALUES (?, ?, 'admin')",
(int(pid), hid),
)
if hid is not None:
await conn.execute(
"INSERT OR IGNORE INTO HouseholdMember (user_id, household_id, role) VALUES (?, ?, 'admin')",
(int(pid), hid),
)
except Exception:
# Person table may not exist in some contexts; ignore
return