Store nutrition per serving, and check the estimate before sending it
Recipes imported from a video caption have no nutrition data, so the values have to be estimated. Mealie will accept anything: the fields are free-text strings, a misspelt key is dropped silently, and a whole-recipe total looks exactly like a per-serving one. The recipe page then renders whatever landed as fact. patch_recipe now validates a nutrition block first. Keys must be Mealie's own, so a number cannot vanish into "carbs". Values are normalized to bare numbers, matching how the library already stores them. Energy is checked against the macros with the Atwater factors (4/9/4 kcal per gram) and refused if it is more than 25% off, which is what catches an arithmetic slip. Nutrition is per serving, and the text import left recipeServings at 0 --- it set only the free-text recipeYield, so "4-6 personer" gave Mealie no number to divide by or scale with. import_recipe_text now also sets recipeServings, from the lower bound of a range, which is how this library already stores "10-12 personer" (recipeServings 10). patch_recipe refuses nutrition while the count is still missing, and accepts it when the same patch supplies it. verify_recipe fails an import that has no nutrition, or has values without a serving count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,9 @@ def complete_recipe(**overrides):
|
||||
"recipeCategory": [{"name": "Huvudrätter"}],
|
||||
"tags": [{"name": "Källa: Köket"}],
|
||||
"orgURL": "https://www.koket.se/kycklinggryta",
|
||||
"recipeServings": 4,
|
||||
"nutrition": {"calories": "520", "proteinContent": "31", "fatContent": "34",
|
||||
"carbohydrateContent": "18"},
|
||||
}
|
||||
recipe.update(overrides)
|
||||
return recipe
|
||||
@@ -130,6 +133,22 @@ class TestTaxonomy:
|
||||
assert report["complete"] is True
|
||||
|
||||
|
||||
class TestNutrition:
|
||||
def test_missing_nutrition_fails(self):
|
||||
report = verify_recipe(complete_recipe(nutrition={}), image_verified=True)
|
||||
assert status_of(report, "nutrition") == "fail"
|
||||
assert report["complete"] is False
|
||||
|
||||
def test_values_without_a_serving_count_fail(self):
|
||||
# Mealie shows nutrition per serving, so figures with recipeServings 0
|
||||
# are a numerator without a denominator — the state the TikTok import left.
|
||||
report = verify_recipe(complete_recipe(recipeServings=0), image_verified=True)
|
||||
assert status_of(report, "nutrition") == "fail"
|
||||
assert "recipeServings" in next(
|
||||
c["detail"] for c in report["checks"] if c["check"] == "nutrition"
|
||||
)
|
||||
|
||||
|
||||
class TestAttribution:
|
||||
def test_missing_source_warns_but_does_not_block(self):
|
||||
report = verify_recipe(
|
||||
|
||||
Reference in New Issue
Block a user