Org Web Adapter

hungryroot/popsicle/cookbook-modification.org

ID
d4e171f5-9b1a-4720-9741-505056aa6119

Cookbook Simplification

Overview

Three related cleanup efforts on the cookbook data model and API:

1. Simplify cookbook types (THEMATIC/PERSONALITY → MARKETING; FAVORITES unchanged)

2. Deprecate sections from the API

3. Add tag-based filtering to replace section grouping

4. Flatten the Cookbook LIST API

See also: flat-cookbook-list-api.org for the flatter list endpoint spec.

Need: Simplify Cookbook Types

Current state

~cookbook_type~ has values: ~THEMATIC~, ~PERSONALITY~, ~FAVORITES~

Target state

| Value | Replaces | Description |

|-------|----------|-------------|

| ~MARKETING~ | ~THEMATIC~, ~PERSONALITY~ | Staff/marketing-controlled cookbooks. All current cookbooks are this type. |

| ~FAVORITES~ | ~FAVORITES~ | Synthesized per-customer cookbook. Unchanged. |

| ~USER~ | (future) | User-created cookbooks. Added when user-created cookbook CRUD lands. |

Work items

- Data migration: ~THEMATIC~ → ~MARKETING~, ~PERSONALITY~ → ~MARKETING~

- Update ~CookbookType~ enum/choices in ~models.py~ (remove ~THEMATIC~, ~PERSONALITY~; add ~MARKETING~)

- Update any code filtering or branching on ~cookbook_type~

Need: Deprecate Sections (Remove from API)

Sections were used to group content in marketing-oriented cookbooks (e.g. "Breakfast",

"Lunch + Dinner", "Snacks"). That grouping is moving to tag-based filtering (see below).

Sections remain in the data model for now; only the API surface changes.

See flat-cookbook-list-api.org for the full spec. Summary:

- List endpoint: ~sections~ removed, replaced by flat ~pairing_ids~ + ~product_ids~

- ~author~ collapsed to ~author_id~

- ~collections~ removed (deprecated model)

- Detail endpoint: unchanged for now

Ordering: decided

Pairings render at the top of the grid; products below. True interleaving (random mix) is

not viable because it would require waiting for both ~/api/v3/pairings/~ and

~/api/v3/products/~ to fully resolve before rendering any cards, defeating the

skeleton/progressive hydration pattern.

Products may load faster and render first, then get pushed down the page when pairings

load in. This is an acceptable UI concern to handle client-side, not a performance

concern.

Need: Allow Cookbook Detail Filtering by DC Tag Keys (like exp242)

Sections provided browse-by-category UX ("Breakfast", "Lunch + Dinner", "Snacks"). That

grouping moves to tag-based filtering on the ~GET /api/v3/cookbooks/<slug>/~ detail

endpoint, matching the pattern already implemented for the customer favorites cookbook

endpoint.

Target behavior

Clients pass tag IDs to filter the ~pairing_ids~/~product_ids~ returned by a single

cookbook:

#+begin_example

GET /api/v3/cookbooks/<slug>/?tag_id=<breakfast-tag-id>

GET /api/v3/cookbooks/<slug>/?tag_id=<lunch-dinner-tag-id>

GET /api/v3/cookbooks/<slug>/?tag_id=<snacks-tag-id>

#+end_example

Work items

- Port tag-filter logic from the customer favorites cookbook endpoint to ~CookbookDetailView~

- Identify the DC dictionary tag IDs that map to the former section names

- Document the tag ID → display label mapping for the frontend

Need: Flatten the Cookbook LIST Endpoint

The ~GET /api/v3/cookbooks/~ list response currently nests ~sections~, a full ~author~

object, and ~collections~ inside each cookbook. Replace all of that with flat scalar

fields. Full spec: flat-cookbook-list-api.org.

Response shape changes

| Field | Before | After |

|-------|--------|-------|

| ~author~ | nested object (id, name, slug, bio, …) | removed |

| ~author_id~ | absent | nullable int |

| ~sections~ | nested list of section+item objects | removed |

| ~pairing_ids~ | absent | flat list of ints |

| ~product_ids~ | absent | flat list of ints |

| ~collections~ | nested list of collection objects | removed |

All other scalar fields (~id~, ~slug~, ~name~, ~description~, ~hero_image~,

~is_featured~, ~is_active~, ~sort_order~, ~cookbook_type~, ~visibility~, ~kind~,

~owner_id~, ~dietary_tag_ids~, ~pairing_count~, ~product_count~, ~referral_code~,

~count~, ~next~, ~previous~) are unchanged.

Migration: two phases

To avoid breaking client code, roll this out in two phases:

*Phase 1 — add new properties alongside old ones*

Add ~author_id~, ~pairing_ids~, ~product_ids~ to the response while keeping ~author~,

~sections~, ~collections~. Client code updates to use the new properties; no user impact.

*Phase 2 — remove old properties*

Once client code no longer references ~author~, ~sections~, ~collections~, drop them from

the serializer.

Work items

- Add ~CookbookListSerializer~ in ~cookbooks/serializers.py~

- Wire ~CookbookListView~ to use ~CookbookListSerializer~ instead of ~CookbookSerializer~

- Drop ~collections~ prefetch from list queryset; keep ~sections__items~ (needed for flat ID lists)

- Update ~CookbookV3ReadAPITestCase~ tests to assert new shape

Detail endpoint

~GET /api/v3/cookbooks/<slug>/~ keeps the existing ~CookbookSerializer~ and nested

structure unchanged for now.

Frontend Fetch Architecture

Cookbook landing pages use a hybrid SSR + client-side fetch pattern:

SSR (fast, SEO-critical)

Single request to ~GET /api/v3/cookbooks/<slug>/~ (detail endpoint). Populates title,

hero image, description, author info, and previously section headings. Section headings

go away with the new grid/list view. This is what gets indexed.

Client-side (parallel, progressive)

Two independent requests fire after SSR:

- ~GET /api/v3/pairings/?ids=[]&expand=ingredients,tags~ — pairing card content

- ~GET /api/v3/products/?ids=[]&expand=tags~ — product card content

Cards render with skeleton placeholders immediately; each hydrates as its request

resolves. Products often resolve faster than pairings, so product cards may appear first.

Client-side (follow-up)

Badge and ingredient thumbnail data — depends on the pairing/product responses above.

Scope Notes

- MARKETING cookbooks: staff controls content. Not expected to have a large number of

products/pairings per cookbook — keep lists bounded.

- FAVORITES cookbooks: synthesized per-customer. Shared via referral link. Ordering deferred

(no favorites signal available in listing context today).

- USER type reserved for future user-created cookbook CRUD; not implemented yet.

- Landing page context: no per-customer favorites signal available. Ordering deferred.