mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-23 18:29:23 -05:00
mealie importer options
This commit is contained in:
@@ -76,6 +76,11 @@ class ImportForm(ImportExportBase):
|
||||
files = MultipleFileField(required=True)
|
||||
duplicates = forms.BooleanField(help_text=_('To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'),
|
||||
required=False)
|
||||
meal_plans = forms.BooleanField(required=False)
|
||||
shopping_lists = forms.BooleanField(required=False)
|
||||
nutrition_per_serving = forms.BooleanField(required=False) # some managers (e.g. mealie) do not specify what the nutrition's relate to so we let the user choose
|
||||
|
||||
|
||||
class ExportForm(ImportExportBase):
|
||||
recipes = forms.ModelMultipleChoiceField(widget=MultiSelectWidget, queryset=Recipe.objects.none(), required=False)
|
||||
all = forms.BooleanField(required=False)
|
||||
|
||||
@@ -29,6 +29,10 @@ class Integration:
|
||||
import_log = None
|
||||
import_duplicates = False
|
||||
|
||||
import_meal_plans = True
|
||||
import_shopping_lists = True
|
||||
nutrition_per_servings = False
|
||||
|
||||
def __init__(self, request, export_type):
|
||||
"""
|
||||
Integration for importing and exporting recipes
|
||||
@@ -60,7 +64,10 @@ class Integration:
|
||||
space=request.space
|
||||
)
|
||||
|
||||
def do_export(self, recipes, el):
|
||||
def do_export(self, recipes, el, meal_plans=True, shopping_lists=True, nutrition_per_servings=False):
|
||||
self.import_meal_plans = meal_plans
|
||||
self.import_shopping_lists = shopping_lists
|
||||
self.nutrition_per_servings = nutrition_per_servings
|
||||
|
||||
with scope(space=self.request.space):
|
||||
el.total_recipes = len(recipes)
|
||||
|
||||
@@ -85,6 +85,7 @@ class Mealie1(Integration):
|
||||
units_dict[u['id']] = unit.pk
|
||||
|
||||
recipes_dict = {}
|
||||
recipe_property_factor_dict = {}
|
||||
recipes = []
|
||||
recipe_keyword_relation = []
|
||||
for r in mealie_database['recipes']:
|
||||
@@ -102,6 +103,9 @@ class Mealie1(Integration):
|
||||
created_by=self.request.user,
|
||||
)
|
||||
|
||||
if not self.nutrition_per_servings:
|
||||
recipe_property_factor_dict[r['id']] = recipe.servings
|
||||
|
||||
self.handle_duplicates(recipe, self.import_duplicates)
|
||||
self.import_log.msg += self.get_recipe_processed_msg(recipe)
|
||||
self.import_log.imported_recipes += 1
|
||||
@@ -211,10 +215,11 @@ class Mealie1(Integration):
|
||||
for r in mealie_database['recipe_nutrition']:
|
||||
for key in property_types_dict:
|
||||
if r[key]:
|
||||
# the mealie UI does not communicate to the user if nutrition's are per serving or recipe, expect per serving by default
|
||||
# TODO add option for user to choose between recipe and serving properties (use recipe_property_factor_dict with pre-calculated property factors)
|
||||
properties_relation.append(
|
||||
Property(property_type_id=property_types_dict[key].pk, property_amount=r[key], open_data_food_slug=r['recipe_id'], space=self.request.space))
|
||||
Property(property_type_id=property_types_dict[key].pk,
|
||||
property_amount=r[key] * recipe_property_factor_dict[r['recipe_id']] if r['recipe_id'] in recipe_property_factor_dict else 1,
|
||||
open_data_food_slug=r['recipe_id'],
|
||||
space=self.request.space))
|
||||
properties = Property.objects.bulk_create(properties_relation)
|
||||
property_ids = []
|
||||
for p in properties:
|
||||
@@ -255,54 +260,56 @@ 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()
|
||||
if self.import_meal_plans:
|
||||
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']:
|
||||
if not m['entry_type'] in meal_types_dict:
|
||||
meal_type = MealType.objects.get_or_create(name=m['entry_type'], created_by=self.request.user, space=self.request.space)[0]
|
||||
meal_types_dict[m['entry_type']] = meal_type.pk
|
||||
meal_plans.append(MealPlan(
|
||||
recipe_id=recipes_dict[m['recipe_id']] if m['recipe_id'] else None,
|
||||
title=m['title'] if m['title'] else "",
|
||||
note=m['text'] if m['text'] else "",
|
||||
from_date=m['date'],
|
||||
to_date=m['date'],
|
||||
meal_type_id=meal_types_dict[m['entry_type']],
|
||||
created_by=self.request.user,
|
||||
space=self.request.space,
|
||||
))
|
||||
meal_types_dict = {}
|
||||
meal_plans = []
|
||||
for m in mealie_database['group_meal_plans']:
|
||||
if not m['entry_type'] in meal_types_dict:
|
||||
meal_type = MealType.objects.get_or_create(name=m['entry_type'], created_by=self.request.user, space=self.request.space)[0]
|
||||
meal_types_dict[m['entry_type']] = meal_type.pk
|
||||
meal_plans.append(MealPlan(
|
||||
recipe_id=recipes_dict[m['recipe_id']] if m['recipe_id'] else None,
|
||||
title=m['title'] if m['title'] else "",
|
||||
note=m['text'] if m['text'] else "",
|
||||
from_date=m['date'],
|
||||
to_date=m['date'],
|
||||
meal_type_id=meal_types_dict[m['entry_type']],
|
||||
created_by=self.request.user,
|
||||
space=self.request.space,
|
||||
))
|
||||
|
||||
MealPlan.objects.bulk_create(meal_plans)
|
||||
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()
|
||||
if self.import_shopping_lists:
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
@@ -2280,7 +2280,13 @@ class AppImportView(APIView):
|
||||
files = []
|
||||
for f in request.FILES.getlist('files'):
|
||||
files.append({'file': io.BytesIO(f.read()), 'name': f.name})
|
||||
t = threading.Thread(target=integration.do_import, args=[files, il, form.cleaned_data['duplicates']])
|
||||
t = threading.Thread(target=integration.do_import,
|
||||
args=[files, il, form.cleaned_data['duplicates']],
|
||||
kwargs={'meal_plans': form.cleaned_data['meal_plans'],
|
||||
'shopping_lists': form.cleaned_data['shopping_lists'],
|
||||
'nutrition_per_serving': form.cleaned_data['nutrition_per_serving']
|
||||
}
|
||||
)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||
import {ref} from "vue";
|
||||
import {getCookie} from "@/utils/cookie";
|
||||
import {AiProvider, RecipeFromSourceResponseFromJSON, RecipeImageFromJSON, ResponseError, UserFile, UserFileFromJSON} from "@/openapi";
|
||||
import {tr} from "vuetify/locale";
|
||||
|
||||
|
||||
/**
|
||||
@@ -117,12 +118,18 @@ export function useFileApi() {
|
||||
* @param files array to import
|
||||
* @param app app to import
|
||||
* @param includeDuplicates if recipes that were found as duplicates should be imported as well
|
||||
* @param mealPlans if meal plans should be imported
|
||||
* @param shoppingLists if shopping lists should be imported
|
||||
* @param nutritionPerServing if nutrition information should be treated as per serving (if false its treated as per recipe)
|
||||
* @returns Promise resolving to the import ID of the app import
|
||||
*/
|
||||
function doAppImport(files: File[], app: string, includeDuplicates: boolean) {
|
||||
function doAppImport(files: File[], app: string, includeDuplicates: boolean, mealPlans: boolean = true, shoppingLists: boolean = true, nutritionPerServing: boolean = false,) {
|
||||
let formData = new FormData()
|
||||
formData.append('type', app);
|
||||
formData.append('duplicates', includeDuplicates ? 'true' : 'false')
|
||||
formData.append('meal_plans', mealPlans ? 'true' : 'false')
|
||||
formData.append('shopping_lists', shoppingLists ? 'true' : 'false')
|
||||
formData.append('nutrition_per_serving', nutritionPerServing ? 'true' : 'false')
|
||||
files.forEach(file => {
|
||||
formData.append('files', file)
|
||||
})
|
||||
@@ -141,4 +148,4 @@ export function useFileApi() {
|
||||
}
|
||||
|
||||
return {fileApiLoading, createOrUpdateUserFile, updateRecipeImage, doAiImport, doAppImport}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,6 +658,9 @@ const urlListImportedRecipes = ref([] as Recipe[])
|
||||
const sourceImportText = ref("")
|
||||
const appImportFiles = ref<File[]>([])
|
||||
const appImportDuplicates = ref(false)
|
||||
const appImportMealPlans = ref(true)
|
||||
const appImportShoppingLists = ref(true)
|
||||
const appImportNutritionsPerServing = ref(false)
|
||||
const appImportLog = ref<null | ImportLog>(null)
|
||||
const image = ref<null | File>(null)
|
||||
const aiMode = ref<'file' | 'text'>('file')
|
||||
@@ -770,7 +773,7 @@ function loadRecipeFromAiImport() {
|
||||
}
|
||||
|
||||
function appImport() {
|
||||
doAppImport(appImportFiles.value, importApp.value, appImportDuplicates.value).then(r => {
|
||||
doAppImport(appImportFiles.value, importApp.value, appImportDuplicates.value, appImportMealPlans.value, appImportShoppingLists.value, appImportNutritionsPerServing.value).then(r => {
|
||||
stepper.value = 'import_log'
|
||||
recLoadImportLog(r)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user