Add properties_consumed_fraction to Ingredient for partially consumed ingredients#4739
Open
dbriscoe511 wants to merge 1 commit into
Conversation
… 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.
e47f845 to
b536bf5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Nutrition sums
amount × food propertiesover 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:
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: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_consumedneeds cross-field validation and itsown 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 itships: loosening a validator later is backward compatible, tightening one is not.
Backward compatibility
default=1→ existing recipes compute bit-identical nutrition (pre-existing propertytests pass unchanged); column add is metadata-only on Postgres 11+.
ignore the key, and old instances importing new exports silently drop it.
(deliberately not
CustomDecimalField, which would lose range validation).The patch (10 files, +115/−7)
cookbook/models.py+ hand-written migration0243(makemigrations --check: no drift)cookbook/helper/property_helper.py— contribution × fraction (all-Decimal)cookbook/serializer.py— field inIngredientSimple/Ingredient/IngredientExportserializers (the last so .zip exports round-trip it)
cookbook/tests/other/test_food_property.py— fraction 0.25 and 0 casesvue3/.../StepEditor.vue+en.json— percent input in the desktop "⋮" menu andmobile editor sheet, mirroring
Disable_Amountvue3/src/openapi/models/*.ts— hand-edited per theno_amountpattern; happy toregenerate 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:
Ingredient editor — the "Counted for Properties" input in the per-ingredient "⋮" menu:
Calories breakdown — 3 cups of oil contributes its absorbed share, 361 kcal of 2,758
total (would be ~8,178 kcal without the fraction):
Verification
105 tests pass (property + ingredient/recipe/step/shopping API suites, sqlite);
makemigrations --checkclean; vite build succeeds.Demo recipe created and verified:
export_2026-07-08.zip