diff --git a/cookbook/forms.py b/cookbook/forms.py index 5f19329fc..39a230e46 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -114,13 +114,14 @@ class ImportExportBase(forms.Form): PEPPERPLATE = 'PEPPERPLATE' RECIPESAGE = 'RECIPESAGE' DOMESTICA = 'DOMESTICA' + MEALMASTER = 'MEALMASTER' 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'), + (MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), )) diff --git a/cookbook/integration/mealmaster.py b/cookbook/integration/mealmaster.py new file mode 100644 index 000000000..cb3ca6115 --- /dev/null +++ b/cookbook/integration/mealmaster.py @@ -0,0 +1,82 @@ +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 MealMaster(Integration): + + def get_recipe_from_file(self, file): + print('------------ getting recipe') + servings = 1 + ingredients = [] + directions = [] + for line in file.replace('\r', '').split('\n'): + print('testing line') + if not line.startswith('MMMMM') and line.strip != '': + if 'Title:' in line: + title = line.replace('Title:', '').strip() + else: + if 'Categories:' in line: + tags = line.replace('Categories:', '').strip() + else: + if 'Yield:' in line: + servings_text = line.replace('Yield:', '').strip() + else: + if re.match('\s{2,}([0-9])+', line): + ingredients.append(line.strip()) + else: + directions.append(line.strip()) + + try: + servings = re.findall('([0-9])+', servings_text)[0] + except Exception as e: + print('failed parsing servings ', e) + + recipe = Recipe.objects.create(name=title, servings=servings, 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('MMMMM') or line.startswith('-----')) and 'meal-master' 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 1058bf391..6b57bb08f 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -16,6 +16,7 @@ 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.mealmaster import MealMaster from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.paprika import Paprika from cookbook.integration.recipesage import RecipeSage @@ -47,6 +48,8 @@ def get_integration(request, export_type): return RecipeSage(request, export_type) if export_type == ImportExportBase.REZKONV: return RezKonv(request, export_type) + if export_type == ImportExportBase.MEALMASTER: + return MealMaster(request, export_type) @group_required('user') diff --git a/docs/features/import_export.md b/docs/features/import_export.md index 3e5425b8a..c44970104 100644 --- a/docs/features/import_export.md +++ b/docs/features/import_export.md @@ -32,6 +32,7 @@ Overview of the capabilities of the different integrations. | Pepperplate | ✔️ | ⌚ | ❌ | | RecipeSage | ✔️ | ✔️ | ✔️ | | Domestica | ✔️ | ⌚ | ✔️ | +| MealMaster | ✔️ | ❌ | ❌ | | RezKonv | ✔️ | ❌ | ❌ | ✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented @@ -146,7 +147,17 @@ or is worse than this feel free to provide me with more example data and I can t As ChefTap cannot import these files anyway there won't be an exporter implemented in Tandoor. +## MealMaster +Meal master can be imported by uploading one or more meal master files. +The files should either be `.txt`, `.MMF` or `.MM` files. + +The MealMaster spec allow for many variations. Currently, only the on column format for ingredients is supported. +Second line notes to ingredients are currently also not imported as a note but simply put into the instructions. +If you have MealMaster recipes that cannot be imported feel free to raise an issue. + ## 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 +The generated file can simply be imported into Tandoor. + +As i only had limited sample data feel free to open an issue if your RezKonv export cannot be imported. \ No newline at end of file