feat: add bulk actions service and routes (WIP) (#747)

* feat(frontend):  Group level recipe data management

* feat(backend):  add bulk actions service and routes

Co-authored-by: Hayden <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-10-18 19:41:41 -08:00
committed by GitHub
parent 3b920babe3
commit 4e70c96f8a
13 changed files with 703 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
from fastapi import APIRouter
from . import all_recipe_routes, comments, image_and_assets, recipe_crud_routes, recipe_export
from . import all_recipe_routes, bulk_actions, comments, image_and_assets, recipe_crud_routes, recipe_export
prefix = "/recipes"
@@ -11,3 +11,4 @@ router.include_router(recipe_export.user_router, prefix=prefix, tags=["Recipe: E
router.include_router(recipe_crud_routes.user_router, prefix=prefix, tags=["Recipe: CRUD"])
router.include_router(image_and_assets.user_router, prefix=prefix, tags=["Recipe: Images and Assets"])
router.include_router(comments.router, prefix=prefix, tags=["Recipe: Comments"])
router.include_router(bulk_actions.router, prefix=prefix, tags=["Recipe: Bulk Actions"])

View File

@@ -0,0 +1,49 @@
from fastapi import APIRouter, Depends
from fastapi.responses import FileResponse
from mealie.core.dependencies.dependencies import temporary_zip_path
from mealie.schema.recipe.recipe_bulk_actions import (
AssignCategories,
AssignTags,
BulkActionsResponse,
DeleteRecipes,
ExportRecipes,
)
from mealie.services.recipe.recipe_bulk_service import RecipeBulkActions
router = APIRouter(prefix="/bulk-actions")
@router.post("/tag", response_model=BulkActionsResponse)
def bulk_tag_recipes(
tag_data: AssignTags,
bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private),
):
bulk_service.assign_tags(tag_data.recipes, tag_data.tags)
@router.post("/categorize", response_model=BulkActionsResponse)
def bulk_categorize_recipes(
assign_cats: AssignCategories,
bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private),
):
bulk_service.assign_categories(assign_cats.recipes, assign_cats.categories)
@router.post("/delete", response_model=BulkActionsResponse)
def bulk_delete_recipes(
delete_recipes: DeleteRecipes,
bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private),
):
bulk_service.delete_recipes(delete_recipes.recipes)
@router.post("/export", response_class=FileResponse)
def bulk_export_recipes(
export_recipes: ExportRecipes,
temp_path=Depends(temporary_zip_path),
bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private),
):
bulk_service.export_recipes(temp_path, export_recipes.recipes)
return FileResponse(temp_path, filename="recipes.zip")

View File

@@ -0,0 +1,40 @@
import enum
from fastapi_camelcase import CamelModel
from . import CategoryBase, TagBase
class ExportTypes(str, enum.Enum):
JSON = "json"
class _ExportBase(CamelModel):
recipes: list[str]
class ExportRecipes(_ExportBase):
export_type: ExportTypes = ExportTypes.JSON
class AssignCategories(_ExportBase):
categories: list[CategoryBase]
class AssignTags(_ExportBase):
tags: list[TagBase]
class DeleteRecipes(_ExportBase):
pass
class BulkActionError(CamelModel):
recipe: str
error: str
class BulkActionsResponse(CamelModel):
success: bool
message: str
errors: list[BulkActionError] = []

View File

@@ -0,0 +1,60 @@
from __future__ import annotations
from pathlib import Path
from mealie.core.root_logger import get_logger
from mealie.schema.recipe import CategoryBase, Recipe
from mealie.schema.recipe.recipe_category import TagBase
from mealie.services._base_http_service.http_services import UserHttpService
from mealie.services.events import create_recipe_event
logger = get_logger(__name__)
class RecipeBulkActions(UserHttpService[int, Recipe]):
event_func = create_recipe_event
_restrict_by_group = True
def populate_item(self, _: int) -> Recipe:
return
def export_recipes(self, temp_path: Path, recipes: list[str]) -> None:
return
def assign_tags(self, recipes: list[str], tags: list[TagBase]) -> None:
for slug in recipes:
recipe = self.db.recipes.get_one(slug)
if recipe is None:
logger.error(f"Failed to tag recipe {slug}, no recipe found")
recipe.tags += tags
try:
self.db.recipes.update(slug, recipe)
except Exception as e:
logger.error(f"Failed to tag recipe {slug}")
logger.error(e)
def assign_categories(self, recipes: list[str], categories: list[CategoryBase]) -> None:
for slug in recipes:
recipe = self.db.recipes.get_one(slug)
if recipe is None:
logger.error(f"Failed to categorize recipe {slug}, no recipe found")
recipe.recipe_category += categories
try:
self.db.recipes.update(slug, recipe)
except Exception as e:
logger.error(f"Failed to categorize recipe {slug}")
logger.error(e)
def delete_recipes(self, recipes: list[str]) -> None:
for slug in recipes:
try:
self.db.recipes.delete(slug)
except Exception as e:
logger.error(f"Failed to delete recipe {slug}")
logger.error(e)