From 577af85d387a0184048d5f1a97ccb43c79b93477 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Mon, 29 Jun 2020 19:09:15 +0200 Subject: [PATCH] fixed import export --- cookbook/serializer.py | 11 ++- .../templates/forms/edit_internal_recipe.html | 25 ++++-- cookbook/views/import_export.py | 88 ++++++------------- 3 files changed, 54 insertions(+), 70 deletions(-) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index a04ed2b7..795b3835 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -42,7 +42,7 @@ class UserPreferenceSerializer(serializers.ModelSerializer): class StorageSerializer(serializers.ModelSerializer): class Meta: model = Storage - fields = ('name', 'method', 'username', 'created_by') + fields = ('id', 'name', 'method', 'username', 'created_by') class SyncSerializer(serializers.ModelSerializer): @@ -58,9 +58,16 @@ class SyncLogSerializer(serializers.ModelSerializer): class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer): + def create(self, validated_data): + # since multi select tags dont have id's duplicate names might be routed to create + obj, created = Keyword.objects.get_or_create(**validated_data) + return obj + class Meta: model = Keyword - fields = '__all__' + fields = ('id', 'name', 'icon', 'description', 'created_by', 'created_at', 'updated_at') + + read_only_fields = ('id',) class UnitSerializer(UniqueFieldsMixin, serializers.ModelSerializer): diff --git a/cookbook/templates/forms/edit_internal_recipe.html b/cookbook/templates/forms/edit_internal_recipe.html index d6587ec8..3d9b59ad 100644 --- a/cookbook/templates/forms/edit_internal_recipe.html +++ b/cookbook/templates/forms/edit_internal_recipe.html @@ -314,7 +314,9 @@ addIngredient: function (step) { //TODO see if default can be generated from options request step.ingredients.push({ 'food': undefined, - 'unit': undefined, + 'unit': { + 'name': '{{request.user.userpreference.default_unit}}' + }, 'amount': 0, 'note': '', 'order': 0, @@ -355,10 +357,13 @@ this.units_loading = true this.$http.get("{% url 'api:unit-list' %}" + '?query=' + query + '&limit=10').then((response) => { this.units = response.data; - for (let s of this.recipe.steps){ - for (let i of s.ingredients) { - if (i.unit.id === undefined) { - this.units.push(i.unit) + + if (this.recipe !== undefined) { + for (let s of this.recipe.steps) { + for (let i of s.ingredients) { + if (i.unit.id === undefined) { + this.units.push(i.unit) + } } } } @@ -372,10 +377,12 @@ this.$http.get("{% url 'api:food-list' %}" + '?query=' + query + '&limit=10').then((response) => { this.foods = response.data - for (let s of this.recipe.steps){ - for (let i of s.ingredients) { - if (i.food.id === undefined) { - this.foods.push(i.food) + if (this.recipe !== undefined) { + for (let s of this.recipe.steps) { + for (let i of s.ingredients) { + if (i.food.id === undefined) { + this.foods.push(i.food) + } } } } diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index 9bd8648d..c2eacb3c 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -1,18 +1,20 @@ import base64 import json import re +from json import JSONDecodeError from django.contrib import messages from django.core.files.base import ContentFile -from django.db import IntegrityError -from django.http import HttpResponseRedirect, JsonResponse, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render from django.urls import reverse_lazy - from django.utils.translation import gettext as _ +from rest_framework.renderers import JSONRenderer + from cookbook.forms import ExportForm, ImportForm from cookbook.helper.permission_helper import group_required -from cookbook.models import Ingredient, Recipe, Unit, Food, Keyword, Food +from cookbook.models import Recipe +from cookbook.serializer import RecipeSerializer @group_required('user') @@ -20,44 +22,28 @@ def import_recipe(request): if request.method == "POST": form = ImportForm(request.POST) if form.is_valid(): - data = json.loads(form.cleaned_data['recipe']) + try: + data = json.loads(form.cleaned_data['recipe']) - recipe = Recipe.objects.create(name=data['recipe']['name'], instructions=data['recipe']['instructions'], - working_time=data['recipe']['working_time'], waiting_time=data['recipe']['waiting_time'], - created_by=request.user, internal=True) + sr = RecipeSerializer(data=data) + if sr.is_valid(): + sr.validated_data['created_by'] = request.user + recipe = sr.save() - for k in data['keywords']: - try: - Keyword.objects.create(name=k['name'], icon=k['icon'], description=k['description']).save() - except IntegrityError: - pass + if data['image']: + fmt, img = data['image'].split(';base64,') + ext = fmt.split('/')[-1] + recipe.image = ContentFile(base64.b64decode(img), name=f'{recipe.pk}.{ext}') # TODO possible security risk, maybe some checks needed + recipe.save() - recipe.keywords.add(Keyword.objects.get(name=k['name'])) + messages.add_message(request, messages.SUCCESS, _('Recipe imported successfully!')) + return HttpResponseRedirect(reverse_lazy('view_recipe', args=[recipe.pk])) + else: + messages.add_message(request, messages.ERROR, _('Something went wrong during the import!')) + messages.add_message(request, messages.WARNING, sr.errors) + except JSONDecodeError: + messages.add_message(request, messages.ERROR, _('Could not parse the supplied JSON!')) - for u in data['units']: - try: - Unit.objects.create(name=u['name'], description=u['description']).save() - except IntegrityError: - pass - - for i in data['ingredients']: - try: - Food.objects.create(name=i['name']).save() - except IntegrityError: - pass - - for ri in data['recipe_ingredients']: - Ingredient.objects.create(recipe=recipe, ingredient=Food.objects.get(name=ri['food']), - unit=Unit.objects.get(name=ri['unit']), amount=ri['amount'], note=ri['note']) - - if data['image']: - fmt, img = data['image'].split(';base64,') - ext = fmt.split('/')[-1] - recipe.image = ContentFile(base64.b64decode(img), name=f'{recipe.pk}.{ext}') - recipe.save() - - messages.add_message(request, messages.SUCCESS, _('Recipe imported successfully!')) - return HttpResponseRedirect(reverse_lazy('view_recipe', args=[recipe.pk])) else: form = ImportForm() @@ -72,36 +58,20 @@ def export_recipe(request): if form.is_valid(): recipe = form.cleaned_data['recipe'] if recipe.internal: - export = { - 'recipe': {'name': recipe.name, 'instructions': recipe.instructions, 'working_time': recipe.working_time, 'waiting_time': recipe.working_time}, - 'units': [], - 'ingredients': [], - 'recipe_ingredients': [], - 'keywords': [], - 'image': None - } - - for k in recipe.keywords.all(): - export['keywords'].append({'name': k.name, 'icon': k.icon, 'description': k.description}) - - for ri in Ingredient.objects.filter(recipe=recipe).all(): - if ri.unit not in export['units']: - export['units'].append({'name': ri.unit.name, 'description': ri.unit.description}) - if ri.ingredient not in export['ingredients']: - export['ingredients'].append({'name': ri.ingredient.name}) - - export['recipe_ingredients'].append({'food': ri.ingredient.name, 'unit': ri.unit.name, 'amount': float(ri.amount), 'note': ri.note}) + export = RecipeSerializer(recipe).data if recipe.image and form.cleaned_data['image']: with open(recipe.image.path, 'rb') as img_f: export['image'] = f'data:image/png;base64,{base64.b64encode(img_f.read()).decode("utf-8")}' + json_string = JSONRenderer().render(export).decode("utf-8") + if form.cleaned_data['download']: - response = HttpResponse(json.dumps(export), content_type='text/plain') + response = HttpResponse(json_string, content_type='text/plain') response['Content-Disposition'] = f'attachment; filename={recipe.name}.json' return response - context['export'] = json.dumps(export, indent=4) + context['export'] = re.sub(r'"id":([0-9])+,', '', json_string) else: form.add_error('recipe', _('External recipes cannot be exported, please share the file directly or select an internal recipe.')) else: