From f6c491a8e68bbc73600c2a52ee8645c2c2cd0cab Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sun, 28 Mar 2021 21:13:26 +0200 Subject: [PATCH] added rezkonv importer --- cookbook/forms.py | 2 + cookbook/integration/rezkonv.py | 81 +++++++++++++++++++++++++++++++++ cookbook/views/import_export.py | 3 ++ docs/features/import_export.md | 10 +++- 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 cookbook/integration/rezkonv.py diff --git a/cookbook/forms.py b/cookbook/forms.py index 696d77bf2..5f19329fc 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -114,11 +114,13 @@ class ImportExportBase(forms.Form): PEPPERPLATE = 'PEPPERPLATE' RECIPESAGE = 'RECIPESAGE' DOMESTICA = 'DOMESTICA' + REZKONV = 'REZKONV' type = forms.ChoiceField(choices=( (DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'), (MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'), (SAFRON, 'Safron'), (CHEFTAP, 'ChefTap'), (PEPPERPLATE, 'Pepperplate'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'), + (REZKONV, 'RezKonv'), )) diff --git a/cookbook/integration/rezkonv.py b/cookbook/integration/rezkonv.py new file mode 100644 index 000000000..4d89b6298 --- /dev/null +++ b/cookbook/integration/rezkonv.py @@ -0,0 +1,81 @@ +import json +import re +from io import BytesIO +from zipfile import ZipFile + +from cookbook.helper.ingredient_parser import parse, get_food, get_unit +from cookbook.integration.integration import Integration +from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword + + +class RezKonv(Integration): + + def get_recipe_from_file(self, file): + + ingredient_mode = False + direction_mode = False + + ingredients = [] + directions = [] + for line in file.replace('\r', '').split('\n'): + if 'Titel:' in line: + title = line.replace('Titel:', '').strip() + if 'Kategorien:' in line: + tags = line.replace('Kategorien:', '').strip() + if ingredient_mode and ('quelle' in line.lower() or 'source' in line.lower()): + ingredient_mode = False + if ingredient_mode: + if line != '' and '===' not in line and 'Zubereitung' not in line: + ingredients.append(line.strip()) + if direction_mode: + if line.strip() != '' and line.strip() != '=====': + directions.append(line.strip()) + if 'Zutaten:' in line: + ingredient_mode = True + if 'Zubereitung:' in line: + ingredient_mode = False + direction_mode = True + + recipe = Recipe.objects.create(name=title, created_by=self.request.user, internal=True, space=self.request.space) + + for k in tags.split(','): + keyword, created = Keyword.objects.get_or_create(name=k.strip(), space=self.request.space) + recipe.keywords.add(keyword) + + step = Step.objects.create( + instruction='\n'.join(directions) + '\n\n' + ) + + for ingredient in ingredients: + 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) + + return recipe + + def get_file_from_recipe(self, recipe): + raise NotImplementedError('Method not implemented in storage integration') + + def split_recipe_file(self, file): + recipe_list = [] + current_recipe = '' + + for fl in file.readlines(): + line = fl.decode("ANSI") + if line.startswith('=====') and 'rezkonv' in line.lower(): + if current_recipe != '': + recipe_list.append(current_recipe) + current_recipe = '' + else: + current_recipe = '' + else: + current_recipe += line + '\n' + + if current_recipe != '': + recipe_list.append(current_recipe) + + return recipe_list diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index b69cb52bb..1058bf391 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -19,6 +19,7 @@ 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.rezkonv import RezKonv from cookbook.integration.safron import Safron from cookbook.models import Recipe, ImportLog @@ -44,6 +45,8 @@ def get_integration(request, export_type): return Domestica(request, export_type) if export_type == ImportExportBase.RECIPESAGE: return RecipeSage(request, export_type) + if export_type == ImportExportBase.REZKONV: + return RezKonv(request, export_type) @group_required('user') diff --git a/docs/features/import_export.md b/docs/features/import_export.md index d69b00466..3e5425b8a 100644 --- a/docs/features/import_export.md +++ b/docs/features/import_export.md @@ -28,10 +28,11 @@ Overview of the capabilities of the different integrations. | Chowdown | ✔️ | ⌚ | ✔️ | | Safron | ✔️ | ⌚ | ❌ | | Paprika | ✔️ | ⌚ | ✔️ | -| ChefTap | ✔️ | ❌ | ❌️ | -| Pepperplate | ✔️ | ⌚ | ❌️ | +| ChefTap | ✔️ | ❌ | ❌ | +| Pepperplate | ✔️ | ⌚ | ❌ | | RecipeSage | ✔️ | ✔️ | ✔️ | | Domestica | ✔️ | ⌚ | ✔️ | +| RezKonv | ✔️ | ❌ | ❌ | ✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented @@ -144,3 +145,8 @@ Usually the import should recognize all ingredients and put everything else into or is worse than this feel free to provide me with more example data and I can try to improve the importer. As ChefTap cannot import these files anyway there won't be an exporter implemented in Tandoor. + +## RezKonv +The RezKonv format is primarily used in the german recipe manager RezKonv Suite. +To migrate from RezKonv Suite to Tandoor select `Export > Gesamtes Kochbuch exportieren` (the last option in the export menu). +The generated file can simply be imported into Tandoor. \ No newline at end of file