Org Web Adapter

hungryroot/brand/customer-dislikes-survey.org

ID
d604a732-3d17-43ab-b564-b9fcb7d65f8e

Customer Dislikes — Cuisine, Dish Type, Ingredients, Flavors

TL;DR

Customers can express dislikes across all four dimensions, but the

mechanism differs by dimension. *Only ingredient dislikes are a hard

filter.* Cuisine, dish type, and flavor dislikes are soft signals that

nudge recommendations rather than excluding products.

| Dimension | Hard or Soft | Storage |

|------------+----------------+------------------------------------------|

| Cuisine | Soft | =Tag= + =CustomerTag= (TagGroup CUISINE) |

| Dish type | Soft | =DishPreference= (signed =rating=) |

| Ingredient | *Hard* | =Periodicity= (=NEVER= excludes) |

| Flavor | Soft | =Tag= + =CustomerTag= (multiple groups) |

Cuisine

- *Models:* =Tag= + =CustomerTag=

- *Files:* =app/models/tag.py:117-120=

- *Tag group:* =TagGroup.ID.CUISINE= (id =15=)

- *Dislike field:* =CustomerTag.preference= (=SmallIntegerField=, negative

= dislike); =Tag.dislike_score= (=IntegerField=) used in recommendation

scoring.

- *Hard or soft:* *Soft.* Feeds pairing recommendation and product

filtering scoring; does not exclude.

- *API:*

- =GET/POST /customers/{customer_id}/tags/= → =CustomerTagList=

- =GET/PUT /customers/{customer_id}/tags/{tag_group_id}/= for

cuisine-specific operations

- Serializer: =CustomerTagListSerializer= in

=app/rest/customer_tag.py=

Dish Type

- *Model:* =DishPreference=

- *File:* =app/models/dish_preference.py:7-10=

- *Relationship:* =ForeignKey= to =DishType= (line 9)

- *Dislike field:* =rating= (=SmallIntegerField=, default =0=). Negative

value = dislike, positive = like.

- *Hard or soft:* *Soft.* Used for dish-type personalization in

survey/preference flows.

- *API:* No dedicated REST endpoint. Surfaced indirectly via the

preference/survey endpoints.

Ingredient — Hard Exclusion

- *Model:* =Periodicity=

- *File:* =app/models/periodicity.py:7-9=

- *Relationship:* =ForeignKey= to =Product= (which carries ingredients)

- *Field:* =periodicity= (=PositiveSmallIntegerField=) with choices from

=PeriodicityConsts=: =NEVER=, =SOMETIMES=, =OFTEN=.

- *Hard or soft:* *HARD EXCLUSION.* When =periodicity = NEVER=, products

are filtered out entirely via =customer.get_never_products()=, which

is consumed by pairing/product query paths.

- *API:*

- =GET/POST /customers/{customer_id}/periodicities/= →

=PeriodicityListCreateAPIView= in

=app/rest/customer_periodicity.py=

Flavor

- *Models:* =Tag= + =CustomerTag= (same machinery as Cuisine)

- *File:* =app/models/tag.py:117-130=

- *Tag groups:*

- =SPICE_LEVEL= (id =6=)

- =FLAVOR_PROFILE= (id =11=)

- =FLAVOUR= (id =22=) — e.g. tags =HOT= (22), =MILD= (20), =MEDIUM= (21)

- *Dislike fields:* =CustomerTag.preference= (negative);

=Tag.dislike_score=, =Tag.pairing_dislike_score=.

- *Hard or soft:* *Soft.* Scoring-based for recommendations.

- *API:* Same surface as Cuisine — =/customers/{customer_id}/tags/= and

=/customers/{customer_id}/tags/{tag_group_id}/=.

Related: Post-Purchase Feedback (not a preference)

- *Model:* =CustomerFoodFeedback=

- *File:* =app/models/customer_food_feedback.py:15-26=

- Captures feedback tags (taste, ingredients, nutrition) *after* the

customer consumes the item. The =preference= field can be negative

(dislike), but this is a feedback mechanism, not a pre-purchase

preference declaration. Worth distinguishing from the four

preference dimensions above when reasoning about whether a dislike

filters out a product before it's recommended.

Open Questions / Follow-ups

- For the soft dimensions (cuisine, dish type, flavor), what are the

current *scoring weights* and where do they live? Need to trace into

pairing recommendation scoring to know how strong a "dislike" signal

actually is in practice.

- Does =CustomerFoodFeedback= feed back into the soft dislike signals

on subsequent recommendations, or is it purely operational/CX data?

- For UX: only ingredient dislikes hard-block products. If a customer

expects "I disliked Italian" to *exclude* Italian dishes, that

expectation is not met today. Worth flagging.