From 5d1d6d42489857621c600d732a85bef329f316f4 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Mon, 8 Feb 2021 21:19:46 +0100 Subject: [PATCH] added mealie importer --- .idea/dictionaries/vabene1111_PC.xml | 1 + cookbook/forms.py | 3 +- cookbook/integration/mealie.py | 56 ++++++++++++++++++++++ cookbook/integration/nextcloud_cookbook.py | 4 +- cookbook/views/import_export.py | 3 ++ docs/features/import_export.md | 8 ++++ 6 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 cookbook/integration/mealie.py diff --git a/.idea/dictionaries/vabene1111_PC.xml b/.idea/dictionaries/vabene1111_PC.xml index cfd8a9e64..76c507690 100644 --- a/.idea/dictionaries/vabene1111_PC.xml +++ b/.idea/dictionaries/vabene1111_PC.xml @@ -5,6 +5,7 @@ csrftoken gunicorn ical + mealie traefik diff --git a/cookbook/forms.py b/cookbook/forms.py index 1e22b070d..9f6024394 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -135,8 +135,9 @@ class ImportExportBase(forms.Form): DEFAULT = 'DEFAULT' PAPRIKA = 'PAPRIKA' NEXTCLOUD = 'NEXTCLOUD' + MEALIE = 'MEALIE' - type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, _('Paprika')), (NEXTCLOUD, _('Nextcloud Cookbook')),)) + type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, _('Paprika')), (NEXTCLOUD, _('Nextcloud Cookbook')), (MEALIE, _('Mealie')),)) class ImportForm(ImportExportBase): diff --git a/cookbook/integration/mealie.py b/cookbook/integration/mealie.py new file mode 100644 index 000000000..1153791aa --- /dev/null +++ b/cookbook/integration/mealie.py @@ -0,0 +1,56 @@ +import json +import re +from io import BytesIO +from zipfile import ZipFile + +from rest_framework.renderers import JSONRenderer + +from cookbook.helper.ingredient_parser import parse +from cookbook.integration.integration import Integration +from cookbook.models import Recipe, Step, Food, Unit, Ingredient +from cookbook.serializer import RecipeExportSerializer + + +class Mealie(Integration): + + def import_file_name_filter(self, zip_info_object): + print("testing", zip_info_object.filename) + return re.match(r'^recipes/([A-Za-z\d-])+.json$', zip_info_object.filename) + + def get_recipe_from_file(self, file): + recipe_json = json.loads(file.getvalue().decode("utf-8")) + + recipe = Recipe.objects.create( + name=recipe_json['name'].strip(), description=recipe_json['description'].strip(), + created_by=self.request.user, internal=True) + + # TODO parse times (given in PT2H3M ) + + ingredients_added = False + for s in recipe_json['recipeInstructions']: + step = Step.objects.create( + instruction=s['text'] + ) + if not ingredients_added: + ingredients_added = True + + for ingredient in recipe_json['recipeIngredient']: + amount, unit, ingredient, note = parse(ingredient) + f, created = Food.objects.get_or_create(name=ingredient) + u, created = Unit.objects.get_or_create(name=unit) + step.ingredients.add(Ingredient.objects.create( + food=f, unit=u, amount=amount, note=note + )) + 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'^images/{recipe_json["slug"]}.jpg$', z.filename): + self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename))) + + return recipe + + def get_file_from_recipe(self, recipe): + raise NotImplementedError('Method not implemented in storage integration') diff --git a/cookbook/integration/nextcloud_cookbook.py b/cookbook/integration/nextcloud_cookbook.py index 872bb249a..80cdff3ca 100644 --- a/cookbook/integration/nextcloud_cookbook.py +++ b/cookbook/integration/nextcloud_cookbook.py @@ -54,6 +54,4 @@ class NextcloudCookbook(Integration): return recipe def get_file_from_recipe(self, recipe): - export = RecipeExportSerializer(recipe).data - - return 'recipe.json', JSONRenderer().render(export).decode("utf-8") + raise NotImplementedError('Method not implemented in storage integration') diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index f58cc2583..c7e98da6c 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -7,6 +7,7 @@ from django.utils.translation import gettext as _ from cookbook.forms import ExportForm, ImportForm, ImportExportBase from cookbook.helper.permission_helper import group_required from cookbook.integration.default import Default +from cookbook.integration.mealie import Mealie from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.paprika import Paprika from cookbook.models import Recipe @@ -19,6 +20,8 @@ def get_integration(request, export_type): return Paprika(request) if export_type == ImportExportBase.NEXTCLOUD: return NextcloudCookbook(request) + if export_type == ImportExportBase.MEALIE: + return Mealie(request) @group_required('user') diff --git a/docs/features/import_export.md b/docs/features/import_export.md index 683fd7a8a..650aec2a6 100644 --- a/docs/features/import_export.md +++ b/docs/features/import_export.md @@ -48,6 +48,14 @@ You will get a `Recipes.zip` file. Simply upload the file and choose the Nextclo └── full.jpg ``` +## Mealie +Mealie provides structured data similar to nextcloud. + +To migrate your recipes + +1. Go to you Mealie settings and create a new Backup +2. Download the backup by clicking on it and pressing download (this wasn't working for me, so I had to manually pull it from the server) +3. Upload the entire `.zip` file to the importer page and import everything ## Paprika Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format.