Feature/import export single recipe (#576)

* remove duplicate keys

* show context menu when not logged in

* remove console.log

* hide menu when printing

* add response to event

* add type definitions

* show context menu always

* add image name enums

* upload/download single recipe

* cleanup menu views+ localization

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-06-21 16:25:37 -07:00
committed by GitHub
parent 3220595a83
commit f5faff66d3
14 changed files with 207 additions and 35 deletions

View File

@@ -4,7 +4,7 @@ from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from mealie.core.config import settings
from mealie.core.config import app_dirs, settings
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.schema.auth import TokenData
@@ -100,3 +100,13 @@ def validate_file_token(token: Optional[str] = None) -> Path:
raise credentials_exception
return file_path
async def temporary_zip_path() -> Path:
temp_path = app_dirs.TEMP_DIR.mkdir(exist_ok=True, parents=True)
temp_path = app_dirs.TEMP_DIR.joinpath("my_zip_archive.zip")
try:
yield temp_path
finally:
temp_path.unlink(missing_ok=True)

View File

@@ -1,4 +1,7 @@
import json
import shutil
from shutil import copyfileobj
from zipfile import ZipFile
from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, status
from fastapi.datastructures import UploadFile
@@ -6,8 +9,8 @@ from mealie.core.config import settings
from mealie.core.root_logger import get_logger
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user, is_logged_in
from mealie.schema.recipe import Recipe, RecipeAsset, RecipeURLIn
from mealie.routes.deps import get_current_user, is_logged_in, temporary_zip_path
from mealie.schema.recipe import Recipe, RecipeAsset, RecipeImageTypes, RecipeURLIn
from mealie.schema.user import UserInDB
from mealie.services.events import create_recipe_event
from mealie.services.image.image import scrape_image, write_image
@@ -16,6 +19,7 @@ from mealie.services.scraper.scraper import create_from_url
from scrape_schema_recipe import scrape_url
from slugify import slugify
from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse
router = APIRouter(prefix="/api/recipes", tags=["Recipe CRUD"])
logger = get_logger()
@@ -87,6 +91,54 @@ def get_recipe(recipe_slug: str, session: Session = Depends(generate_session), i
raise HTTPException(status.HTTP_401_UNAUTHORIZED, {"details": "unauthorized"})
@router.post("/create-from-zip", dependencies=[Depends(get_current_user)])
async def create_recipe_from_zip(
session: Session = Depends(generate_session),
temp_path=Depends(temporary_zip_path),
archive: UploadFile = File(...),
):
""" Create recipe from archive """
with temp_path.open("wb") as buffer:
shutil.copyfileobj(archive.file, buffer)
recipe_dict = None
recipe_image = None
with ZipFile(temp_path) as myzip:
for file in myzip.namelist():
if file.endswith(".json"):
with myzip.open(file) as myfile:
recipe_dict = json.loads(myfile.read())
elif file.endswith(".webp"):
with myzip.open(file) as myfile:
recipe_image = myfile.read()
recipe: Recipe = db.recipes.create(session, Recipe(**recipe_dict))
write_image(recipe.slug, recipe_image, "webp")
return recipe
@router.get("/{recipe_slug}/zip")
async def get_recipe_as_zip(
recipe_slug: str, session: Session = Depends(generate_session), temp_path=Depends(temporary_zip_path)
):
""" Get a Recipe and It's Original Image as a Zip File """
recipe: Recipe = db.recipes.get(session, recipe_slug)
image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value)
with ZipFile(temp_path, "w") as myzip:
myzip.writestr(f"{recipe_slug}.json", recipe.json())
if image_asset.is_file():
myzip.write(image_asset, arcname=image_asset.name)
return FileResponse(temp_path, filename=f"{recipe_slug}.zip")
@router.put("/{recipe_slug}", dependencies=[Depends(get_current_user)])
def update_recipe(
recipe_slug: str,

View File

@@ -1,4 +1,5 @@
import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Optional
@@ -11,6 +12,12 @@ from pydantic.utils import GetterDict
from slugify import slugify
class RecipeImageTypes(str, Enum):
original = "original.webp"
min = "min-original.webp"
tiny = "tiny-original.webp"
class RecipeSettings(CamelModel):
public: bool = True
show_nutrition: bool = True