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
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue