added recipe sage and domestica imports

This commit is contained in:
vabene1111 2021-03-28 18:58:37 +02:00
parent 738f0781b2
commit 7f8e29f1bc
7 changed files with 152 additions and 24 deletions

View File

@ -112,13 +112,15 @@ class ImportExportBase(forms.Form):
SAFRON = 'SAFRON'
CHEFTAP = 'CHEFTAP'
PEPPERPLATE = 'PEPPERPLATE'
RECIPESAGE = 'RECIPESAGE'
DOMESTICA = 'DOMESTICA'
type = forms.ChoiceField(choices=(
(DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'),
(MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'), (SAFRON, 'Safron'), (CHEFTAP, 'ChefTap'),
(PEPPERPLATE, 'Pepperplate'),
(PEPPERPLATE, 'Pepperplate'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'),
))
duplicates = forms.BooleanField(help_text=_('To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'))
duplicates = forms.BooleanField(help_text=_('To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'), required=False)
class ImportForm(ImportExportBase):

View File

@ -0,0 +1,51 @@
import base64
from io import BytesIO
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Ingredient
class Domestica(Integration):
def get_recipe_from_file(self, file):
recipe = Recipe.objects.create(
name=file['name'].strip(),
created_by=self.request.user, internal=True,
space=self.request.space)
if file['servings'] != '':
recipe.servings = file['servings']
if file['timeCook'] != '':
recipe.waiting_time = file['timeCook']
if file['timePrep'] != '':
recipe.working_time = file['timePrep']
recipe.save()
step = Step.objects.create(
instruction=file['directions']
)
if file['source'] != '':
step.instruction += '\n' + file['source']
for ingredient in file['ingredients'].split('\n'):
amount, unit, ingredient, note = parse(ingredient)
f = get_food(ingredient, self.request.space)
u = get_unit(unit, self.request.space)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
if file['image'] != '':
self.import_recipe_image(recipe, BytesIO(base64.b64decode(file['image'].replace('data:image/jpeg;base64,', ''))))
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@ -1,4 +1,5 @@
import datetime
import json
import uuid
from io import BytesIO, StringIO
@ -19,6 +20,7 @@ class Integration:
request = None
keyword = None
files = None
ignored_recipes = []
def __init__(self, request, export_type):
"""
@ -89,7 +91,6 @@ class Integration:
self.keyword.name = _('Import') + ' ' + str(il.pk)
self.keyword.save()
ignored_recipes = []
try:
self.files = files
for f in files:
@ -100,38 +101,41 @@ class Integration:
recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename)))
recipe.keywords.add(self.keyword)
il.msg += f'{recipe.pk} - {recipe.name} \n'
if not import_duplicates:
if duplicate := self.is_duplicate(recipe):
ignored_recipes.append(duplicate)
self.handle_duplicates(recipe, import_duplicates)
import_zip.close()
elif '.json' in f['name']:
json_data = json.loads(f['file'].read().decode("utf-8"))
for d in json_data:
recipe = self.get_recipe_from_file(d)
recipe.keywords.add(self.keyword)
il.msg += f'{recipe.pk} - {recipe.name} \n'
self.handle_duplicates(recipe, import_duplicates)
else:
recipe = self.get_recipe_from_file(f['file'])
recipe.keywords.add(self.keyword)
il.msg += f'{recipe.pk} - {recipe.name} \n'
if not import_duplicates:
if duplicate := self.is_duplicate(recipe):
ignored_recipes.append(duplicate)
self.handle_duplicates(recipe, import_duplicates)
except BadZipFile:
il.msg += 'ERROR ' + _('Importer expected a .zip file. Did you choose the correct importer type for your data ?') + '\n'
if len(ignored_recipes) > 0:
il.msg += '\n' + _('The following recipes were ignored because they already existed:') + ' ' + ', '.join(ignored_recipes) + '\n\n'
if len(self.ignored_recipes) > 0:
il.msg += '\n' + _('The following recipes were ignored because they already existed:') + ' ' + ', '.join(self.ignored_recipes) + '\n\n'
il.keyword = self.keyword
il.msg += (_('Imported %s recipes.') % Recipe.objects.filter(keywords=self.keyword).count()) + '\n'
il.running = False
il.save()
def is_duplicate(self, recipe):
def handle_duplicates(self, recipe, import_duplicates):
"""
Checks if a recipe is already present, if so deletes it
:param recipe: Recipe object
:param import_duplicates: if duplicates should be imported
"""
if Recipe.objects.filter(space=self.request.space, name=recipe.name).count() > 1:
if Recipe.objects.filter(space=self.request.space, name=recipe.name).count() > 1 and not import_duplicates:
recipe.delete()
return recipe.name
else:
return None
self.ignored_recipes.append(recipe.name)
@staticmethod
def import_recipe_image(recipe, image_file):

View File

@ -1,17 +1,11 @@
import base64
import gzip
import json
import re
from io import BytesIO
from zipfile import ZipFile
import microdata
from bs4 import BeautifulSoup
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.helper.recipe_url_import import find_recipe_json
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Ingredient, Unit
import gzip
from cookbook.models import Recipe, Step, Ingredient
class Paprika(Integration):

View File

@ -0,0 +1,61 @@
import base64
from io import BytesIO
import requests
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Ingredient
class RecipeSage(Integration):
def get_recipe_from_file(self, file):
recipe = Recipe.objects.create(
name=file['name'].strip(),
created_by=self.request.user, internal=True,
space=self.request.space)
try:
if file['recipeYield'] != '':
recipe.servings = int(file['recipeYield'])
if file['totalTime'] != '':
recipe.waiting_time = int(file['totalTime']) - int(file['timePrep'])
if file['prepTime'] != '':
recipe.working_time = int(file['timePrep'])
recipe.save()
except Exception as e:
print('failed to parse yield or time ', str(e))
ingredients_added = False
for s in file['recipeInstructions']:
step = Step.objects.create(
instruction=s['text']
)
if not ingredients_added:
ingredients_added = True
for ingredient in file['recipeIngredient']:
amount, unit, ingredient, note = parse(ingredient)
f = get_food(ingredient, self.request.space)
u = get_unit(unit, self.request.space)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
if len(file['image']) > 0:
try:
response = requests.get(file['image'][0])
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@ -14,9 +14,11 @@ from cookbook.integration.Pepperplate import Pepperplate
from cookbook.integration.cheftap import ChefTap
from cookbook.integration.chowdown import Chowdown
from cookbook.integration.default import Default
from cookbook.integration.domestica import Domestica
from cookbook.integration.mealie import Mealie
from cookbook.integration.nextcloud_cookbook import NextcloudCookbook
from cookbook.integration.paprika import Paprika
from cookbook.integration.recipesage import RecipeSage
from cookbook.integration.safron import Safron
from cookbook.models import Recipe, ImportLog
@ -38,6 +40,10 @@ def get_integration(request, export_type):
return ChefTap(request, export_type)
if export_type == ImportExportBase.PEPPERPLATE:
return Pepperplate(request, export_type)
if export_type == ImportExportBase.DOMESTICA:
return Domestica(request, export_type)
if export_type == ImportExportBase.RECIPESAGE:
return RecipeSage(request, export_type)
@group_required('user')

View File

@ -30,6 +30,8 @@ Overview of the capabilities of the different integrations.
| Paprika | ✔️ | ⌚ | ✔️ |
| ChefTap | ✔️ | ❌ | ❌️ |
| Pepperplate | ✔️ | ⌚ | ❌️ |
| RecipeSage | ✔️ | ⌚ | ✔️ |
| Domestica | ✔️ | ⌚ | ✔️ |
✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
@ -40,6 +42,14 @@ It is maintained with new fields added and contains all data to transfer your re
It is also one of the few recipe formats that is actually structured in a way that allows for
easy machine readability if you want to use the data for any other purpose.
## RecipeSage
Go to Settings > Export Recipe Data and select `EXPORT AS JSON-LD (BEST)`. Then simply upload the exported file
to Tandoor.
## Domestica
Go to Import/Export and select `Export Recipes`. Then simply upload the exported file
to Tandoor.
## Nextcloud
Importing recipes from Nextcloud cookbook is very easy and since Nextcloud Cookbook provides nice, standardized and
structured information most of your recipe is going to be intact.