mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 13:35:23 +02:00
feat: Query Filter Builder for Cookbooks and Meal Plans (#4346)
This commit is contained in:
parent
2a9a6fa5e6
commit
b8e62ab8dd
47 changed files with 2043 additions and 440 deletions
|
@ -3,6 +3,19 @@ from collections.abc import Generator
|
|||
|
||||
from pytest import MonkeyPatch, fixture
|
||||
|
||||
|
||||
def _clean_temp_dir():
|
||||
with contextlib.suppress(Exception):
|
||||
temp_dir = Path(__file__).parent / ".temp"
|
||||
|
||||
if temp_dir.exists():
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
|
||||
_clean_temp_dir()
|
||||
|
||||
mp = MonkeyPatch()
|
||||
mp.setenv("PRODUCTION", "True")
|
||||
mp.setenv("TESTING", "True")
|
||||
|
@ -54,11 +67,6 @@ def test_image_png():
|
|||
@fixture(scope="session", autouse=True)
|
||||
def global_cleanup() -> Generator[None, None, None]:
|
||||
"""Purges the .temp directory used for testing"""
|
||||
|
||||
yield None
|
||||
with contextlib.suppress(Exception):
|
||||
temp_dir = Path(__file__).parent / ".temp"
|
||||
|
||||
if temp_dir.exists():
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
_clean_temp_dir()
|
||||
|
|
|
@ -178,7 +178,7 @@ def test_get_cookbooks_with_recipes(api_client: TestClient, unique_user: TestUse
|
|||
|
||||
database.recipes.update_many([public_recipe, private_recipe])
|
||||
|
||||
# Create a recipe in another household that's public with the same known tag
|
||||
# Create a public and private recipe with a known tag in another household
|
||||
other_database = h2_user.repos
|
||||
other_household = other_database.households.get_one(h2_user.household_id)
|
||||
assert other_household and other_household.preferences
|
||||
|
@ -187,17 +187,24 @@ def test_get_cookbooks_with_recipes(api_client: TestClient, unique_user: TestUse
|
|||
other_household.preferences.recipe_public = True
|
||||
other_database.household_preferences.update(household.id, household.preferences)
|
||||
|
||||
other_household_recipe = other_database.recipes.create(
|
||||
other_household_public_recipe, other_household_private_recipe = database.recipes.create_many(
|
||||
Recipe(
|
||||
user_id=h2_user.user_id,
|
||||
group_id=h2_user.group_id,
|
||||
name=random_string(),
|
||||
)
|
||||
for _ in range(2)
|
||||
)
|
||||
assert other_household_recipe.settings
|
||||
other_household_recipe.settings.public = True
|
||||
other_household_recipe.tags = [tag]
|
||||
other_database.recipes.update(other_household_recipe.slug, other_household_recipe)
|
||||
|
||||
assert other_household_public_recipe.settings
|
||||
other_household_public_recipe.settings.public = True
|
||||
other_household_public_recipe.tags = [tag]
|
||||
|
||||
assert other_household_private_recipe.settings
|
||||
other_household_private_recipe.settings.public = False
|
||||
other_household_private_recipe.tags = [tag]
|
||||
|
||||
other_database.recipes.update_many([other_household_public_recipe, other_household_private_recipe])
|
||||
|
||||
# Create a public cookbook with tag
|
||||
cookbook = database.cookbooks.create(
|
||||
|
@ -206,11 +213,91 @@ def test_get_cookbooks_with_recipes(api_client: TestClient, unique_user: TestUse
|
|||
group_id=unique_user.group_id,
|
||||
household_id=unique_user.household_id,
|
||||
public=True,
|
||||
tags=[tag],
|
||||
query_filter_string=f'tags.id IN ["{tag.id}"]',
|
||||
)
|
||||
)
|
||||
|
||||
# Get the cookbook and make sure we only get the public recipe from the correct household
|
||||
# Get the cookbook and make sure we only get the public recipes from each household
|
||||
response = api_client.get(api_routes.explore_groups_group_slug_cookbooks_item_id(unique_user.group_id, cookbook.id))
|
||||
assert response.status_code == 200
|
||||
cookbook_data = response.json()
|
||||
assert cookbook_data["id"] == str(cookbook.id)
|
||||
|
||||
cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in cookbook_data["recipes"]}
|
||||
assert len(cookbook_recipe_ids) == 2
|
||||
assert str(public_recipe.id) in cookbook_recipe_ids
|
||||
assert str(private_recipe.id) not in cookbook_recipe_ids
|
||||
assert str(other_household_public_recipe.id) in cookbook_recipe_ids
|
||||
assert str(other_household_private_recipe.id) not in cookbook_recipe_ids
|
||||
|
||||
|
||||
def test_get_cookbooks_private_household(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
|
||||
database = unique_user.repos
|
||||
|
||||
# Create a public recipe with a known tag
|
||||
group = database.groups.get_one(unique_user.group_id)
|
||||
assert group and group.preferences
|
||||
|
||||
group.preferences.private_group = False
|
||||
database.group_preferences.update(group.id, group.preferences)
|
||||
|
||||
household = database.households.get_one(unique_user.household_id)
|
||||
assert household and household.preferences
|
||||
|
||||
household.preferences.private_household = False
|
||||
household.preferences.recipe_public = True
|
||||
database.household_preferences.update(household.id, household.preferences)
|
||||
|
||||
tag = database.tags.create(TagSave(name=random_string(), group_id=unique_user.group_id))
|
||||
public_recipe = database.recipes.create(
|
||||
Recipe(
|
||||
user_id=unique_user.user_id,
|
||||
group_id=unique_user.group_id,
|
||||
name=random_string(),
|
||||
)
|
||||
)
|
||||
|
||||
assert public_recipe.settings
|
||||
public_recipe.settings.public = True
|
||||
public_recipe.tags = [tag]
|
||||
|
||||
database.recipes.update(public_recipe.slug, public_recipe)
|
||||
|
||||
# Create a public recipe with a known tag on a private household
|
||||
other_database = h2_user.repos
|
||||
other_household = other_database.households.get_one(h2_user.household_id)
|
||||
assert other_household and other_household.preferences
|
||||
|
||||
other_household.preferences.private_household = True
|
||||
other_household.preferences.recipe_public = True
|
||||
other_database.household_preferences.update(household.id, household.preferences)
|
||||
|
||||
other_household_private_recipe = database.recipes.create(
|
||||
Recipe(
|
||||
user_id=h2_user.user_id,
|
||||
group_id=h2_user.group_id,
|
||||
name=random_string(),
|
||||
)
|
||||
)
|
||||
|
||||
assert other_household_private_recipe.settings
|
||||
other_household_private_recipe.settings.public = False
|
||||
other_household_private_recipe.tags = [tag]
|
||||
|
||||
other_database.recipes.update(other_household_private_recipe.slug, other_household_private_recipe)
|
||||
|
||||
# Create a public cookbook with tag
|
||||
cookbook = database.cookbooks.create(
|
||||
SaveCookBook(
|
||||
name=random_string(),
|
||||
group_id=unique_user.group_id,
|
||||
household_id=unique_user.household_id,
|
||||
public=True,
|
||||
query_filter_string=f'tags.id IN ["{tag.id}"]',
|
||||
)
|
||||
)
|
||||
|
||||
# Get the cookbook and make sure we only get the public recipes from each household
|
||||
response = api_client.get(api_routes.explore_groups_group_slug_cookbooks_item_id(unique_user.group_id, cookbook.id))
|
||||
assert response.status_code == 200
|
||||
cookbook_data = response.json()
|
||||
|
@ -219,5 +306,4 @@ def test_get_cookbooks_with_recipes(api_client: TestClient, unique_user: TestUse
|
|||
cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in cookbook_data["recipes"]}
|
||||
assert len(cookbook_recipe_ids) == 1
|
||||
assert str(public_recipe.id) in cookbook_recipe_ids
|
||||
assert str(private_recipe.id) not in cookbook_recipe_ids
|
||||
assert str(other_household_recipe.id) not in cookbook_recipe_ids
|
||||
assert str(other_household_private_recipe.id) not in cookbook_recipe_ids
|
||||
|
|
|
@ -244,8 +244,12 @@ def test_public_recipe_cookbook_filter(
|
|||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_public_recipe_cookbook_filter_with_recipes(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
|
||||
@pytest.mark.parametrize("other_household_private", [True, False])
|
||||
def test_public_recipe_cookbook_filter_with_recipes(
|
||||
api_client: TestClient, unique_user: TestUser, h2_user: TestUser, other_household_private: bool
|
||||
):
|
||||
database = unique_user.repos
|
||||
database.session.rollback()
|
||||
|
||||
# Create a public and private recipe with a known tag
|
||||
group = database.groups.get_one(unique_user.group_id)
|
||||
|
@ -281,14 +285,14 @@ def test_public_recipe_cookbook_filter_with_recipes(api_client: TestClient, uniq
|
|||
|
||||
database.recipes.update_many([public_recipe, private_recipe])
|
||||
|
||||
# Create a recipe in another household that's public with the same known tag
|
||||
# Create a recipe in another household with the same known tag
|
||||
other_database = h2_user.repos
|
||||
other_household = other_database.households.get_one(h2_user.household_id)
|
||||
assert other_household and other_household.preferences
|
||||
|
||||
other_household.preferences.private_household = False
|
||||
other_household.preferences.private_household = other_household_private
|
||||
other_household.preferences.recipe_public = True
|
||||
other_database.household_preferences.update(household.id, household.preferences)
|
||||
other_database.household_preferences.update(other_household.id, other_household.preferences)
|
||||
|
||||
other_household_recipe = other_database.recipes.create(
|
||||
Recipe(
|
||||
|
@ -309,17 +313,25 @@ def test_public_recipe_cookbook_filter_with_recipes(api_client: TestClient, uniq
|
|||
group_id=unique_user.group_id,
|
||||
household_id=unique_user.household_id,
|
||||
public=True,
|
||||
tags=[tag],
|
||||
query_filter_string=f'tags.id IN ["{tag.id}"]',
|
||||
)
|
||||
)
|
||||
|
||||
# Get the cookbook's recipes and make sure we only get the public recipe from the correct household
|
||||
# Get the cookbook's recipes and make sure we get both public recipes
|
||||
response = api_client.get(
|
||||
api_routes.explore_groups_group_slug_recipes(unique_user.group_id), params={"cookbook": cookbook.id}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
recipe_ids: set[str] = {recipe["id"] for recipe in response.json()["items"]}
|
||||
assert len(recipe_ids) == 1
|
||||
if other_household_private:
|
||||
assert len(recipe_ids) == 1
|
||||
else:
|
||||
assert len(recipe_ids) == 2
|
||||
|
||||
assert str(public_recipe.id) in recipe_ids
|
||||
assert str(private_recipe.id) not in recipe_ids
|
||||
assert str(other_household_recipe.id) not in recipe_ids
|
||||
|
||||
if other_household_private:
|
||||
assert str(other_household_recipe.id) not in recipe_ids
|
||||
else:
|
||||
assert str(other_household_recipe.id) in recipe_ids
|
||||
|
|
|
@ -21,7 +21,7 @@ def get_page_data(group_id: UUID | str, household_id: UUID4 | str):
|
|||
"slug": name_and_slug,
|
||||
"description": "",
|
||||
"position": 0,
|
||||
"categories": [],
|
||||
"query_filter_string": "",
|
||||
"group_id": str(group_id),
|
||||
"household_id": str(household_id),
|
||||
}
|
||||
|
@ -143,3 +143,42 @@ def test_delete_cookbook(api_client: TestClient, unique_user: TestUser, cookbook
|
|||
|
||||
response = api_client.get(api_routes.households_cookbooks_item_id(sample.slug), headers=unique_user.token)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"qf_string, expected_code",
|
||||
[
|
||||
('tags.name CONTAINS ALL ["tag1","tag2"]', 200),
|
||||
('badfield = "badvalue"', 422),
|
||||
('recipe_category.id IN ["1"]', 422),
|
||||
('created_at >= "not-a-date"', 422),
|
||||
],
|
||||
ids=[
|
||||
"valid qf",
|
||||
"invalid field",
|
||||
"invalid UUID",
|
||||
"invalid date",
|
||||
],
|
||||
)
|
||||
def test_cookbook_validate_query_filter_string(
|
||||
api_client: TestClient, unique_user: TestUser, qf_string: str, expected_code: int
|
||||
):
|
||||
# Create
|
||||
cb_data = {"name": random_string(10), "slug": random_string(10), "query_filter_string": qf_string}
|
||||
response = api_client.post(api_routes.households_cookbooks, json=cb_data, headers=unique_user.token)
|
||||
assert response.status_code == expected_code if expected_code != 200 else 201
|
||||
|
||||
# Update
|
||||
cb_data = {"name": random_string(10), "slug": random_string(10), "query_filter_string": ""}
|
||||
response = api_client.post(api_routes.households_cookbooks, json=cb_data, headers=unique_user.token)
|
||||
assert response.status_code == 201
|
||||
cb_data = response.json()
|
||||
|
||||
cb_data["queryFilterString"] = qf_string
|
||||
response = api_client.put(
|
||||
api_routes.households_cookbooks_item_id(cb_data["id"]), json=cb_data, headers=unique_user.token
|
||||
)
|
||||
assert response.status_code == expected_code if expected_code != 201 else 200
|
||||
|
||||
# Out; should skip validation, so this should never error out
|
||||
ReadCookBook(**cb_data)
|
||||
|
|
|
@ -40,15 +40,22 @@ def create_rule(
|
|||
categories: list[CategoryOut] | None = None,
|
||||
households: list[HouseholdSummary] | None = None,
|
||||
):
|
||||
qf_parts: list[str] = []
|
||||
if tags:
|
||||
qf_parts.append(f'tags.id CONTAINS ALL [{",".join([str(tag.id) for tag in tags])}]')
|
||||
if categories:
|
||||
qf_parts.append(f'recipe_category.id CONTAINS ALL [{",".join([str(cat.id) for cat in categories])}]')
|
||||
if households:
|
||||
qf_parts.append(f'household_id IN [{",".join([str(household.id) for household in households])}]')
|
||||
|
||||
query_filter_string = " AND ".join(qf_parts)
|
||||
return unique_user.repos.group_meal_plan_rules.create(
|
||||
PlanRulesSave(
|
||||
group_id=UUID(unique_user.group_id),
|
||||
household_id=UUID(unique_user.household_id),
|
||||
day=day,
|
||||
entry_type=entry_type,
|
||||
tags=tags or [],
|
||||
categories=categories or [],
|
||||
households=households or [],
|
||||
query_filter_string=query_filter_string,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from mealie.schema.recipe.recipe import RecipeCategory
|
|||
from mealie.schema.recipe.recipe_category import CategorySave
|
||||
from tests import utils
|
||||
from tests.utils import api_routes
|
||||
from tests.utils.factories import random_string
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
||||
|
@ -32,7 +33,7 @@ def plan_rule(api_client: TestClient, unique_user: TestUser):
|
|||
"householdId": unique_user.household_id,
|
||||
"day": "monday",
|
||||
"entryType": "breakfast",
|
||||
"categories": [],
|
||||
"queryFilterString": "",
|
||||
}
|
||||
|
||||
response = api_client.post(
|
||||
|
@ -48,12 +49,13 @@ def plan_rule(api_client: TestClient, unique_user: TestUser):
|
|||
|
||||
def test_group_mealplan_rules_create(api_client: TestClient, unique_user: TestUser, category: RecipeCategory):
|
||||
database = unique_user.repos
|
||||
query_filter_string = f'recipe_category.id IN ["{category.id}"]'
|
||||
payload = {
|
||||
"groupId": unique_user.group_id,
|
||||
"householdId": unique_user.household_id,
|
||||
"day": "monday",
|
||||
"entryType": "breakfast",
|
||||
"categories": [category.model_dump()],
|
||||
"queryFilterString": query_filter_string,
|
||||
}
|
||||
|
||||
response = api_client.post(
|
||||
|
@ -67,8 +69,8 @@ def test_group_mealplan_rules_create(api_client: TestClient, unique_user: TestUs
|
|||
assert response_data["householdId"] == str(unique_user.household_id)
|
||||
assert response_data["day"] == "monday"
|
||||
assert response_data["entryType"] == "breakfast"
|
||||
assert len(response_data["categories"]) == 1
|
||||
assert response_data["categories"][0]["slug"] == category.slug
|
||||
assert len(response_data["queryFilter"]["parts"]) == 1
|
||||
assert response_data["queryFilter"]["parts"][0]["value"] == [str(category.id)]
|
||||
|
||||
# Validate database entry
|
||||
rule = database.group_meal_plan_rules.get_one(UUID(response_data["id"]))
|
||||
|
@ -78,8 +80,7 @@ def test_group_mealplan_rules_create(api_client: TestClient, unique_user: TestUs
|
|||
assert str(rule.household_id) == unique_user.household_id
|
||||
assert rule.day == "monday"
|
||||
assert rule.entry_type == "breakfast"
|
||||
assert len(rule.categories) == 1
|
||||
assert rule.categories[0].slug == category.slug
|
||||
assert rule.query_filter_string == query_filter_string
|
||||
|
||||
# Cleanup
|
||||
database.group_meal_plan_rules.delete(rule.id)
|
||||
|
@ -96,7 +97,8 @@ def test_group_mealplan_rules_read(api_client: TestClient, unique_user: TestUser
|
|||
assert response_data["householdId"] == str(unique_user.household_id)
|
||||
assert response_data["day"] == "monday"
|
||||
assert response_data["entryType"] == "breakfast"
|
||||
assert len(response_data["categories"]) == 0
|
||||
assert response_data["queryFilterString"] == ""
|
||||
assert len(response_data["queryFilter"]["parts"]) == 0
|
||||
|
||||
|
||||
def test_group_mealplan_rules_update(api_client: TestClient, unique_user: TestUser, plan_rule: PlanRulesOut):
|
||||
|
@ -119,7 +121,8 @@ def test_group_mealplan_rules_update(api_client: TestClient, unique_user: TestUs
|
|||
assert response_data["householdId"] == str(unique_user.household_id)
|
||||
assert response_data["day"] == "tuesday"
|
||||
assert response_data["entryType"] == "lunch"
|
||||
assert len(response_data["categories"]) == 0
|
||||
assert response_data["queryFilterString"] == ""
|
||||
assert len(response_data["queryFilter"]["parts"]) == 0
|
||||
|
||||
|
||||
def test_group_mealplan_rules_delete(api_client: TestClient, unique_user: TestUser, plan_rule: PlanRulesOut):
|
||||
|
@ -131,3 +134,42 @@ def test_group_mealplan_rules_delete(api_client: TestClient, unique_user: TestUs
|
|||
|
||||
response = api_client.get(api_routes.households_mealplans_rules_item_id(plan_rule.id), headers=unique_user.token)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"qf_string, expected_code",
|
||||
[
|
||||
('tags.name CONTAINS ALL ["tag1","tag2"]', 200),
|
||||
('badfield = "badvalue"', 422),
|
||||
('recipe_category.id IN ["1"]', 422),
|
||||
('created_at >= "not-a-date"', 422),
|
||||
],
|
||||
ids=[
|
||||
"valid qf",
|
||||
"invalid field",
|
||||
"invalid UUID",
|
||||
"invalid date",
|
||||
],
|
||||
)
|
||||
def test_group_mealplan_rules_validate_query_filter_string(
|
||||
api_client: TestClient, unique_user: TestUser, qf_string: str, expected_code: int
|
||||
):
|
||||
# Create
|
||||
rule_data = {"name": random_string(10), "slug": random_string(10), "query_filter_string": qf_string}
|
||||
response = api_client.post(api_routes.households_mealplans_rules, json=rule_data, headers=unique_user.token)
|
||||
assert response.status_code == expected_code if expected_code != 200 else 201
|
||||
|
||||
# Update
|
||||
rule_data = {"name": random_string(10), "slug": random_string(10), "query_filter_string": ""}
|
||||
response = api_client.post(api_routes.households_mealplans_rules, json=rule_data, headers=unique_user.token)
|
||||
assert response.status_code == 201
|
||||
rule_data = response.json()
|
||||
|
||||
rule_data["queryFilterString"] = qf_string
|
||||
response = api_client.put(
|
||||
api_routes.households_mealplans_rules_item_id(rule_data["id"]), json=rule_data, headers=unique_user.token
|
||||
)
|
||||
assert response.status_code == expected_code if expected_code != 201 else 200
|
||||
|
||||
# Out; should skip validation, so this should never error out
|
||||
PlanRulesOut(**rule_data)
|
||||
|
|
|
@ -257,9 +257,7 @@ def test_user_can_update_last_made_on_other_household(
|
|||
assert new_last_made == now != old_last_made
|
||||
|
||||
|
||||
def test_cookbook_recipes_only_includes_current_households(
|
||||
api_client: TestClient, unique_user: TestUser, h2_user: TestUser
|
||||
):
|
||||
def test_cookbook_recipes_includes_all_households(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
|
||||
tag = unique_user.repos.tags.create(TagSave(name=random_string(), group_id=unique_user.group_id))
|
||||
recipes = unique_user.repos.recipes.create_many(
|
||||
[
|
||||
|
@ -300,4 +298,4 @@ def test_cookbook_recipes_only_includes_current_households(
|
|||
for recipe in recipes:
|
||||
assert recipe.id in fetched_recipe_ids
|
||||
for recipe in other_recipes:
|
||||
assert recipe.id not in fetched_recipe_ids
|
||||
assert recipe.id in fetched_recipe_ids
|
||||
|
|
|
@ -858,7 +858,7 @@ def test_get_cookbook_recipes(api_client: TestClient, unique_user: utils.TestUse
|
|||
name=random_string(),
|
||||
group_id=unique_user.group_id,
|
||||
household_id=unique_user.household_id,
|
||||
tags=[tag],
|
||||
query_filter_string=f'tags.id IN ["{tag.id}"]',
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
from mealie.schema.response.query_filter import (
|
||||
LogicalOperator,
|
||||
QueryFilterBuilder,
|
||||
QueryFilterJSON,
|
||||
QueryFilterJSONPart,
|
||||
RelationalKeyword,
|
||||
RelationalOperator,
|
||||
)
|
||||
|
||||
|
||||
def test_query_filter_builder_json():
|
||||
qf = (
|
||||
'(( (name = "my-recipe") AND is_active = TRUE) AND tags.name CONTAINS ALL ["tag1","tag2"]) '
|
||||
'OR (name="my-other-recipe" AND (count=1 OR count=2) )'
|
||||
)
|
||||
builder = QueryFilterBuilder(qf)
|
||||
assert builder.as_json_model() == QueryFilterJSON(
|
||||
parts=[
|
||||
QueryFilterJSONPart(
|
||||
left_parenthesis="(((",
|
||||
attribute_name="name",
|
||||
relational_operator=RelationalOperator.EQ,
|
||||
value="my-recipe",
|
||||
right_parenthesis=")",
|
||||
),
|
||||
QueryFilterJSONPart(
|
||||
logical_operator=LogicalOperator.AND,
|
||||
attribute_name="is_active",
|
||||
relational_operator=RelationalOperator.EQ,
|
||||
value="TRUE",
|
||||
right_parenthesis=")",
|
||||
),
|
||||
QueryFilterJSONPart(
|
||||
logical_operator=LogicalOperator.AND,
|
||||
attribute_name="tags.name",
|
||||
relational_operator=RelationalKeyword.CONTAINS_ALL,
|
||||
value=["tag1", "tag2"],
|
||||
right_parenthesis=")",
|
||||
),
|
||||
QueryFilterJSONPart(
|
||||
logical_operator=LogicalOperator.OR,
|
||||
left_parenthesis="(",
|
||||
attribute_name="name",
|
||||
relational_operator=RelationalOperator.EQ,
|
||||
value="my-other-recipe",
|
||||
),
|
||||
QueryFilterJSONPart(
|
||||
logical_operator=LogicalOperator.AND,
|
||||
left_parenthesis="(",
|
||||
attribute_name="count",
|
||||
relational_operator=RelationalOperator.EQ,
|
||||
value="1",
|
||||
),
|
||||
QueryFilterJSONPart(
|
||||
logical_operator=LogicalOperator.OR,
|
||||
attribute_name="count",
|
||||
relational_operator=RelationalOperator.EQ,
|
||||
value="2",
|
||||
right_parenthesis="))",
|
||||
),
|
||||
]
|
||||
)
|
|
@ -11,6 +11,8 @@ from mealie.core.config import get_app_settings
|
|||
from mealie.db.db_setup import session_context
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.household.cookbook import CookBook
|
||||
from mealie.db.models.household.mealplan import GroupMealPlanRules
|
||||
from mealie.db.models.household.shopping_list import ShoppingList
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
|
@ -124,6 +126,9 @@ def test_database_restore_data(backup_path: Path):
|
|||
foods = session.query(IngredientFoodModel).all()
|
||||
units = session.query(IngredientUnitModel).all()
|
||||
|
||||
cookbooks = session.query(CookBook).all()
|
||||
mealplan_rules = session.query(GroupMealPlanRules).all()
|
||||
|
||||
# 2023-02-14-20.45.41_5ab195a474eb_add_normalized_search_properties
|
||||
for recipe in recipes:
|
||||
if recipe.name:
|
||||
|
@ -174,5 +179,39 @@ def test_database_restore_data(backup_path: Path):
|
|||
user_ratings = [x.rating for x in user_to_recipes if x.rating]
|
||||
assert recipe.rating == (statistics.mean(user_ratings) if user_ratings else None)
|
||||
|
||||
# 2024-10-08-21.17.31_86054b40fd06_added_query_filter_string_to_cookbook_and_mealplan
|
||||
for cookbook in cookbooks:
|
||||
parts = []
|
||||
if cookbook.categories:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_categories else "IN"
|
||||
vals = ",".join([f'"{cat.id}"' for cat in cookbook.categories])
|
||||
parts.append(f"recipe_category.id {relop} [{vals}]")
|
||||
if cookbook.tags:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_tags else "IN"
|
||||
vals = ",".join([f'"{tag.id}"' for tag in cookbook.tags])
|
||||
parts.append(f"tags.id {relop} [{vals}]")
|
||||
if cookbook.tools:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_tools else "IN"
|
||||
vals = ",".join([f'"{tool.id}"' for tool in cookbook.tools])
|
||||
parts.append(f"tools.id {relop} [{vals}]")
|
||||
|
||||
expected_query_filter_string = " AND ".join(parts)
|
||||
assert cookbook.query_filter_string == expected_query_filter_string
|
||||
|
||||
for rule in mealplan_rules:
|
||||
parts = []
|
||||
if rule.categories:
|
||||
vals = ",".join([f'"{cat.id}"' for cat in rule.categories])
|
||||
parts.append(f"recipe_category.id CONTAINS ALL [{vals}]")
|
||||
if rule.tags:
|
||||
vals = ",".join([f'"{tag.id}"' for tag in rule.tags])
|
||||
parts.append(f"tags.id CONTAINS ALL [{vals}]")
|
||||
if rule.households:
|
||||
vals = ",".join([f'"{household.id}"' for household in rule.households])
|
||||
parts.append(f"household_id IN [{vals}]")
|
||||
|
||||
expected_query_filter_string = " AND ".join(parts)
|
||||
assert rule.query_filter_string == expected_query_filter_string
|
||||
|
||||
finally:
|
||||
backup_v2.restore(original_data_backup)
|
||||
|
|
|
@ -37,8 +37,6 @@ admin_users_unlock = "/api/admin/users/unlock"
|
|||
"""`/api/admin/users/unlock`"""
|
||||
app_about = "/api/app/about"
|
||||
"""`/api/app/about`"""
|
||||
app_about_oidc = "/api/app/about/oidc"
|
||||
"""`/api/app/about/oidc`"""
|
||||
app_about_startup_info = "/api/app/about/startup-info"
|
||||
"""`/api/app/about/startup-info`"""
|
||||
app_about_theme = "/api/app/about/theme"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue