Org Web Adapter

pages/ep_627_exp_232_cookbook_mvp.org

ID
2f37c7bc-6cb2-47ae-a513-f634e5c4cfbb

EP-627 Exp 232: Cookbook MVP

#+begin_src python

from cookbooks.models import Cookbook

from cookbooks.serializers import _auto_select_hero_image_from_sections

def _batch_pairing_featured_urls_by_id(pairing_ids: set[int]) -> dict[int, str | None]:

if not pairing_ids:

return {}

return {p.id: p.featured_img_url for p in Pairing.objects.only("id", "featured_img").filter(id__in=pairing_ids)}

def _batch_product_usable_image_urls_by_id(product_ids: set[int]) -> dict[int, str | None]:

if not product_ids:

return {}

result: dict[int, str | None] = {}

for product in Product.objects.only("id", "card_image", "large_image").filter(id__in=product_ids):

result[product.id] = product.card_image_url or product.large_image_url or None

return result

def _auto_select_cover_image_source_and_url_from_sections(

sections: list[dict[str, Any]],

) -> tuple[_CoverImageSource, str] | None:

"""Pick hero/cover image: all pairing items cookbook-wide (section then item order), then all products."""

pairing_sequence = [id_ for _, id_ in _iter_pairing_cover_sources_global(sections)]

pairing_urls = _batch_pairing_featured_urls_by_id(set(pairing_sequence))

for pid in pairing_sequence:

url = pairing_urls.get(pid)

if url:

return ("pairing", pid), url

product_sequence = [id_ for _, id_ in _iter_product_cover_sources_global(sections)]

product_urls = _batch_product_usable_image_urls_by_id(set(product_sequence))

for prod_id in product_sequence:

url = product_urls.get(prod_id)

if url:

return ("product", prod_id), url

return None

def _sections_data(cookbook):

return [

{

"position": section.position,

"items": [

{"pairing_id": item.pairing_id, "product_id": item.product_id, "position": item.position}

for item in section.items.all()

],

}

for section in cookbook.sections.all()

]

# Adjust filter as needed — e.g. all active cookbooks missing a hero image

qs = Cookbook.objects.filter(hero_image__isnull=True, is_active=True).prefetch_related("sections__items")

updated = []

for cb in qs:

url = _auto_select_hero_image_from _sections(_sections_data(cb))

if url:

updated = []

for cb in qs:

url = _auto_select_hero_image_from_sections(_sections_data(cb))

if url:

cb.hero_image = url

updated.append(cb)

Cookbook.objects.bulk_update(updated, ["hero_image"])

print(f"Updated {len(updated)} of {qs.count()} cookbooks")

#+end_src

#+begin_src python

from cookbooks.models import Cookbook

from cookbooks.serializers import _auto_select_hero_image_from_sections

cookbooks = Cookbook.objects.prefetch_related("sections__items").all()

updated = 0

skipped = 0

for cookbook in cookbooks:

sections = [

{

"position": section.position,

"items": [

{

"position": item.position,

"pairing_id": item.pairing_id,

"product_id": item.product_id,

}

for item in section.items.all()

],

}

for section in cookbook.sections.all()

]

hero_image = _auto_select_hero_image_from_sections(sections)

if hero_image:

cookbook.hero_image = hero_image

cookbook.save(update_fields=["hero_image"])

updated += 1

else:

skipped += 1

print(f"Updated: {updated}, Skipped (no image found): {skipped}")

#+end_src

Example cookbook PUT data:

#+begin_src json

{

"id": 1,

"name": "Summer Grilling",

"slug": "summer-grilling",

"description": "Light meals for warm evenings.",

"is_featured": false,

"is_active": true,

"sort_order": 0,

"cookbook_type": "thematic",

"hero_image_pairing_id": 125451,

"dietary_tag_ids": [1, 3],

"author": {

"id": 1,

"slug": "chef-jane",

"name": "Chef Jane",

"social_handle": "@chefjane"

},

"sections": [

{

"name": "Mains",

"position": 0,

"items": [

{ "item_type": "pairing", "position": 0, "pairing_id": 125451 },

{ "item_type": "pairing", "position": 1, "pairing_id": 461309 },

{ "item_type": "pairing", "position": 2, "pairing_id": 461408 },

{ "item_type": "pairing", "position": 3, "pairing_id": 464473 }

]

},

{

"name": "Add-ons",

"position": 1,

"items": [

{ "item_type": "product", "position": 0, "product_id": 696 },

{ "item_type": "product", "position": 1, "product_id": 735 },

{ "item_type": "product", "position": 2, "product_id": 1977 },

{ "item_type": "product", "position": 3, "product_id": 1961 }

]

}

]

}

#+end_src