From 23415f8a61d84573fba8517ccc1ebf20ef5012c2 Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Mon, 26 Apr 2021 14:43:37 +0100 Subject: [PATCH 1/6] Initial importer created. --- cookbook/forms.py | 3 +- cookbook/integration/integration.py | 11 +++ cookbook/integration/recettetek.py | 107 ++++++++++++++++++++++++++++ cookbook/views/import_export.py | 3 + 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 cookbook/integration/recettetek.py diff --git a/cookbook/forms.py b/cookbook/forms.py index 39a230e46f..7c40a14864 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -112,6 +112,7 @@ class ImportExportBase(forms.Form): SAFRON = 'SAFRON' CHEFTAP = 'CHEFTAP' PEPPERPLATE = 'PEPPERPLATE' + RECETTETEK = 'RECETTETEK' RECIPESAGE = 'RECIPESAGE' DOMESTICA = 'DOMESTICA' MEALMASTER = 'MEALMASTER' @@ -120,7 +121,7 @@ class ImportExportBase(forms.Form): 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'), + (PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'), (MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), )) diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index 33ed72cfce..d2024c0d4f 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -122,6 +122,17 @@ def do_import(self, files, il, import_duplicates): recipe.keywords.add(self.keyword) il.msg += f'{recipe.pk} - {recipe.name} \n' self.handle_duplicates(recipe, import_duplicates) + elif '.rtk' in f['name']: + import_zip = ZipFile(f['file']) + for z in import_zip.filelist: + if self.import_file_name_filter(z): + data_list = self.split_recipe_file(import_zip.read(z.filename).decode('utf-8')) + for d in data_list: + 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) + import_zip.close() else: recipe = self.get_recipe_from_file(f['file']) recipe.keywords.add(self.keyword) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py new file mode 100644 index 0000000000..c1bf490cef --- /dev/null +++ b/cookbook/integration/recettetek.py @@ -0,0 +1,107 @@ +import re +import json +import base64 +import requests +from io import BytesIO +from zipfile import ZipFile +import imghdr +from django.utils.translation import gettext as _ + +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 + + +class RecetteTek(Integration): + + def import_file_name_filter(self, zip_info_object): + print("testing", zip_info_object.filename) + return re.match(r'^recipes_0.json$', zip_info_object.filename) + + def split_recipe_file(self, file): + + recipe_json = json.loads(file) + + recipe_list = [r for r in recipe_json] + + return recipe_list + + def get_recipe_from_file(self, file): + + # Create initial recipe with just a title and a decription + recipe = Recipe.objects.create(name=file['title'], created_by=self.request.user, internal=True, space=self.request.space, ) + + try: + if file['description'] != '': + recipe.description = file['description'].strip() + except Exception as e: + print(recipe.name, ': failed to parse recipe description ', str(e)) + + step = Step.objects.create(instruction=file['instructions']) + + try: + # Process the ingredients. Assumes 1 ingredient per line. + for ingredient in file['ingredients'].split('\n'): + if len(ingredient.strip()) > 0: + 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 + )) + except Exception as e: + print(recipe.name, ': failed to parse recipe ingredients ', str(e)) + recipe.steps.add(step) + + # Attempt to import prep/cooking times + try: + if file['quantity'] != '': + recipe.servings = int(file['quantity']) + except Exception as e: + print(recipe.name, ': failed to parse quantity ', str(e)) + + try: + if file['totalTime'] != '': + recipe.waiting_time = int(file['totalTime']) + except Exception as e: + print(recipe.name, ': failed to parse total times ', str(e)) + + try: + if file['preparationTime'] != '': + recipe.working_time = int(file['preparationTime']) + except Exception as e: + print(recipe.name, ': failed to parse prep time ', str(e)) + + try: + if file['cookingTime'] != '': + recipe.waiting_time = int(file['cookingTime']) + except Exception as e: + print(recipe.name, ': failed to parse cooking time ', str(e)) + + recipe.save() + + # Append the original import url if it exists + try: + if file['url'] != '': + step.instruction += '\n\nImported from: ' + file['url'] + step.save() + except Exception as e: + print(recipe.name, ': failed to import source url ', str(e)) + + # TODO: Parse Nutritional Information + + # Grab the original image that was used for the recipe + try: + if file['originalPicture']!= '': + response = requests.get(file['originalPicture']) + if imghdr.what(BytesIO(response.content)) != None: + self.import_recipe_image(recipe, BytesIO(response.content)) + else: + raise Exception("Original image failed to download.") + except Exception as e: + print(recipe.name, ': failed to import image ', str(e)) + + return recipe + + def get_file_from_recipe(self, recipe): + raise NotImplementedError('Method not implemented in storage integration') diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index 1276306769..19dde0ef35 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -19,6 +19,7 @@ from cookbook.integration.mealmaster import MealMaster from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.paprika import Paprika +from cookbook.integration.recettetek import RecetteTek from cookbook.integration.recipesage import RecipeSage from cookbook.integration.rezkonv import RezKonv from cookbook.integration.safron import Safron @@ -44,6 +45,8 @@ def get_integration(request, export_type): return Pepperplate(request, export_type) if export_type == ImportExportBase.DOMESTICA: return Domestica(request, export_type) + if export_type == ImportExportBase.RECETTETEK: + return RecetteTek(request, export_type) if export_type == ImportExportBase.RECIPESAGE: return RecipeSage(request, export_type) if export_type == ImportExportBase.REZKONV: From 1bc5af1caba0ca87318316ac372ac21f0196d583 Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Wed, 28 Apr 2021 13:44:19 +0100 Subject: [PATCH 2/6] import local image, download if fails. --- cookbook/integration/recettetek.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index c1bf490cef..505e2be0bc 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -90,17 +90,24 @@ def get_recipe_from_file(self, file): # TODO: Parse Nutritional Information - # Grab the original image that was used for the recipe + # Import the original image from the zip file, if we cannot do that, attempt to download it again. try: - if file['originalPicture']!= '': - response = requests.get(file['originalPicture']) - if imghdr.what(BytesIO(response.content)) != None: - self.import_recipe_image(recipe, BytesIO(response.content)) - else: - raise Exception("Original image failed to download.") + if file['pictures'][0] !='': + image_file_name = file['pictures'][0].split('/')[-1] + for f in self.files: + if '.rtk' in f['name']: + import_zip = ZipFile(f['file']) + self.import_recipe_image(recipe, BytesIO(import_zip.read(image_file_name))) + else: + if file['originalPicture'] != '': + response=requests.get(file['originalPicture']) + if imghdr.what(BytesIO(response.content)) != None: + self.import_recipe_image(recipe, BytesIO(response.content)) + else: + raise Exception("Original image failed to download.") except Exception as e: print(recipe.name, ': failed to import image ', str(e)) - + return recipe def get_file_from_recipe(self, recipe): From c710d42ccb9620df37f4f0c48026232754830bf5 Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Wed, 28 Apr 2021 17:50:05 +0100 Subject: [PATCH 3/6] Add the source url to description not step --- cookbook/integration/recettetek.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index 505e2be0bc..778cf66c2c 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -31,6 +31,9 @@ def get_recipe_from_file(self, file): # Create initial recipe with just a title and a decription recipe = Recipe.objects.create(name=file['title'], created_by=self.request.user, internal=True, space=self.request.space, ) + # set the description as an empty string for later use for the source URL, incase there is no description text. + recipe.description = '' + try: if file['description'] != '': recipe.description = file['description'].strip() @@ -80,11 +83,10 @@ def get_recipe_from_file(self, file): recipe.save() - # Append the original import url if it exists + # Append the original import url to the description if it exists try: if file['url'] != '': - step.instruction += '\n\nImported from: ' + file['url'] - step.save() + recipe.description += "\n\nOriginal Source: " + file['url'] except Exception as e: print(recipe.name, ': failed to import source url ', str(e)) From b9040cb3a4f700c79fa6034c2b6c2ec718542a7d Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Wed, 28 Apr 2021 18:41:26 +0100 Subject: [PATCH 4/6] parse the servings --- cookbook/integration/recettetek.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index 778cf66c2c..bf34828514 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -57,9 +57,13 @@ def get_recipe_from_file(self, file): recipe.steps.add(step) # Attempt to import prep/cooking times + # quick hack, this assumes only one number in the quantity field. try: if file['quantity'] != '': - recipe.servings = int(file['quantity']) + for item in file['quantity'].split(' '): + if item.isdigit(): + recipe.servings = int(item) + break except Exception as e: print(recipe.name, ': failed to parse quantity ', str(e)) From 5ef55303923670ba3500f943231216e150e4494f Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Fri, 30 Apr 2021 08:40:00 +0100 Subject: [PATCH 5/6] Move the source url back into the step - inline with other importers --- cookbook/integration/recettetek.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index bf34828514..3ebfd3b910 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -42,6 +42,14 @@ def get_recipe_from_file(self, file): step = Step.objects.create(instruction=file['instructions']) + # Append the original import url to the step (if it exists) + try: + if file['url'] != '': + step.instruction += '\n\nImported from: ' + file['url'] + step.save() + except Exception as e: + print(recipe.name, ': failed to import source url ', str(e)) + try: # Process the ingredients. Assumes 1 ingredient per line. for ingredient in file['ingredients'].split('\n'): @@ -87,13 +95,6 @@ def get_recipe_from_file(self, file): recipe.save() - # Append the original import url to the description if it exists - try: - if file['url'] != '': - recipe.description += "\n\nOriginal Source: " + file['url'] - except Exception as e: - print(recipe.name, ': failed to import source url ', str(e)) - # TODO: Parse Nutritional Information # Import the original image from the zip file, if we cannot do that, attempt to download it again. From 7cd6a7c2a6ca2ecb1cc3244363cbe061f4c6ffae Mon Sep 17 00:00:00 2001 From: its_me_gb Date: Sat, 1 May 2021 15:02:22 +0100 Subject: [PATCH 6/6] Import recipe keywords --- cookbook/integration/recettetek.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index 3ebfd3b910..e0cc848221 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -9,7 +9,7 @@ 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 +from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword class RecetteTek(Integration): @@ -95,6 +95,16 @@ def get_recipe_from_file(self, file): recipe.save() + # Import the recipe keywords + try: + if file['keywords'] != '': + for keyword in file['keywords'].split(';'): + k, created = Keyword.objects.get_or_create(name=keyword.strip(), space=self.request.space) + recipe.keywords.add(k) + recipe.save() + except Exception as e: + pass + # TODO: Parse Nutritional Information # Import the original image from the zip file, if we cannot do that, attempt to download it again.