mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-06 05:55:23 +02:00
feat: plural foods and units, and aliases (#2674)
* added plural names and alias tables to foods/units
* updated models to include plural names and aliases
* updated parser to include plural and aliases
* fixed migrations
* fixed recursive models
* added plural abbreviation to migration
* updated parser and display prop
* update displays to use plurals
* fix display edgecase and remove print
* added/updated display tests
* fixed model bug and added parser tests
* added tests for aliases
* added new plural options to data management page
* removed unique constraint
* made base dialog more customizable
* added alias management to food and unit data pages
* removed unused awaits
* 🧹
This commit is contained in:
parent
4b55b838ed
commit
d440d51ffe
17 changed files with 1181 additions and 104 deletions
|
@ -0,0 +1,202 @@
|
|||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit, RecipeIngredient
|
||||
from tests.utils.factories import random_string
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["quantity", "quantity_display_decimal", "quantity_display_fraction", "expect_plural_unit", "expect_plural_food"],
|
||||
[
|
||||
[0, "", "", False, True],
|
||||
[0.5, "0.5", "¹/₂", False, False],
|
||||
[1, "1", "1", False, False],
|
||||
[1.5, "1.5", "1 ¹/₂", True, True],
|
||||
[2, "2", "2", True, True],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
["unit", "expect_display_fraction", "expected_unit_singular_string", "expected_unit_plural_string"],
|
||||
[
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name=None,
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation=None,
|
||||
use_abbreviation=False,
|
||||
fraction=True,
|
||||
),
|
||||
True,
|
||||
"tablespoon",
|
||||
"tablespoon",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name=None,
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation=None,
|
||||
use_abbreviation=False,
|
||||
fraction=False,
|
||||
),
|
||||
False,
|
||||
"tablespoon",
|
||||
"tablespoon",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name=None,
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation=None,
|
||||
use_abbreviation=True,
|
||||
fraction=True,
|
||||
),
|
||||
True,
|
||||
"tbsp",
|
||||
"tbsp",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name=None,
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation=None,
|
||||
use_abbreviation=True,
|
||||
fraction=False,
|
||||
),
|
||||
False,
|
||||
"tbsp",
|
||||
"tbsp",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name="tablespoons",
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation="tbsps",
|
||||
use_abbreviation=False,
|
||||
fraction=True,
|
||||
),
|
||||
True,
|
||||
"tablespoon",
|
||||
"tablespoons",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name="tablespoons",
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation="tbsps",
|
||||
use_abbreviation=False,
|
||||
fraction=False,
|
||||
),
|
||||
False,
|
||||
"tablespoon",
|
||||
"tablespoons",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name="tablespoons",
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation="tbsps",
|
||||
use_abbreviation=True,
|
||||
fraction=True,
|
||||
),
|
||||
True,
|
||||
"tbsp",
|
||||
"tbsps",
|
||||
],
|
||||
[
|
||||
IngredientUnit(
|
||||
id=uuid4(),
|
||||
name="tablespoon",
|
||||
plural_name="tablespoons",
|
||||
abbreviation="tbsp",
|
||||
plural_abbreviation="tbsps",
|
||||
use_abbreviation=True,
|
||||
fraction=False,
|
||||
),
|
||||
False,
|
||||
"tbsp",
|
||||
"tbsps",
|
||||
],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
["food", "expected_food_singular_string", "expected_food_plural_string"],
|
||||
[
|
||||
[
|
||||
IngredientFood(id=uuid4(), name="chopped onion", plural_name=None),
|
||||
"chopped onion",
|
||||
"chopped onion",
|
||||
],
|
||||
[
|
||||
IngredientFood(id=uuid4(), name="chopped onion", plural_name="chopped onions"),
|
||||
"chopped onion",
|
||||
"chopped onions",
|
||||
],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("note", ["very thin", ""])
|
||||
@pytest.mark.parametrize("use_food", [True, False])
|
||||
def test_ingredient_display(
|
||||
quantity: float | None,
|
||||
quantity_display_decimal: str,
|
||||
quantity_display_fraction: str,
|
||||
unit: IngredientUnit,
|
||||
food: IngredientFood,
|
||||
note: str,
|
||||
use_food: bool,
|
||||
expect_display_fraction: bool,
|
||||
expect_plural_unit: bool,
|
||||
expect_plural_food: bool,
|
||||
expected_unit_singular_string: str,
|
||||
expected_unit_plural_string: str,
|
||||
expected_food_singular_string: str,
|
||||
expected_food_plural_string: str,
|
||||
):
|
||||
expected_components = []
|
||||
if use_food:
|
||||
if expect_display_fraction:
|
||||
expected_components.append(quantity_display_fraction)
|
||||
else:
|
||||
expected_components.append(quantity_display_decimal)
|
||||
|
||||
if quantity:
|
||||
if expect_plural_unit:
|
||||
expected_components.append(expected_unit_plural_string)
|
||||
else:
|
||||
expected_components.append(expected_unit_singular_string)
|
||||
|
||||
if expect_plural_food:
|
||||
expected_components.append(expected_food_plural_string)
|
||||
else:
|
||||
expected_components.append(expected_food_singular_string)
|
||||
|
||||
expected_components.append(note)
|
||||
|
||||
else:
|
||||
if quantity != 0 and quantity != 1:
|
||||
if expect_display_fraction:
|
||||
expected_components.append(quantity_display_fraction)
|
||||
else:
|
||||
expected_components.append(quantity_display_decimal)
|
||||
|
||||
expected_components.append(note)
|
||||
|
||||
expected_display_value = " ".join(c for c in expected_components if c)
|
||||
ingredient = RecipeIngredient(
|
||||
quantity=quantity, unit=unit, food=food, note=note, use_food=use_food, disable_amount=not use_food
|
||||
)
|
||||
assert ingredient.display == expected_display_value
|
|
@ -9,7 +9,9 @@ from mealie.db.db_setup import session_context
|
|||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.schema.recipe.recipe_ingredient import (
|
||||
CreateIngredientFood,
|
||||
CreateIngredientFoodAlias,
|
||||
CreateIngredientUnit,
|
||||
CreateIngredientUnitAlias,
|
||||
IngredientFood,
|
||||
IngredientUnit,
|
||||
ParsedIngredient,
|
||||
|
@ -67,6 +69,12 @@ def parsed_ingredient_data(
|
|||
SaveIngredientFood(name="fresh ginger", group_id=unique_local_group_id),
|
||||
SaveIngredientFood(name="ground ginger", group_id=unique_local_group_id),
|
||||
SaveIngredientFood(name="ñör̃m̈ãl̈ĩz̈ẽm̈ẽ", group_id=unique_local_group_id),
|
||||
SaveIngredientFood(name="PluralFoodTest", plural_name="myfoodisplural", group_id=unique_local_group_id),
|
||||
SaveIngredientFood(
|
||||
name="IHaveAnAlias",
|
||||
group_id=unique_local_group_id,
|
||||
aliases=[CreateIngredientFoodAlias(name="thisismyalias")],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -86,6 +94,18 @@ def parsed_ingredient_data(
|
|||
SaveIngredientUnit(name="Teaspoon", group_id=unique_local_group_id),
|
||||
SaveIngredientUnit(name="Stalk", group_id=unique_local_group_id),
|
||||
SaveIngredientUnit(name="My Very Long Unit Name", abbreviation="mvlun", group_id=unique_local_group_id),
|
||||
SaveIngredientUnit(
|
||||
name="PluralUnitName",
|
||||
plural_name="abc123",
|
||||
abbreviation="doremiabc",
|
||||
plural_abbreviation="doremi123",
|
||||
group_id=unique_local_group_id,
|
||||
),
|
||||
SaveIngredientUnit(
|
||||
name="IHaveAnAliasToo",
|
||||
group_id=unique_local_group_id,
|
||||
aliases=[CreateIngredientUnitAlias(name="thisismyalias")],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -267,6 +287,46 @@ def test_brute_parser(unique_user: TestUser):
|
|||
True,
|
||||
id="normalization",
|
||||
),
|
||||
pytest.param(
|
||||
build_parsed_ing(unit=None, food="myfoodisplural"),
|
||||
None,
|
||||
"PluralFoodTest",
|
||||
False,
|
||||
True,
|
||||
id="plural food name",
|
||||
),
|
||||
pytest.param(
|
||||
build_parsed_ing(unit="abc123", food=None),
|
||||
"PluralUnitName",
|
||||
None,
|
||||
True,
|
||||
False,
|
||||
id="plural unit name",
|
||||
),
|
||||
pytest.param(
|
||||
build_parsed_ing(unit="doremi123", food=None),
|
||||
"PluralUnitName",
|
||||
None,
|
||||
True,
|
||||
False,
|
||||
id="plural unit abbreviation",
|
||||
),
|
||||
pytest.param(
|
||||
build_parsed_ing(unit=None, food="thisismyalias"),
|
||||
None,
|
||||
"IHaveAnAlias",
|
||||
False,
|
||||
True,
|
||||
id="food alias",
|
||||
),
|
||||
pytest.param(
|
||||
build_parsed_ing(unit="thisismyalias", food=None),
|
||||
"IHaveAnAliasToo",
|
||||
None,
|
||||
True,
|
||||
False,
|
||||
id="unit alias",
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_parser_ingredient_match(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue