Skip to content

Add properties_consumed_fraction to Ingredient for partially consumed ingredients#4739

Open
dbriscoe511 wants to merge 1 commit into
TandoorRecipes:developfrom
dbriscoe511:feature/ingredient-properties-consumed-fraction
Open

Add properties_consumed_fraction to Ingredient for partially consumed ingredients#4739
dbriscoe511 wants to merge 1 commit into
TandoorRecipes:developfrom
dbriscoe511:feature/ingredient-properties-consumed-fraction

Conversation

@dbriscoe511

@dbriscoe511 dbriscoe511 commented Jul 8, 2026

Copy link
Copy Markdown

Problem

Nutrition sums amount × food properties over every ingredient
(cookbook/helper/property_helper.py). For mostly-discarded ingredients (frying oil,
marinades, brines, dredges) there is no way to express that only part of the listed
amount is actually eaten, which forces a choice between two bad options:

  1. List the working amount (3 cups oil) → nutrition is wildly inflated.
  2. List the consumed amount (3 Tbsp absorbed) → nutrition is right, but the ingredient
    list misstates what you actually need to buy and cook with, and recipe steps have to
    carry "3 cups" notes

Design

New optional field on Ingredient:

properties_consumed_fraction = models.DecimalField(
    default=1, decimal_places=4, max_digits=5,
    validators=[MinValueValidator(0), MaxValueValidator(1)])

The share of the listed amount that ends up in the served dish and counts for property
calculations.
Deep-frying becomes 3 cups oil, fraction 0.0625: display, steps,
scaling and shopping all see 3 cups; nutrition counts the absorbed ~3 Tbsp.

Why not the alternatives: a boolean exclude flag is subsumed by fraction 0 and can't say
"partially absorbed"; an absolute amount_consumed needs cross-field validation and its
own scaling logic, while a relative fraction survives recipe scaling for free.

Scope decisions: shopping lists stay on the full amount (you still buy 3 cups — a
feature, not an omission); the fraction applies to all property types ("counted if it
ends up in the dish"); warning semantics are untouched (fraction 0 with a missing
conversion still warns — short-circuiting is an easy follow-up if preferred).

Open question: drop the max=1 cap?

Values > 1 would turn the field into a general per-ingredient multiplier: correcting for
habitual over-pouring (recipe says 1 Tbsp oil, you pour 2), concentrated substitutions
(double-strength stock on a "1 cup stock" line), or doubling the cheese without changing
the displayed amounts. It costs nothing — delete two validators; calculation and scaling
already handle any positive value — but "consumed fraction of 180%" is nonsense, so the
field would want renaming (e.g. properties_factor). Feedback on this welcome before it
ships: loosening a validator later is backward compatible, tightening one is not.

Backward compatibility

  • default=1 → existing recipes compute bit-identical nutrition (pre-existing property
    tests pass unchanged); column add is metadata-only on Postgres 11+.
  • All ~20 importers and the ingredient parser never name the field → zero changes.
  • API change is purely additive; old clients that omit it get the default, old readers
    ignore the key, and old instances importing new exports silently drop it.
  • DRF auto-generates the serializer field as optional with the 0–1 validators
    (deliberately not CustomDecimalField, which would lose range validation).

The patch (10 files, +115/−7)

  • cookbook/models.py + hand-written migration 0243 (makemigrations --check: no drift)
  • cookbook/helper/property_helper.py — contribution × fraction (all-Decimal)
  • cookbook/serializer.py — field in IngredientSimple/Ingredient/IngredientExport
    serializers (the last so .zip exports round-trip it)
  • cookbook/tests/other/test_food_property.py — fraction 0.25 and 0 cases
  • vue3/.../StepEditor.vue + en.json — percent input in the desktop "⋮" menu and
    mobile editor sheet, mirroring Disable_Amount
  • vue3/src/openapi/models/*.ts — hand-edited per the no_amount pattern; happy to
    regenerate them with openapi-generator if preferred

Screenshots (local demo: 900 g chicken; buttermilk 20%, dredge 50%, oil 6.25% consumed)

Recipe view — ingredient lines keep the real working amounts:

image

Ingredient editor — the "Counted for Properties" input in the per-ingredient "⋮" menu:

image

Calories breakdown — 3 cups of oil contributes its absorbed share, 361 kcal of 2,758
total (would be ~8,178 kcal without the fraction):

image

Verification

105 tests pass (property + ingredient/recipe/step/shopping API suites, sqlite);
makemigrations --check clean; vite build succeeds.
Demo recipe created and verified:
export_2026-07-08.zip

@CLAassistant

CLAassistant commented Jul 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@dbriscoe511 dbriscoe511 changed the title Add properties_consumed_fraction to Ingredient for partially consumed… Add properties_consumed_fraction to Ingredient for partially consumed ingredients Jul 8, 2026
… ingredients

Ingredients like frying oil, marinades, rubs or brines are mostly discarded:
only a fraction of the listed amount ends up in the served dish. Property
(nutrition) calculations previously counted the full amount, forcing a choice
between wrong nutrition values and listing misleading amounts.

This adds an optional properties_consumed_fraction field (0-1, default 1) to
Ingredient. The property calculation multiplies each ingredient's contribution
by its fraction; everything else (display, scaling, shopping lists, imports,
exports) keeps using the full amount, which is the desired behavior since you
still shop for and cook with the working amount.

Backward compatible by design: the default of 1 leaves every existing recipe,
import path and API client unchanged, and the field is optional everywhere.

The ingredient edit UIs (desktop context menu and mobile editor sheet) expose
the fraction as a percentage. TS API client models updated by hand following
the no_amount pattern; a proper openapi-generator run should reproduce them.

Verified: cookbook/tests/other/test_food_property.py (extended with fraction
cases), test_api_ingredient, test_api_recipe, test_api_step and shopping
recipe suites all pass; makemigrations --check reports no drift; vite build
succeeds.
@dbriscoe511 dbriscoe511 force-pushed the feature/ingredient-properties-consumed-fraction branch from e47f845 to b536bf5 Compare July 8, 2026 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants