feat: Cross-Household Recipes (#4089)

This commit is contained in:
Michael Genson
2024-08-31 21:54:10 -05:00
committed by GitHub
parent 7ef2e91ecf
commit 9acf9ec27c
16 changed files with 545 additions and 92 deletions

View File

@@ -32,6 +32,7 @@ from mealie.core.dependencies import (
from mealie.core.security import create_recipe_slug_token
from mealie.db.models.household.cookbook import CookBook
from mealie.pkgs import cache
from mealie.repos.all_repositories import get_repositories
from mealie.repos.repository_generic import RepositoryGeneric
from mealie.repos.repository_recipes import RepositoryRecipes
from mealie.routes._base import BaseCrudController, controller
@@ -94,9 +95,13 @@ class JSONBytes(JSONResponse):
class BaseRecipeController(BaseCrudController):
@cached_property
def repo(self) -> RepositoryRecipes:
def recipes(self) -> RepositoryRecipes:
return self.repos.recipes
@cached_property
def group_recipes(self) -> RepositoryRecipes:
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
@cached_property
def cookbooks_repo(self) -> RepositoryGeneric[ReadCookBook, CookBook]:
return self.repos.cookbooks
@@ -107,7 +112,7 @@ class BaseRecipeController(BaseCrudController):
@cached_property
def mixins(self):
return HttpRepo[CreateRecipe, Recipe, Recipe](self.repo, self.logger)
return HttpRepo[CreateRecipe, Recipe, Recipe](self.recipes, self.logger)
class FormatResponse(BaseModel):
@@ -331,8 +336,9 @@ class RecipeController(BaseRecipeController):
if cookbook_data is None:
raise HTTPException(status_code=404, detail="cookbook not found")
# we use the repo by user so we can sort favorites correctly
pagination_response = self.repos.recipes.by_user(self.user.id).page_all(
# We use "group_recipes" here so we can return all recipes regardless of household. The query filter can include
# a household_id to filter by household. We use the "by_user" so we can sort favorites correctly.
pagination_response = self.group_recipes.by_user(self.user.id).page_all(
pagination=q,
cookbook=cookbook_data,
categories=categories,
@@ -362,7 +368,7 @@ class RecipeController(BaseRecipeController):
def get_one(self, slug: str = Path(..., description="A recipe's slug or id")):
"""Takes in a recipe's slug or id and returns all data for a recipe"""
try:
recipe = self.service.get_one_by_slug_or_id(slug)
recipe = self.service.get_one(slug)
except Exception as e:
self.handle_exceptions(e)
return None
@@ -534,7 +540,7 @@ class RecipeController(BaseRecipeController):
data_service = RecipeDataService(recipe.id)
data_service.write_image(image, extension)
new_version = self.repo.update_image(slug, extension)
new_version = self.recipes.update_image(slug, extension)
return UpdateImageResponse(image=new_version)
@router.post("/{slug}/assets", response_model=RecipeAsset, tags=["Recipe: Images and Assets"])

View File

@@ -4,6 +4,7 @@ from functools import cached_property
from fastapi import Depends, File, Form, HTTPException
from pydantic import UUID4
from mealie.repos.all_repositories import get_repositories
from mealie.routes._base import BaseCrudController, controller
from mealie.routes._base.mixins import HttpRepo
from mealie.routes._base.routers import MealieCrudRoute, UserAPIRouter
@@ -31,8 +32,8 @@ class RecipeTimelineEventsController(BaseCrudController):
return self.repos.recipe_timeline_events
@cached_property
def recipes_repo(self):
return self.repos.recipes
def group_recipes(self):
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
@cached_property
def mixins(self):
@@ -57,7 +58,7 @@ class RecipeTimelineEventsController(BaseCrudController):
# if the user id is not specified, use the currently-authenticated user
data.user_id = data.user_id or self.user.id
recipe = self.recipes_repo.get_one(data.recipe_id, "id")
recipe = self.group_recipes.get_one(data.recipe_id, "id")
if not recipe:
raise HTTPException(status_code=404, detail="recipe not found")
@@ -87,7 +88,7 @@ class RecipeTimelineEventsController(BaseCrudController):
@events_router.put("/{item_id}", response_model=RecipeTimelineEventOut)
def update_one(self, item_id: UUID4, data: RecipeTimelineEventUpdate):
event = self.mixins.patch_one(data, item_id)
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
recipe = self.group_recipes.get_one(event.recipe_id, "id")
if recipe:
self.publish_event(
event_type=EventTypes.recipe_updated,
@@ -114,7 +115,7 @@ class RecipeTimelineEventsController(BaseCrudController):
except FileNotFoundError:
pass
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
recipe = self.group_recipes.get_one(event.recipe_id, "id")
if recipe:
self.publish_event(
event_type=EventTypes.recipe_updated,
@@ -144,7 +145,7 @@ class RecipeTimelineEventsController(BaseCrudController):
if event.image != TimelineEventImage.has_image.value:
event.image = TimelineEventImage.has_image
event = self.mixins.patch_one(event.cast(RecipeTimelineEventUpdate), event.id)
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
recipe = self.group_recipes.get_one(event.recipe_id, "id")
if recipe:
self.publish_event(
event_type=EventTypes.recipe_updated,

View File

@@ -1,8 +1,10 @@
from functools import cached_property
from uuid import UUID
from fastapi import HTTPException, status
from pydantic import UUID4
from mealie.repos.all_repositories import get_repositories
from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed
@@ -14,6 +16,10 @@ router = UserAPIRouter()
@controller(router)
class UserRatingsController(BaseUserController):
@cached_property
def group_recipes(self):
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
def get_recipe_or_404(self, slug_or_id: str | UUID):
"""Fetches a recipe by slug or id, or raises a 404 error if not found."""
if isinstance(slug_or_id, str):
@@ -22,11 +28,10 @@ class UserRatingsController(BaseUserController):
except ValueError:
pass
recipes_repo = self.repos.recipes
if isinstance(slug_or_id, UUID):
recipe = recipes_repo.get_one(slug_or_id, key="id")
recipe = self.group_recipes.get_one(slug_or_id, key="id")
else:
recipe = recipes_repo.get_one(slug_or_id, key="slug")
recipe = self.group_recipes.get_one(slug_or_id, key="slug")
if not recipe:
raise HTTPException(