mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-24 08:43:11 -05:00
fix: Include unmade recipes when filtering by last made (#7130)
This commit is contained in:
@@ -79,8 +79,8 @@ This filter will find all foods that are not named "carrot": <br>
|
||||
##### Keyword Filters
|
||||
The API supports many SQL keywords, such as `IS NULL` and `IN`, as well as their negations (e.g. `IS NOT NULL` and `NOT IN`).
|
||||
|
||||
Here is an example of a filter that returns all recipes where the "last made" value is not null: <br>
|
||||
`lastMade IS NOT NULL`
|
||||
Here is an example of a filter that returns all shopping list items without a food: <br>
|
||||
`foodId IS NULL`
|
||||
|
||||
This filter will find all recipes that don't start with the word "Test": <br>
|
||||
`name NOT LIKE "Test%"`
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import re as re
|
||||
from collections.abc import Iterable, Sequence
|
||||
from datetime import UTC, datetime
|
||||
from random import randint
|
||||
from typing import Self, cast
|
||||
from uuid import UUID
|
||||
@@ -51,10 +52,13 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]):
|
||||
return self
|
||||
|
||||
def _get_last_made_col_alias(self) -> sa.ColumnElement | None:
|
||||
"""Computed last_made which uses `HouseholdToRecipe.last_made` for the user's household, otherwise None"""
|
||||
"""
|
||||
Computed last_made which uses `HouseholdToRecipe.last_made` for the user's household,
|
||||
otherwise an arbitrarily low date
|
||||
"""
|
||||
|
||||
user_household_subquery = sa.select(User.household_id).where(User.id == self.user_id).scalar_subquery()
|
||||
return (
|
||||
last_made_subquery = (
|
||||
sa.select(HouseholdToRecipe.last_made)
|
||||
.where(
|
||||
HouseholdToRecipe.recipe_id == self.model.id,
|
||||
@@ -63,6 +67,7 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]):
|
||||
.correlate(self.model)
|
||||
.scalar_subquery()
|
||||
)
|
||||
return sa.func.coalesce(last_made_subquery, datetime(year=1900, month=1, day=1, tzinfo=UTC))
|
||||
|
||||
def _get_rating_col_alias(self) -> sa.ColumnElement | None:
|
||||
"""Computed rating which uses the user's rating if it exists, otherwise falling back to the recipe's rating"""
|
||||
|
||||
@@ -647,6 +647,47 @@ def test_order_by_last_made(unique_user: TestUser, h2_user: TestUser):
|
||||
assert [item.id for item in h2_query.items] == [recipe_2.id, recipe_1.id]
|
||||
|
||||
|
||||
def test_coalesce_last_made(unique_user: TestUser):
|
||||
dt = datetime.now(UTC)
|
||||
|
||||
made_recipe, unmade_recipe = (
|
||||
unique_user.repos.recipes.create(
|
||||
Recipe(user_id=unique_user.user_id, group_id=unique_user.group_id, name=random_string())
|
||||
)
|
||||
for _ in range(2)
|
||||
)
|
||||
unique_user.repos.household_recipes.create(
|
||||
HouseholdRecipeCreate(recipe_id=made_recipe.id, household_id=unique_user.household_id, last_made=dt)
|
||||
)
|
||||
|
||||
repos = get_repositories(
|
||||
unique_user.repos.session, group_id=unique_user.group_id, household_id=None
|
||||
).recipes.by_user(unique_user.user_id)
|
||||
r = repos.page_all(
|
||||
PaginationQuery(
|
||||
page=1,
|
||||
per_page=-1,
|
||||
order_by="last_made",
|
||||
order_direction=OrderDirection.asc,
|
||||
query_filter=f"id IN [{made_recipe.id}, {unmade_recipe.id}] AND lastMade <= {dt.isoformat()}",
|
||||
)
|
||||
)
|
||||
assert len(r.items) == 2
|
||||
assert [item.id for item in r.items] == [unmade_recipe.id, made_recipe.id]
|
||||
|
||||
r = repos.page_all(
|
||||
PaginationQuery(
|
||||
page=1,
|
||||
per_page=-1,
|
||||
order_by="last_made",
|
||||
order_direction=OrderDirection.desc,
|
||||
query_filter=f"id IN [{made_recipe.id}, {unmade_recipe.id}]",
|
||||
)
|
||||
)
|
||||
assert len(r.items) == 2
|
||||
assert [item.id for item in r.items] == [made_recipe.id, unmade_recipe.id]
|
||||
|
||||
|
||||
def test_order_by_rating(user_tuple: tuple[TestUser, TestUser]):
|
||||
user_1, user_2 = user_tuple
|
||||
database = user_1.repos
|
||||
|
||||
Reference in New Issue
Block a user