From fe1ddf12376548acee2bba28b98447dd50eb0bae Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Thu, 18 Mar 2021 14:43:19 +0100 Subject: [PATCH] improved paprika importer --- cookbook/integration/integration.py | 2 +- cookbook/integration/paprika.py | 53 ++++++++++++----------------- docs/features/import_export.md | 38 +++++++++++---------- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index 49200d54..08ad6ad2 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -84,7 +84,7 @@ class Integration: try: self.files = files for f in files: - if '.zip' in f.name: + if '.zip' in f.name or '.paprikarecipes' in f.name: import_zip = ZipFile(f.file) for z in import_zip.filelist: if self.import_file_name_filter(z): diff --git a/cookbook/integration/paprika.py b/cookbook/integration/paprika.py index 596b0ba2..26fd6b86 100644 --- a/cookbook/integration/paprika.py +++ b/cookbook/integration/paprika.py @@ -1,3 +1,4 @@ +import base64 import json import re from io import BytesIO @@ -6,51 +7,39 @@ from zipfile import ZipFile import microdata from bs4 import BeautifulSoup +from cookbook.helper.ingredient_parser import parse 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 class Paprika(Integration): - def import_file_name_filter(self, zip_info_object): - print("testing", zip_info_object.filename) - return re.match(r'^Recipes/([A-Za-z\s])+.html$', zip_info_object.filename) - def get_file_from_recipe(self, recipe): raise NotImplementedError('Method not implemented in storage integration') def get_recipe_from_file(self, file): - html_text = file.getvalue().decode("utf-8") + with gzip.open(file, 'r') as recipe_zip: + recipe_json = json.loads(recipe_zip.read().decode("utf-8")) - items = microdata.get_items(html_text) - for i in items: - md_json = json.loads(i.json()) - if 'schema.org/Recipe' in str(md_json['type']): - recipe_json = find_recipe_json(md_json['properties'], '', space=self.request.space) - recipe = Recipe.objects.create(name=recipe_json['name'].strip(), created_by=self.request.user, internal=True, space=self.request.space) - step = Step.objects.create( - instruction=recipe_json['recipeInstructions'] - ) + recipe = Recipe.objects.create( + name=recipe_json['name'].strip(), description=recipe_json['description'].strip(), + created_by=self.request.user, internal=True, space=self.request.space) - for ingredient in recipe_json['recipeIngredient']: - f, created = Food.objects.get_or_create(name=ingredient['ingredient']['text'], space=self.request.space) - u, created = Unit.objects.get_or_create(name=ingredient['unit']['text'], space=self.request.space) - step.ingredients.add(Ingredient.objects.create( - food=f, unit=u, amount=ingredient['amount'], note=ingredient['note'] - )) + step = Step.objects.create( + instruction=recipe_json['directions'] + '\n\n' + recipe_json['nutritional_info'] + ) - recipe.steps.add(step) + for ingredient in recipe_json['ingredients'].split('\n'): + amount, unit, ingredient, note = parse(ingredient) + f, created = Food.objects.get_or_create(name=ingredient, space=self.request.space) + u, created = Unit.objects.get_or_create(name=unit, space=self.request.space) + step.ingredients.add(Ingredient.objects.create( + food=f, unit=u, amount=amount, note=note + )) - soup = BeautifulSoup(html_text, "html.parser") - image = soup.find('img') - image_name = image.attrs['src'].strip().replace('Images/', '') + recipe.steps.add(step) - 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'^Recipes/Images/{image_name}$', z.filename): - self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename))) - - return recipe + self.import_recipe_image(recipe, BytesIO(base64.b64decode(recipe_json['photo_data']))) + return recipe diff --git a/docs/features/import_export.md b/docs/features/import_export.md index 069687eb..2c9f786b 100644 --- a/docs/features/import_export.md +++ b/docs/features/import_export.md @@ -1,6 +1,10 @@ This application features a very versatile import and export feature in order to offer the best experience possible and allow you to freely choose where your data goes. +!!! warning "WIP" + The Module is relatively new. There is a know issue with [Timeouts](https://github.com/vabene1111/recipes/issues/417) on large exports. + A fix is being developed and will likely be released with the next version. + The Module is build with maximum flexibility and expandability in mind and allows to easily add new integrations to allow you to both import and export your recipes into whatever format you desire. @@ -14,6 +18,19 @@ if your favorite one is missing. Because of this importing as many formats as possible is prioritized over exporting. Exporter for the different formats will follow over time. +Overview of the capabilities of the different integrations. + +| Integration | Import | Export | Images | +| ----------- | ------ | ------ | ------ | +| Default | ✔️ | ✔️ | ✔️ | +| Nextcloud | ✔️ | ⌚ | ✔️ | +| Mealie | ✔️ | ⌚ | ✔️ | +| Chowdown | ✔️ | ⌚ | ✔️ | +| Safron | ✔️ | ⌚ | ❌ | +| Paprika | ✔️ | ⌚ | ✔️ | + +✔ = implemented, ❌ = not implemented and not possible, ⌚ = not yet implemented + ## Default The default integration is the build in (and preferred) way to import and export recipes. It is maintained with new fields added and contains all data to transfer your recipes from one installation to another. @@ -88,22 +105,7 @@ Then simply upload the entire `.zip` file to the importer. Safron exports do not contain any images. They will be lost during import. ## Paprika -Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format. -The second one is HTML files containing at least a bit of microdata. +A Paprika export contains a folder with a html representation of your recipes and a `.paprikarecipes` file. -If you want to import your Paprika recipes follow these steps - -1. create a html export -2. Create a `.zip` file from the `Recipes` folder (next to the `index.html`) the structure should look like this -``` -Recipes.zip/ - └── Recipes/ - ├── recipe one.html - ├── recipe two.thml - └── Images/ - ├── 5D5E09CD-8F88-4F61-8121-0727DD3E0E89/ - │ └── 5D5E09CD-8F88-4F61-8121-0727DD3E0E89.jpg - └── 7CEE2AC6-DF60-4464-9B61-4F5E347EB90C/ - └── 7CEE2AC6-DF60-4464-9B61-4F5E347EB90C.jpg -``` -3. Upload the zip file in the import module and import it \ No newline at end of file +The `.paprikarecipes` file is basically just a zip with gzipped contents. Simply upload the whole file and import +all your recipes. \ No newline at end of file