Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ability to filter by nutritional values for meal plan rules #4859

Open
wants to merge 2 commits into
base: mealie-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/components/Domain/Household/GroupMealPlanRuleForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ export default defineComponent({
label: i18n.tc("general.date-updated"),
type: "date",
},
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have to be snake_case otherwise they end up being invalid on the frontend

nutrition.carbohydrate_content, etc.

When I try adding this to my meal plan rules, and I go to edit the rule again, it gets removed

name: "nutrition.calories",
label: i18n.tc("recipe.calories"),
type: "number",
},
{
name: "nutrition.carbohydrateContent",
label: i18n.tc("recipe.carbohydrate-content"),
type: "number",
},
{
name: "nutrition.proteinContent",
label: i18n.tc("recipe.protein-content"),
type: "number",
},
{
name: "nutrition.fatContent",
label: i18n.tc("recipe.fat-content"),
type: "number",
},

];

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,20 @@ def test_group_mealplan_rules_delete(api_client: TestClient, unique_user: TestUs
"qf_string, expected_code",
[
('tags.name CONTAINS ALL ["tag1","tag2"]', 200),
("nutrition.calories >= 100", 200),
("nutrition.fatContent <= 10", 200),
("nutrition.proteinContent >= 40", 200),
("nutrition.carbohydrateContent = 200", 200),
('badfield = "badvalue"', 422),
('recipe_category.id IN ["1"]', 422),
('created_at >= "not-a-date"', 422),
],
ids=[
"valid qf",
"valid calorie filter",
"valid fat filter",
"valid protein filter",
"valid carb filter",
"invalid field",
"invalid UUID",
"invalid date",
Expand Down
42 changes: 27 additions & 15 deletions tests/unit_tests/repository_tests/test_pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ def test_pagination_guides(unique_user: TestUser):
next_params: dict = dict(parse_qsl(urlsplit(random_page_of_results.next).query)) # type: ignore
assert int(next_params["page"]) == random_page + 1

prev_params: dict = dict(parse_qsl(urlsplit(random_page_of_results.previous).query)) # type: ignore
prev_params: dict = dict(
# type: ignore
parse_qsl(urlsplit(random_page_of_results.previous).query)
)
assert int(prev_params["page"]) == random_page - 1

source_params = camelize(query.model_dump())
Expand Down Expand Up @@ -532,7 +535,7 @@ def test_pagination_filter_datetimes(
# units are created in order with increasing createdAt values
units_repo, unit_1, unit_2, unit_3 = query_units

## GT
# GT
past_dt: datetime = unit_1.created_at - timedelta(seconds=1) # type: ignore
dt = past_dt.isoformat()
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>"{dt}"')
Expand Down Expand Up @@ -574,7 +577,7 @@ def test_pagination_filter_datetimes(
unit_ids = {unit.id for unit in unit_results}
assert len(unit_ids) == 0

## GTE
# GTE
past_dt = unit_1.created_at - timedelta(seconds=1) # type: ignore
dt = past_dt.isoformat()
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>="{dt}"')
Expand Down Expand Up @@ -937,7 +940,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
response = api_client.post(api_routes.households_mealplans, json=data, headers=unique_user.token)
assert response.status_code == 201

## Yesterday
# Yesterday
params = {
"page": 1,
"perPage": -1,
Expand Down Expand Up @@ -966,7 +969,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
assert mealplan_today.title in fetched_mealplan_titles
assert mealplan_tomorrow.title in fetched_mealplan_titles

## Today
# Today
params = {
"page": 1,
"perPage": -1,
Expand Down Expand Up @@ -995,7 +998,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
assert mealplan_today.title not in fetched_mealplan_titles
assert mealplan_tomorrow.title in fetched_mealplan_titles

## Tomorrow
# Tomorrow
params = {
"page": 1,
"perPage": -1,
Expand All @@ -1021,7 +1024,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):

assert len(response_json["items"]) == 0

## Day After Tomorrow
# Day After Tomorrow
params = {
"page": 1,
"perPage": -1,
Expand Down Expand Up @@ -1050,7 +1053,8 @@ def test_pagination_filter_booleans(query_units: tuple[RepositoryUnit, Ingredien
query = PaginationQuery(
page=1,
per_page=-1,
query_filter=f"useAbbreviation=true AND id IN [{', '.join([str(unit.id) for unit in query_units[1:]])}]",
query_filter=f"useAbbreviation=true AND id IN [{
', '.join([str(unit.id) for unit in query_units[1:]])}]",
)
unit_results = units_repo.page_all(query).items
assert len(unit_results) == 1
Expand All @@ -1061,7 +1065,8 @@ def test_pagination_filter_advanced(query_units: tuple[RepositoryUnit, Ingredien
units_repo, unit_1, unit_2, unit_3 = query_units

dt = str(unit_3.created_at.isoformat()) # type: ignore
qf = f'name="test unit 1" OR (useAbbreviation=f AND (name="{unit_2.name}" OR createdAt > "{dt}"))'
qf = f'name="test unit 1" OR (useAbbreviation=f AND (name="{
unit_2.name}" OR createdAt > "{dt}"))'
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
unit_results = units_repo.page_all(query).items

Expand All @@ -1070,7 +1075,8 @@ def test_pagination_filter_advanced(query_units: tuple[RepositoryUnit, Ingredien
assert unit_2.id in result_ids
assert unit_3.id not in result_ids

qf = f'(name LIKE %_1 OR name IN ["{unit_2.name}"]) AND createdAt IS NOT NONE'
qf = f'(name LIKE %_1 OR name IN ["{
unit_2.name}"]) AND createdAt IS NOT NONE'
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
unit_results = units_repo.page_all(query).items

Expand Down Expand Up @@ -1185,7 +1191,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):

repo = database.recipes

qf = f'recipeCategory.id IN ["{category_1.id}"] AND tools.id IN ["{tool_1.id}"]'
qf = f'recipeCategory.id IN ["{
category_1.id}"] AND tools.id IN ["{tool_1.id}"]'
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
recipe_results = repo.page_all(query).items
assert len(recipe_results) == 1
Expand All @@ -1198,7 +1205,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
assert recipe_ct0_tg2_tl2.id not in recipe_ids
assert recipe_ct12_tg12_tl2.id not in recipe_ids

qf = f'recipeCategory.id CONTAINS ALL ["{category_1.id}", "{category_2.id}"] AND tags.id IN ["{tag_1.id}"]'
qf = f'recipeCategory.id CONTAINS ALL ["{category_1.id}", "{
category_2.id}"] AND tags.id IN ["{tag_1.id}"]'
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
recipe_results = repo.page_all(query).items
assert len(recipe_results) == 1
Expand All @@ -1211,7 +1219,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
assert recipe_ct0_tg2_tl2.id not in recipe_ids
assert recipe_ct12_tg12_tl2.id in recipe_ids

qf = f'tags.id IN ["{tag_1.id}", "{tag_2.id}"] AND tools.id IN ["{tool_2.id}"]'
qf = f'tags.id IN ["{tag_1.id}", "{
tag_2.id}"] AND tools.id IN ["{tool_2.id}"]'
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
recipe_results = repo.page_all(query).items
assert len(recipe_results) == 2
Expand All @@ -1225,8 +1234,10 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
assert recipe_ct12_tg12_tl2.id in recipe_ids

qf = (
f'recipeCategory.id CONTAINS ALL ["{category_1.id}", "{category_2.id}"]'
f'AND tags.id IN ["{tag_1.id}", "{tag_2.id}"] AND tools.id IN ["{tool_1.id}", "{tool_2.id}"]'
f'recipeCategory.id CONTAINS ALL ["{
category_1.id}", "{category_2.id}"]'
f'AND tags.id IN ["{tag_1.id}", "{
tag_2.id}"] AND tools.id IN ["{tool_1.id}", "{tool_2.id}"]'
)
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
recipe_results = repo.page_all(query).items
Expand Down Expand Up @@ -1266,6 +1277,7 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
'group.preferences.badAttribute="test value"',
id="bad double nested attribute",
),
pytest.param('nutrition.calories="test"', id="comparing int to string"),
],
)
def test_malformed_query_filters(api_client: TestClient, unique_user: TestUser, qf: str):
Expand Down
Loading