feat: added "cookbook" filter to recipe pagination to serve frontend (#1609)
* added cookbook filter to recipe pagination * fixed wrong filter var * restored cookbook sorting * reverted unnecessary var change
This commit is contained in:
parent
d26cb570ba
commit
2007bcfe28
5 changed files with 80 additions and 9 deletions
|
@ -161,6 +161,10 @@ export default defineComponent({
|
|||
type: Array as () => Recipe[],
|
||||
default: () => [],
|
||||
},
|
||||
cookbookSlug: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
categorySlug: {
|
||||
type: String,
|
||||
default: null,
|
||||
|
@ -213,6 +217,8 @@ export default defineComponent({
|
|||
const hasMore = ref(true);
|
||||
const ready = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const cookbook = ref<string>(props.cookbookSlug);
|
||||
const category = ref<string>(props.categorySlug);
|
||||
const tag = ref<string>(props.tagSlug);
|
||||
const tool = ref<string>(props.toolSlug);
|
||||
|
@ -227,6 +233,7 @@ export default defineComponent({
|
|||
perPage.value*2,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.value,
|
||||
tool.value,
|
||||
|
@ -253,6 +260,7 @@ export default defineComponent({
|
|||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.value,
|
||||
tool.value,
|
||||
|
@ -314,6 +322,7 @@ export default defineComponent({
|
|||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.value,
|
||||
tool.value,
|
||||
|
|
|
@ -11,8 +11,17 @@ export const useLazyRecipes = function () {
|
|||
|
||||
const recipes = ref<Recipe[]>([]);
|
||||
|
||||
async function fetchMore(page: number, perPage: number, orderBy: string | null = null, orderDirection = "desc", category: string | null = null, tag: string | null = null, tool: string | null = null) {
|
||||
const { data } = await api.recipes.getAll(page, perPage, { orderBy, orderDirection, "categories": category, "tags": tag, "tools": tool });
|
||||
async function fetchMore(
|
||||
page: number,
|
||||
perPage: number,
|
||||
orderBy: string | null = null,
|
||||
orderDirection = "desc",
|
||||
cookbook: string | null = null,
|
||||
category: string | null = null,
|
||||
tag: string | null = null,
|
||||
tool: string | null = null
|
||||
) {
|
||||
const { data } = await api.recipes.getAll(page, perPage, { orderBy, orderDirection, cookbook, "categories": category, "tags": tag, "tools": tool });
|
||||
return data ? data.items : [];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,25 +11,35 @@
|
|||
</v-card>
|
||||
|
||||
<v-container class="pa-0">
|
||||
<RecipeCardSection class="mb-5 mx-1" :recipes="book.recipes" />
|
||||
<RecipeCardSection
|
||||
class="mb-5 mx-1"
|
||||
:recipes="recipes"
|
||||
:cookbook-slug="slug"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
@delete="removeRecipe"
|
||||
/>
|
||||
</v-container>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, useRoute, ref, useMeta } from "@nuxtjs/composition-api";
|
||||
import { useLazyRecipes } from "~/composables/recipes";
|
||||
import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { useCookbook } from "~/composables/use-group-cookbooks";
|
||||
|
||||
export default defineComponent({
|
||||
components: { RecipeCardSection },
|
||||
setup() {
|
||||
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes();
|
||||
|
||||
const route = useRoute();
|
||||
const slug = route.value.params.slug;
|
||||
const { getOne } = useCookbook();
|
||||
|
||||
const tab = ref(null);
|
||||
|
||||
const book = getOne(slug);
|
||||
|
||||
useMeta(() => {
|
||||
|
@ -40,7 +50,13 @@ export default defineComponent({
|
|||
|
||||
return {
|
||||
book,
|
||||
slug,
|
||||
tab,
|
||||
appendRecipes,
|
||||
assignSorted,
|
||||
recipes,
|
||||
removeRecipe,
|
||||
replaceRecipes,
|
||||
};
|
||||
},
|
||||
head: {}, // Must include for useMeta
|
||||
|
|
|
@ -14,6 +14,7 @@ from mealie.db.models.recipe.recipe import RecipeModel
|
|||
from mealie.db.models.recipe.settings import RecipeSettings
|
||||
from mealie.db.models.recipe.tag import Tag
|
||||
from mealie.db.models.recipe.tool import Tool
|
||||
from mealie.schema.cookbook.cookbook import ReadCookBook
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipePagination, RecipeSummary, RecipeTag, RecipeTool
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, TagBase
|
||||
|
@ -134,6 +135,7 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
|
|||
pagination: PaginationQuery,
|
||||
override=None,
|
||||
load_food=False,
|
||||
cookbook: Optional[ReadCookBook] = None,
|
||||
categories: Optional[list[UUID4 | str]] = None,
|
||||
tags: Optional[list[UUID4 | str]] = None,
|
||||
tools: Optional[list[UUID4 | str]] = None,
|
||||
|
@ -154,6 +156,18 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
|
|||
fltr = self._filter_builder()
|
||||
q = q.filter_by(**fltr)
|
||||
|
||||
if cookbook:
|
||||
cb_filters = self._category_tag_filters(
|
||||
cookbook.categories,
|
||||
cookbook.tags,
|
||||
cookbook.tools,
|
||||
cookbook.require_all_categories,
|
||||
cookbook.require_all_tags,
|
||||
cookbook.require_all_tools,
|
||||
)
|
||||
|
||||
q = q.filter(*cb_filters)
|
||||
|
||||
if categories:
|
||||
for category in categories:
|
||||
if isinstance(category, UUID):
|
||||
|
@ -241,7 +255,7 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
|
|||
tool_ids = [x.id for x in tools]
|
||||
|
||||
if require_all_tools:
|
||||
fltr.extend(RecipeModel.tags.any(Tag.id == tag_id) for tag_id in tag_ids)
|
||||
fltr.extend(RecipeModel.tools.any(Tool.id == tool_id) for tool_id in tool_ids)
|
||||
else:
|
||||
fltr.append(RecipeModel.tools.any(Tool.id.in_(tool_ids)))
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Optional
|
|||
from zipfile import ZipFile
|
||||
|
||||
import sqlalchemy
|
||||
from fastapi import BackgroundTasks, Depends, File, Form, HTTPException, Query, status
|
||||
from fastapi import BackgroundTasks, Depends, File, Form, HTTPException, Query, Request, status
|
||||
from fastapi.datastructures import UploadFile
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import JSONResponse
|
||||
|
@ -16,11 +16,14 @@ from mealie.core import exceptions
|
|||
from mealie.core.dependencies import temporary_zip_path
|
||||
from mealie.core.dependencies.dependencies import temporary_dir, validate_recipe_token
|
||||
from mealie.core.security import create_recipe_slug_token
|
||||
from mealie.db.models.group.cookbook import CookBook
|
||||
from mealie.pkgs import cache
|
||||
from mealie.repos.repository_generic import RepositoryGeneric
|
||||
from mealie.repos.repository_recipes import RepositoryRecipes
|
||||
from mealie.routes._base import BaseCrudController, controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.routes._base.routers import MealieCrudRoute, UserAPIRouter
|
||||
from mealie.schema.cookbook.cookbook import ReadCookBook
|
||||
from mealie.schema.recipe import Recipe, RecipeImageTypes, ScrapeRecipe
|
||||
from mealie.schema.recipe.recipe import (
|
||||
CreateRecipe,
|
||||
|
@ -54,6 +57,10 @@ class BaseRecipeController(BaseCrudController):
|
|||
def repo(self) -> RepositoryRecipes:
|
||||
return self.repos.recipes.by_group(self.group_id)
|
||||
|
||||
@cached_property
|
||||
def cookbooks_repo(self) -> RepositoryGeneric[ReadCookBook, CookBook]:
|
||||
return self.repos.cookbooks.by_group(self.group_id)
|
||||
|
||||
@cached_property
|
||||
def service(self) -> RecipeService:
|
||||
return RecipeService(self.repos, self.user, self.group)
|
||||
|
@ -219,24 +226,40 @@ class RecipeController(BaseRecipeController):
|
|||
@router.get("", response_model=RecipePagination)
|
||||
def get_all(
|
||||
self,
|
||||
q: RecipePaginationQuery = Depends(RecipePaginationQuery),
|
||||
request: Request,
|
||||
q: RecipePaginationQuery = Depends(),
|
||||
cookbook: Optional[UUID4 | str] = Query(None),
|
||||
categories: Optional[list[UUID4 | str]] = Query(None),
|
||||
tags: Optional[list[UUID4 | str]] = Query(None),
|
||||
tools: Optional[list[UUID4 | str]] = Query(None),
|
||||
):
|
||||
cookbook_data: Optional[ReadCookBook] = None
|
||||
if cookbook:
|
||||
cb_match_attr = "slug" if isinstance(cookbook, str) else "id"
|
||||
cookbook_data = self.cookbooks_repo.get_one(cookbook, cb_match_attr)
|
||||
|
||||
if cookbook is None:
|
||||
raise HTTPException(status_code=404, detail="cookbook not found")
|
||||
|
||||
pagination_response = self.repo.page_all(
|
||||
pagination=q,
|
||||
load_food=q.load_food,
|
||||
cookbook=cookbook_data,
|
||||
categories=categories,
|
||||
tags=tags,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
pagination_response.set_pagination_guides(router.url_path_for("get_all"), q.dict())
|
||||
# merge default pagination with the request's query params
|
||||
query_params = q.dict() | {**request.query_params}
|
||||
pagination_response.set_pagination_guides(
|
||||
router.url_path_for("get_all"),
|
||||
{k: v for k, v in query_params.items() if v is not None},
|
||||
)
|
||||
|
||||
new_items = []
|
||||
for item in pagination_response.items:
|
||||
# Pydantic/FastAPI can't seem to serialize the ingredient field on thier own.
|
||||
# Pydantic/FastAPI can't seem to serialize the ingredient field on their own.
|
||||
new_item = item.__dict__
|
||||
|
||||
if q.load_food:
|
||||
|
|
Loading…
Reference in a new issue