From 13626ca11b6b552ed288faf6a338df262bcddf99 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Tue, 16 Sep 2025 07:48:58 +0200 Subject: [PATCH] mealie importer improvements --- cookbook/integration/integration.py | 9 ++- cookbook/integration/mealie1.py | 69 ++++++++++++++++++- .../components/display/ImportLogViewer.vue | 6 +- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index d23dc374b..130506108 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -26,6 +26,8 @@ class Integration: files = None export_type = None ignored_recipes = [] + import_log = None + import_duplicates = False def __init__(self, request, export_type): """ @@ -111,7 +113,8 @@ class Integration: :return: HttpResponseRedirect to the recipe search showing all imported recipes """ with scope(space=self.request.space): - + self.import_log = il + self.import_duplicates = import_duplicates try: self.files = files for f in files: @@ -169,10 +172,6 @@ class Integration: if isinstance(self, cookbook.integration.mealie1.Mealie1): # since the mealie 1.0 export is a backup and not a classic recipe export we treat it a bit differently recipes = self.get_recipe_from_file(import_zip) - for r in recipes: - self.handle_duplicates(r, import_duplicates) - il.imported_recipes += 1 - il.save() else: for z in file_list: try: diff --git a/cookbook/integration/mealie1.py b/cookbook/integration/mealie1.py index 6e6117dd8..59c86f3be 100644 --- a/cookbook/integration/mealie1.py +++ b/cookbook/integration/mealie1.py @@ -11,7 +11,7 @@ from cookbook.helper.image_processing import get_filetype from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.recipe_url_import import parse_servings, parse_servings_text, parse_time from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Keyword, Recipe, Step, Food, Unit, SupermarketCategory, PropertyType, Property, MealType, MealPlan, CookLog +from cookbook.models import Ingredient, Keyword, Recipe, Step, Food, Unit, SupermarketCategory, PropertyType, Property, MealType, MealPlan, CookLog, ShoppingListEntry class Mealie1(Integration): @@ -21,6 +21,9 @@ class Mealie1(Integration): def get_recipe_from_file(self, file): mealie_database = json.loads(BytesIO(file.read('database.json')).getvalue().decode("utf-8")) + self.import_log.total_recipes = len(mealie_database['recipes']) + self.import_log.msg += f"Importing {len(mealie_database["categories"]) + len(mealie_database["tags"])} tags and categories as keywords...\n" + self.import_log.save() keywords_categories_dict = {} for c in mealie_database['categories']: @@ -38,6 +41,9 @@ class Mealie1(Integration): keyword = Keyword.objects.create(name=t['name'], space=self.request.space) keywords_tags_dict[t['id']] = keyword.pk + self.import_log.msg +=f"Importing {len(mealie_database["multi_purpose_labels"])} multi purpose labels as supermarket categories...\n" + self.import_log.save() + supermarket_categories_dict = {} for m in mealie_database['multi_purpose_labels']: if supermarket_category := SupermarketCategory.objects.filter(name=m['name'], space=self.request.space).first(): @@ -46,6 +52,9 @@ class Mealie1(Integration): supermarket_category = SupermarketCategory.objects.create(name=m['name'], space=self.request.space) supermarket_categories_dict[m['id']] = supermarket_category.pk + self.import_log.msg +=f"Importing {len(mealie_database["ingredient_foods"])} foods...\n" + self.import_log.save() + foods_dict = {} for f in mealie_database['ingredient_foods']: if food := Food.objects.filter(name=f['name'], space=self.request.space).first(): @@ -64,6 +73,9 @@ class Mealie1(Integration): food.onhand_users.add(self.request.user) foods_dict[f['id']] = food.pk + self.import_log.msg +=f"Importing {len(mealie_database["ingredient_units"])} units...\n" + self.import_log.save() + units_dict = {} for u in mealie_database['ingredient_units']: if unit := Unit.objects.filter(name=u['name'], space=self.request.space).first(): @@ -74,6 +86,7 @@ class Mealie1(Integration): recipes_dict = {} recipes = [] + recipe_keyword_relation = [] for r in mealie_database['recipes']: recipe = Recipe.objects.create( waiting_time=parse_time(r['perform_time']), @@ -88,8 +101,20 @@ class Mealie1(Integration): space=self.request.space, created_by=self.request.user, ) + + self.handle_duplicates(recipe, self.import_duplicates) + self.import_log.msg += self.get_recipe_processed_msg(recipe) + self.import_log.imported_recipes += 1 + self.import_log.save() + recipes.append(recipe) recipes_dict[r['id']] = recipe.pk + recipe_keyword_relation.append(Recipe.keywords.through(recipe_id=recipe.pk, keyword_id=self.keyword.pk)) + + Recipe.keywords.through.objects.bulk_create(recipe_keyword_relation) + + self.import_log.msg +=f"Importing {len(mealie_database["recipe_instructions"])} instructions...\n" + self.import_log.save() steps_relation = [] first_step_of_recipe_dict = {} @@ -113,6 +138,9 @@ class Mealie1(Integration): ingredient_parser = IngredientParser(self.request, True) + self.import_log.msg +=f"Importing {len(mealie_database["recipes_ingredients"])} ingredients...\n" + self.import_log.save() + ingredients_relation = [] for i in mealie_database['recipes_ingredients']: if i['title']: @@ -148,6 +176,9 @@ class Mealie1(Integration): ingredients_relation.append(Step.ingredients.through(step_id=first_step_of_recipe_dict[i['recipe_id']], ingredient_id=ingredient.pk)) Step.ingredients.through.objects.bulk_create(ingredients_relation) + self.import_log.msg += f"Importing {len(mealie_database["recipes_to_categories"]) + len(mealie_database["recipes_to_tags"])} category and keyword relations...\n" + self.import_log.save() + recipe_keyword_relation = [] for rC in mealie_database['recipes_to_categories']: recipe_keyword_relation.append(Recipe.keywords.through(recipe_id=recipes_dict[rC['recipe_id']], keyword_id=keywords_categories_dict[rC['category_id']])) @@ -157,6 +188,9 @@ class Mealie1(Integration): Recipe.keywords.through.objects.bulk_create(recipe_keyword_relation, ignore_conflicts=True) + self.import_log.msg += f"Importing {len(mealie_database["recipe_nutrition"]) } properties...\n" + self.import_log.save() + property_types_dict = { 'calories': PropertyType.objects.get_or_create(name=_('Calories'), space=self.request.space, defaults={'unit': 'kcal', 'fdc_id': 1008})[0], 'carbohydrate_content': PropertyType.objects.get_or_create(name=_('Carbohydrates'), space=self.request.space, defaults={'unit': 'g', 'fdc_id': 1005})[0], @@ -196,6 +230,9 @@ class Mealie1(Integration): except: pass + self.import_log.msg += f"Importing {len(mealie_database["recipe_comments"]) + len(mealie_database["recipe_timeline_events"])} comments and cook logs...\n" + self.import_log.save() + cook_log_list = [] for c in mealie_database['recipe_comments']: cook_log_list.append(CookLog( @@ -218,6 +255,9 @@ class Mealie1(Integration): CookLog.objects.bulk_create(cook_log_list) + self.import_log.msg += f"Importing {len(mealie_database["group_meal_plans"])} meal plans...\n" + self.import_log.save() + meal_types_dict = {} meal_plans = [] for m in mealie_database['group_meal_plans']: @@ -237,6 +277,33 @@ class Mealie1(Integration): MealPlan.objects.bulk_create(meal_plans) + self.import_log.msg += f"Importing {len(mealie_database["shopping_list_items"])} shopping list items...\n" + self.import_log.save() + + shopping_list_items = [] + for sli in mealie_database['shopping_list_items']: + if not sli['checked']: + if sli['food_id']: + shopping_list_items.append(ShoppingListEntry( + amount=sli['quantity'], + unit_id=units_dict[sli['unit_id']] if sli['unit_id'] else None, + food_id=foods_dict[sli['food_id']] if sli['food_id'] else None, + created_by=self.request.user, + space=self.request.space, + )) + elif not sli['food_id'] and sli['note'].strip(): + amount, unit, food, note = ingredient_parser.parse(sli['note'].strip()) + f = ingredient_parser.get_food(food) + u = ingredient_parser.get_unit(unit) + shopping_list_items.append(ShoppingListEntry( + amount=amount, + unit=u, + food=f, + created_by=self.request.user, + space=self.request.space, + )) + ShoppingListEntry.objects.bulk_create(shopping_list_items) + return recipes def get_file_from_recipe(self, recipe): diff --git a/vue3/src/components/display/ImportLogViewer.vue b/vue3/src/components/display/ImportLogViewer.vue index 4cb0b5a07..ffb60d0ae 100644 --- a/vue3/src/components/display/ImportLogViewer.vue +++ b/vue3/src/components/display/ImportLogViewer.vue @@ -1,6 +1,6 @@