mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
Add nutrition query filters and tests
This commit is contained in:
parent
932473d0ff
commit
5907dbf35d
3 changed files with 64 additions and 23 deletions
|
@ -148,6 +148,27 @@ export default defineComponent({
|
||||||
label: i18n.tc("general.date-updated"),
|
label: i18n.tc("general.date-updated"),
|
||||||
type: "date",
|
type: "date",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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 {
|
return {
|
||||||
|
|
|
@ -140,12 +140,20 @@ def test_group_mealplan_rules_delete(api_client: TestClient, unique_user: TestUs
|
||||||
"qf_string, expected_code",
|
"qf_string, expected_code",
|
||||||
[
|
[
|
||||||
('tags.name CONTAINS ALL ["tag1","tag2"]', 200),
|
('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),
|
('badfield = "badvalue"', 422),
|
||||||
('recipe_category.id IN ["1"]', 422),
|
('recipe_category.id IN ["1"]', 422),
|
||||||
('created_at >= "not-a-date"', 422),
|
('created_at >= "not-a-date"', 422),
|
||||||
],
|
],
|
||||||
ids=[
|
ids=[
|
||||||
"valid qf",
|
"valid qf",
|
||||||
|
"valid calorie filter",
|
||||||
|
"valid fat filter",
|
||||||
|
"valid protein filter",
|
||||||
|
"valid carb filter",
|
||||||
"invalid field",
|
"invalid field",
|
||||||
"invalid UUID",
|
"invalid UUID",
|
||||||
"invalid date",
|
"invalid date",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import UTC, datetime, timedelta
|
||||||
from random import randint
|
from random import randint
|
||||||
from urllib.parse import parse_qsl, urlsplit
|
from urllib.parse import parse_qsl, urlsplit
|
||||||
|
|
||||||
|
@ -166,7 +166,10 @@ def test_pagination_guides(unique_user: TestUser):
|
||||||
next_params: dict = dict(parse_qsl(urlsplit(random_page_of_results.next).query)) # type: ignore
|
next_params: dict = dict(parse_qsl(urlsplit(random_page_of_results.next).query)) # type: ignore
|
||||||
assert int(next_params["page"]) == random_page + 1
|
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
|
assert int(prev_params["page"]) == random_page - 1
|
||||||
|
|
||||||
source_params = camelize(query.model_dump())
|
source_params = camelize(query.model_dump())
|
||||||
|
@ -238,7 +241,7 @@ def test_pagination_filter_null(unique_user: TestUser):
|
||||||
user_id=unique_user.user_id,
|
user_id=unique_user.user_id,
|
||||||
group_id=unique_user.group_id,
|
group_id=unique_user.group_id,
|
||||||
name=random_string(),
|
name=random_string(),
|
||||||
last_made=datetime.now(timezone.utc),
|
last_made=datetime.now(UTC),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -531,7 +534,7 @@ def test_pagination_filter_datetimes(
|
||||||
# units are created in order with increasing createdAt values
|
# units are created in order with increasing createdAt values
|
||||||
units_repo, unit_1, unit_2, unit_3 = query_units
|
units_repo, unit_1, unit_2, unit_3 = query_units
|
||||||
|
|
||||||
## GT
|
# GT
|
||||||
past_dt: datetime = unit_1.created_at - timedelta(seconds=1) # type: ignore
|
past_dt: datetime = unit_1.created_at - timedelta(seconds=1) # type: ignore
|
||||||
dt = past_dt.isoformat()
|
dt = past_dt.isoformat()
|
||||||
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>"{dt}"')
|
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>"{dt}"')
|
||||||
|
@ -573,7 +576,7 @@ def test_pagination_filter_datetimes(
|
||||||
unit_ids = {unit.id for unit in unit_results}
|
unit_ids = {unit.id for unit in unit_results}
|
||||||
assert len(unit_ids) == 0
|
assert len(unit_ids) == 0
|
||||||
|
|
||||||
## GTE
|
# GTE
|
||||||
past_dt = unit_1.created_at - timedelta(seconds=1) # type: ignore
|
past_dt = unit_1.created_at - timedelta(seconds=1) # type: ignore
|
||||||
dt = past_dt.isoformat()
|
dt = past_dt.isoformat()
|
||||||
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>="{dt}"')
|
query = PaginationQuery(page=1, per_page=-1, query_filter=f'createdAt>="{dt}"')
|
||||||
|
@ -626,7 +629,7 @@ def test_pagination_filter_datetimes(
|
||||||
)
|
)
|
||||||
def test_pagination_order_by_multiple(unique_user: TestUser, order_direction: OrderDirection):
|
def test_pagination_order_by_multiple(unique_user: TestUser, order_direction: OrderDirection):
|
||||||
database = unique_user.repos
|
database = unique_user.repos
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(UTC)
|
||||||
|
|
||||||
alphabet = ["a", "b", "c", "d", "e"]
|
alphabet = ["a", "b", "c", "d", "e"]
|
||||||
abbreviations = alphabet.copy()
|
abbreviations = alphabet.copy()
|
||||||
|
@ -687,7 +690,7 @@ def test_pagination_order_by_multiple_directions(
|
||||||
unique_user: TestUser, order_by_str: str, order_direction: OrderDirection
|
unique_user: TestUser, order_by_str: str, order_direction: OrderDirection
|
||||||
):
|
):
|
||||||
database = unique_user.repos
|
database = unique_user.repos
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(UTC)
|
||||||
|
|
||||||
alphabet = ["a", "b", "c", "d", "e"]
|
alphabet = ["a", "b", "c", "d", "e"]
|
||||||
abbreviations = alphabet.copy()
|
abbreviations = alphabet.copy()
|
||||||
|
@ -735,7 +738,7 @@ def test_pagination_order_by_multiple_directions(
|
||||||
)
|
)
|
||||||
def test_pagination_order_by_nested_model(unique_user: TestUser, order_direction: OrderDirection):
|
def test_pagination_order_by_nested_model(unique_user: TestUser, order_direction: OrderDirection):
|
||||||
database = unique_user.repos
|
database = unique_user.repos
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(UTC)
|
||||||
|
|
||||||
alphabet = ["a", "b", "c", "d", "e"]
|
alphabet = ["a", "b", "c", "d", "e"]
|
||||||
labels = database.group_multi_purpose_labels.create_many(
|
labels = database.group_multi_purpose_labels.create_many(
|
||||||
|
@ -766,7 +769,7 @@ def test_pagination_order_by_nested_model(unique_user: TestUser, order_direction
|
||||||
|
|
||||||
def test_pagination_order_by_doesnt_filter(unique_user: TestUser):
|
def test_pagination_order_by_doesnt_filter(unique_user: TestUser):
|
||||||
database = unique_user.repos
|
database = unique_user.repos
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(UTC)
|
||||||
|
|
||||||
label = database.group_multi_purpose_labels.create(
|
label = database.group_multi_purpose_labels.create(
|
||||||
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
|
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
|
||||||
|
@ -810,7 +813,7 @@ def test_pagination_order_by_nulls(
|
||||||
unique_user: TestUser, null_position: OrderByNullPosition, order_direction: OrderDirection
|
unique_user: TestUser, null_position: OrderByNullPosition, order_direction: OrderDirection
|
||||||
):
|
):
|
||||||
database = unique_user.repos
|
database = unique_user.repos
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(UTC)
|
||||||
|
|
||||||
label = database.group_multi_purpose_labels.create(
|
label = database.group_multi_purpose_labels.create(
|
||||||
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
|
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
|
||||||
|
@ -916,7 +919,7 @@ def test_pagination_shopping_list_items_with_labels(unique_user: TestUser):
|
||||||
|
|
||||||
|
|
||||||
def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
|
def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
|
||||||
today = datetime.now(timezone.utc).date()
|
today = datetime.now(UTC).date()
|
||||||
|
|
||||||
yesterday = today - timedelta(days=1)
|
yesterday = today - timedelta(days=1)
|
||||||
tomorrow = today + timedelta(days=1)
|
tomorrow = today + timedelta(days=1)
|
||||||
|
@ -936,7 +939,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)
|
response = api_client.post(api_routes.households_mealplans, json=data, headers=unique_user.token)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
|
|
||||||
## Yesterday
|
# Yesterday
|
||||||
params = {
|
params = {
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"perPage": -1,
|
"perPage": -1,
|
||||||
|
@ -965,7 +968,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
|
||||||
assert mealplan_today.title in fetched_mealplan_titles
|
assert mealplan_today.title in fetched_mealplan_titles
|
||||||
assert mealplan_tomorrow.title in fetched_mealplan_titles
|
assert mealplan_tomorrow.title in fetched_mealplan_titles
|
||||||
|
|
||||||
## Today
|
# Today
|
||||||
params = {
|
params = {
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"perPage": -1,
|
"perPage": -1,
|
||||||
|
@ -994,7 +997,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
|
||||||
assert mealplan_today.title not in fetched_mealplan_titles
|
assert mealplan_today.title not in fetched_mealplan_titles
|
||||||
assert mealplan_tomorrow.title in fetched_mealplan_titles
|
assert mealplan_tomorrow.title in fetched_mealplan_titles
|
||||||
|
|
||||||
## Tomorrow
|
# Tomorrow
|
||||||
params = {
|
params = {
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"perPage": -1,
|
"perPage": -1,
|
||||||
|
@ -1020,7 +1023,7 @@ def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
|
||||||
|
|
||||||
assert len(response_json["items"]) == 0
|
assert len(response_json["items"]) == 0
|
||||||
|
|
||||||
## Day After Tomorrow
|
# Day After Tomorrow
|
||||||
params = {
|
params = {
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"perPage": -1,
|
"perPage": -1,
|
||||||
|
@ -1049,7 +1052,8 @@ def test_pagination_filter_booleans(query_units: tuple[RepositoryUnit, Ingredien
|
||||||
query = PaginationQuery(
|
query = PaginationQuery(
|
||||||
page=1,
|
page=1,
|
||||||
per_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
|
unit_results = units_repo.page_all(query).items
|
||||||
assert len(unit_results) == 1
|
assert len(unit_results) == 1
|
||||||
|
@ -1060,7 +1064,8 @@ def test_pagination_filter_advanced(query_units: tuple[RepositoryUnit, Ingredien
|
||||||
units_repo, unit_1, unit_2, unit_3 = query_units
|
units_repo, unit_1, unit_2, unit_3 = query_units
|
||||||
|
|
||||||
dt = str(unit_3.created_at.isoformat()) # type: ignore
|
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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
unit_results = units_repo.page_all(query).items
|
unit_results = units_repo.page_all(query).items
|
||||||
|
|
||||||
|
@ -1069,7 +1074,8 @@ def test_pagination_filter_advanced(query_units: tuple[RepositoryUnit, Ingredien
|
||||||
assert unit_2.id in result_ids
|
assert unit_2.id in result_ids
|
||||||
assert unit_3.id not 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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
unit_results = units_repo.page_all(query).items
|
unit_results = units_repo.page_all(query).items
|
||||||
|
|
||||||
|
@ -1184,7 +1190,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
|
||||||
|
|
||||||
repo = database.recipes
|
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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
recipe_results = repo.page_all(query).items
|
recipe_results = repo.page_all(query).items
|
||||||
assert len(recipe_results) == 1
|
assert len(recipe_results) == 1
|
||||||
|
@ -1197,7 +1204,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
|
||||||
assert recipe_ct0_tg2_tl2.id not in recipe_ids
|
assert recipe_ct0_tg2_tl2.id not in recipe_ids
|
||||||
assert recipe_ct12_tg12_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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
recipe_results = repo.page_all(query).items
|
recipe_results = repo.page_all(query).items
|
||||||
assert len(recipe_results) == 1
|
assert len(recipe_results) == 1
|
||||||
|
@ -1210,7 +1218,8 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
|
||||||
assert recipe_ct0_tg2_tl2.id not in recipe_ids
|
assert recipe_ct0_tg2_tl2.id not in recipe_ids
|
||||||
assert recipe_ct12_tg12_tl2.id 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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
recipe_results = repo.page_all(query).items
|
recipe_results = repo.page_all(query).items
|
||||||
assert len(recipe_results) == 2
|
assert len(recipe_results) == 2
|
||||||
|
@ -1224,8 +1233,10 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
|
||||||
assert recipe_ct12_tg12_tl2.id in recipe_ids
|
assert recipe_ct12_tg12_tl2.id in recipe_ids
|
||||||
|
|
||||||
qf = (
|
qf = (
|
||||||
f'recipeCategory.id CONTAINS ALL ["{category_1.id}", "{category_2.id}"]'
|
f'recipeCategory.id CONTAINS ALL ["{
|
||||||
f'AND tags.id IN ["{tag_1.id}", "{tag_2.id}"] AND tools.id IN ["{tool_1.id}", "{tool_2.id}"]'
|
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)
|
query = PaginationQuery(page=1, per_page=-1, query_filter=qf)
|
||||||
recipe_results = repo.page_all(query).items
|
recipe_results = repo.page_all(query).items
|
||||||
|
@ -1265,6 +1276,7 @@ def test_pagination_filter_advanced_frontend_sort(unique_user: TestUser):
|
||||||
'group.preferences.badAttribute="test value"',
|
'group.preferences.badAttribute="test value"',
|
||||||
id="bad double nested attribute",
|
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):
|
def test_malformed_query_filters(api_client: TestClient, unique_user: TestUser, qf: str):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue