193 lines
7.7 KiB
Python
193 lines
7.7 KiB
Python
import json
|
|
import re
|
|
from io import BytesIO, StringIO
|
|
from zipfile import ZipFile
|
|
|
|
from PIL import Image
|
|
|
|
from cookbook.helper.image_processing import get_filetype
|
|
from cookbook.helper.ingredient_parser import IngredientParser
|
|
from cookbook.helper.recipe_url_import import iso_duration_to_minutes
|
|
from cookbook.integration.integration import Integration
|
|
from cookbook.models import Ingredient, Keyword, NutritionInformation, Recipe, Step
|
|
|
|
|
|
class NextcloudCookbook(Integration):
|
|
|
|
def import_file_name_filter(self, zip_info_object):
|
|
return zip_info_object.filename.endswith('.json')
|
|
|
|
def get_recipe_from_file(self, file):
|
|
recipe_json = json.loads(file.getvalue().decode("utf-8"))
|
|
|
|
description = '' if len(recipe_json['description'].strip()) > 500 else recipe_json['description'].strip()
|
|
|
|
recipe = Recipe.objects.create(
|
|
name=recipe_json['name'].strip(), description=description,
|
|
created_by=self.request.user, internal=True,
|
|
servings=recipe_json['recipeYield'], space=self.request.space)
|
|
|
|
try:
|
|
recipe.working_time = iso_duration_to_minutes(recipe_json['prepTime'])
|
|
recipe.waiting_time = iso_duration_to_minutes(recipe_json['cookTime'])
|
|
except Exception:
|
|
pass
|
|
|
|
if 'url' in recipe_json:
|
|
recipe.source_url = recipe_json['url'].strip()
|
|
|
|
if 'recipeCategory' in recipe_json:
|
|
try:
|
|
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=recipe_json['recipeCategory'])[0])
|
|
except Exception:
|
|
pass
|
|
|
|
if 'keywords' in recipe_json:
|
|
try:
|
|
for x in recipe_json['keywords'].split(','):
|
|
if x.strip() != '':
|
|
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=x)[0])
|
|
except Exception:
|
|
pass
|
|
|
|
ingredients_added = False
|
|
for s in recipe_json['recipeInstructions']:
|
|
if 'text' in s:
|
|
step = Step.objects.create(
|
|
instruction=s['text'], name=s['name'], space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,
|
|
)
|
|
else:
|
|
step = Step.objects.create(
|
|
instruction=s, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,
|
|
)
|
|
if not ingredients_added:
|
|
if len(recipe_json['description'].strip()) > 500:
|
|
step.instruction = recipe_json['description'].strip() + '\n\n' + step.instruction
|
|
|
|
ingredients_added = True
|
|
|
|
ingredient_parser = IngredientParser(self.request, True)
|
|
for ingredient in recipe_json['recipeIngredient']:
|
|
amount, unit, food, note = ingredient_parser.parse(ingredient)
|
|
f = ingredient_parser.get_food(food)
|
|
u = ingredient_parser.get_unit(unit)
|
|
step.ingredients.add(Ingredient.objects.create(
|
|
food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space,
|
|
))
|
|
recipe.steps.add(step)
|
|
|
|
if 'nutrition' in recipe_json:
|
|
nutrition = {}
|
|
try:
|
|
if 'calories' in recipe_json['nutrition']:
|
|
nutrition['calories'] = int(re.search(r'\d+', recipe_json['nutrition']['calories']).group())
|
|
if 'proteinContent' in recipe_json['nutrition']:
|
|
nutrition['proteins'] = int(re.search(r'\d+', recipe_json['nutrition']['proteinContent']).group())
|
|
if 'fatContent' in recipe_json['nutrition']:
|
|
nutrition['fats'] = int(re.search(r'\d+', recipe_json['nutrition']['fatContent']).group())
|
|
if 'carbohydrateContent' in recipe_json['nutrition']:
|
|
nutrition['carbohydrates'] = int(re.search(r'\d+', recipe_json['nutrition']['carbohydrateContent']).group())
|
|
|
|
if nutrition != {}:
|
|
recipe.nutrition = NutritionInformation.objects.create(**nutrition, space=self.request.space)
|
|
recipe.save()
|
|
except Exception:
|
|
pass
|
|
|
|
for f in self.files:
|
|
if '.zip' in f['name']:
|
|
import_zip = ZipFile(f['file'])
|
|
for z in import_zip.filelist:
|
|
if re.match(f'^(.)+{recipe.name}/full.jpg$', z.filename):
|
|
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
|
|
|
|
return recipe
|
|
|
|
def formatTime(self, min):
|
|
h = min // 60
|
|
m = min % 60
|
|
return f'PT{h}H{m}M0S'
|
|
|
|
def get_file_from_recipe(self, recipe):
|
|
|
|
export = {}
|
|
export['name'] = recipe.name
|
|
export['description'] = recipe.description
|
|
export['url'] = recipe.source_url
|
|
export['prepTime'] = self.formatTime(recipe.working_time)
|
|
export['cookTime'] = self.formatTime(recipe.waiting_time)
|
|
export['totalTime'] = self.formatTime(recipe.working_time + recipe.waiting_time)
|
|
export['recipeYield'] = recipe.servings
|
|
export['image'] = f'/Recipes/{recipe.name}/full.jpg'
|
|
export['imageUrl'] = f'/Recipes/{recipe.name}/full.jpg'
|
|
|
|
recipeKeyword = []
|
|
for k in recipe.keywords.all():
|
|
recipeKeyword.append(k.name)
|
|
|
|
export['keywords'] = recipeKeyword
|
|
|
|
recipeInstructions = []
|
|
recipeIngredient = []
|
|
for s in recipe.steps.all():
|
|
recipeInstructions.append(s.instruction)
|
|
|
|
for i in s.ingredients.all():
|
|
recipeIngredient.append(f'{float(i.amount)} {i.unit} {i.food}')
|
|
|
|
export['recipeIngredient'] = recipeIngredient
|
|
export['recipeInstructions'] = recipeInstructions
|
|
|
|
return "recipe.json", json.dumps(export)
|
|
|
|
def get_files_from_recipes(self, recipes, el, cookie):
|
|
export_zip_stream = BytesIO()
|
|
export_zip_obj = ZipFile(export_zip_stream, 'w')
|
|
|
|
for recipe in recipes:
|
|
if recipe.internal and recipe.space == self.request.space:
|
|
|
|
recipe_stream = StringIO()
|
|
filename, data = self.get_file_from_recipe(recipe)
|
|
recipe_stream.write(data)
|
|
export_zip_obj.writestr(f'{recipe.name}/{filename}', recipe_stream.getvalue())
|
|
recipe_stream.close()
|
|
|
|
try:
|
|
imageByte = recipe.image.file.read()
|
|
export_zip_obj.writestr(f'{recipe.name}/full.jpg', self.getJPEG(imageByte))
|
|
export_zip_obj.writestr(f'{recipe.name}/thumb.jpg', self.getThumb(171, imageByte))
|
|
export_zip_obj.writestr(f'{recipe.name}/thumb16.jpg', self.getThumb(16, imageByte))
|
|
except ValueError:
|
|
pass
|
|
|
|
el.exported_recipes += 1
|
|
el.msg += self.get_recipe_processed_msg(recipe)
|
|
el.save()
|
|
|
|
export_zip_obj.close()
|
|
|
|
return [[self.get_export_file_name(), export_zip_stream.getvalue()]]
|
|
|
|
def getJPEG(self, imageByte):
|
|
image = Image.open(BytesIO(imageByte))
|
|
image = image.convert('RGB')
|
|
|
|
bytes = BytesIO()
|
|
image.save(bytes, "JPEG")
|
|
return bytes.getvalue()
|
|
|
|
def getThumb(self, size, imageByte):
|
|
image = Image.open(BytesIO(imageByte))
|
|
|
|
w, h = image.size
|
|
m = min(w, h)
|
|
|
|
image = image.crop(((w - m) // 2, (h - m) // 2, (w + m) // 2, (h + m) // 2))
|
|
image = image.resize([size, size], Image.Resampling.LANCZOS)
|
|
image = image.convert('RGB')
|
|
|
|
bytes = BytesIO()
|
|
image.save(bytes, "JPEG")
|
|
return bytes.getvalue()
|