wip on new import/export system

This commit is contained in:
vabene1111
2021-02-07 13:27:15 +01:00
parent b0fe98c091
commit d45adc1688
8 changed files with 239 additions and 40 deletions

View File

@ -153,6 +153,20 @@ class ImportForm(forms.Form):
) )
class ImportExportBase(forms.Form):
DEFAULT = 'Default'
type = forms.ChoiceField(choices=((DEFAULT, _('Default')),))
class NewImportForm(ImportExportBase):
files = forms.FileField(required=True, widget=forms.ClearableFileInput(attrs={'multiple': True}))
class NewExportForm(ImportExportBase):
recipes = forms.ModelMultipleChoiceField(queryset=Recipe.objects.filter(internal=True).all(), widget=MultiSelectWidget)
class UnitMergeForm(forms.Form): class UnitMergeForm(forms.Form):
prefix = 'unit' prefix = 'unit'

View File

@ -0,0 +1,43 @@
import json
import os
from zipfile import ZipFile
from rest_framework.renderers import JSONRenderer
from cookbook.integration.integration import Integration
from cookbook.serializer import RecipeExportSerializer
class Default(Integration):
def do_export(self, recipes):
path = self.get_tmp_dir_path()
export_zip_obj = ZipFile(os.path.join(path, 'export.zip'), 'w')
for r in recipes:
if r.internal:
base_path = os.path.join(path, str(r.pk))
os.makedirs(base_path, exist_ok=True)
recipe_zip_obj = ZipFile(base_path + '.zip', 'w')
f = open(os.path.join(path, str(r.pk), 'recipe.json'), "w", encoding="utf-8")
f.write(self.get_export(r))
recipe_zip_obj.write(f.name)
recipe_zip_obj.write(r.image.path)
f.close()
recipe_zip_obj.close()
export_zip_obj.write(recipe_zip_obj.filename)
export_zip_obj.close()
return export_zip_obj.filename
def get_recipe(self, string):
data = json.loads(string)
return RecipeExportSerializer(data=data, context={'request': self.request})
def get_export(self, recipe):
export = RecipeExportSerializer(recipe).data
return JSONRenderer().render(export).decode("utf-8")

View File

@ -1,8 +1,36 @@
import os
import tempfile
class Integration: class Integration:
@staticmethod request = None
def get_recipe(string):
def __init__(self, request):
self.request = request
def do_export(self, recipes):
raise Exception('Method not implemented in storage integration') raise Exception('Method not implemented in storage integration')
@staticmethod def do_import(self):
def get_export(recipe):
raise Exception('Method not implemented in storage integration') raise Exception('Method not implemented in storage integration')
def get_recipe(self, string):
raise Exception('Method not implemented in storage integration')
def get_export(self, recipe):
raise Exception('Method not implemented in storage integration')
def get_export_file(self, recipe):
try:
with open(recipe.image.path, 'rb') as img_f:
return img_f
except:
return None
def get_tmp_dir_path(self):
path = os.path.join(tempfile.gettempdir(), 'recipe_io', str(self.request.user.pk))
os.makedirs(path, exist_ok=True)
return path
def delete_temp_dir_path(self):
os.remove(self.get_tmp_dir_path())

View File

@ -113,7 +113,7 @@ class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
# since multi select tags dont have id's # since multi select tags dont have id's
# duplicate names might be routed to create # duplicate names might be routed to create
obj, created = Keyword.objects.get_or_create(**validated_data) obj, created = Keyword.objects.get_or_create(name=validated_data['name'])
return obj return obj
class Meta: class Meta:
@ -131,7 +131,7 @@ class UnitSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
# since multi select tags dont have id's # since multi select tags dont have id's
# duplicate names might be routed to create # duplicate names might be routed to create
obj, created = Unit.objects.get_or_create(**validated_data) obj, created = Unit.objects.get_or_create(name=validated_data['name'])
return obj return obj
class Meta: class Meta:
@ -145,7 +145,7 @@ class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerial
def create(self, validated_data): def create(self, validated_data):
# since multi select tags dont have id's # since multi select tags dont have id's
# duplicate names might be routed to create # duplicate names might be routed to create
obj, created = SupermarketCategory.objects.get_or_create(**validated_data) obj, created = SupermarketCategory.objects.get_or_create(name=validated_data['name'])
return obj return obj
def update(self, instance, validated_data): def update(self, instance, validated_data):
@ -221,17 +221,6 @@ class StepSerializer(WritableNestedModelSerializer):
) )
# used for the import export. temporary workaround until that module is finally fixed
class StepExportSerializer(WritableNestedModelSerializer):
ingredients = IngredientSerializer(many=True)
class Meta:
model = Step
fields = (
'id', 'name', 'type', 'instruction', 'ingredients', 'time', 'order', 'show_as_header'
)
class NutritionInformationSerializer(serializers.ModelSerializer): class NutritionInformationSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = NutritionInformation model = NutritionInformation
@ -270,11 +259,6 @@ class RecipeSerializer(WritableNestedModelSerializer):
return super().create(validated_data) return super().create(validated_data)
# used for the import export. temporary workaround until that module is finally fixed
class RecipeExportSerializer(RecipeSerializer):
steps = StepExportSerializer(many=True)
class RecipeImageSerializer(WritableNestedModelSerializer): class RecipeImageSerializer(WritableNestedModelSerializer):
class Meta: class Meta:
model = Recipe model = Recipe
@ -403,3 +387,72 @@ class ViewLogSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ViewLog model = ViewLog
fields = '__all__' fields = '__all__'
# Export/Import Serializers
class KeywordExportSerializer(KeywordSerializer):
class Meta:
model = Keyword
fields = ('name', 'icon', 'description', 'created_at', 'updated_at')
class NutritionInformationExportSerializer(NutritionInformationSerializer):
class Meta:
model = NutritionInformation
fields = ('carbohydrates', 'fats', 'proteins', 'calories', 'source')
class SupermarketCategoryExportSerializer(SupermarketCategorySerializer):
class Meta:
model = SupermarketCategory
fields = ('name',)
class UnitExportSerializer(UnitSerializer):
class Meta:
model = Unit
fields = ('name', 'description')
class FoodExportSerializer(FoodSerializer):
supermarket_category = SupermarketCategoryExportSerializer(allow_null=True, required=False)
class Meta:
model = Food
fields = ('name', 'ignore_shopping', 'supermarket_category')
class IngredientExportSerializer(WritableNestedModelSerializer):
food = FoodExportSerializer(allow_null=True)
unit = UnitExportSerializer(allow_null=True)
amount = CustomDecimalField()
class Meta:
model = Ingredient
fields = ('food', 'unit', 'amount', 'note', 'order', 'is_header', 'no_amount')
class StepExportSerializer(WritableNestedModelSerializer):
ingredients = IngredientExportSerializer(many=True)
class Meta:
model = Step
fields = ('name', 'type', 'instruction', 'ingredients', 'time', 'order', 'show_as_header')
class RecipeExportSerializer(WritableNestedModelSerializer):
nutrition = NutritionInformationSerializer(allow_null=True, required=False)
steps = StepExportSerializer(many=True)
keywords = KeywordExportSerializer(many=True)
class Meta:
model = Recipe
fields = (
'name', 'description', 'keywords', 'steps', 'working_time',
'waiting_time', 'internal', 'nutrition', 'servings', 'servings_text',
)
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)

View File

@ -1,11 +1,26 @@
<script type="application/javascript"> {% extends "base.html" %}
window.addEventListener("load", () => { {% load crispy_forms_filters %}
if ("serviceWorker" in navigator) { {% load i18n %}
navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: '/'}).then(function (reg) { {% load static %}
console.warn('Successfully registered service worker', err);
}).catch(function (err) { {% block title %}{% trans 'Import Recipes' %}{% endblock %}
console.warn('Error whilst registering service worker', err);
}); {% block extra_head %}
} {{ form.media }}
}); {% endblock %}
</script>
{% block content %}
<h2>{% trans 'Import' %}</h2>
<div class="row">
<div class="col col-md-12">
<form action="." method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit"><i class="fas fa-file-import"></i> {% trans 'Import' %}
</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n %}
{% load static %}
{% block title %}{% trans 'Export Recipes' %}{% endblock %}
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block content %}
<h2>{% trans 'Export' %}</h2>
<div class="row">
<div class="col col-md-12">
<form action="." method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit"><i class="fas fa-file-export"></i> {% trans 'Export' %}
</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -51,7 +51,8 @@ urlpatterns = [
path('shopping/latest/', views.latest_shopping_list, name='view_shopping_latest'), path('shopping/latest/', views.latest_shopping_list, name='view_shopping_latest'),
path('settings/', views.user_settings, name='view_settings'), path('settings/', views.user_settings, name='view_settings'),
path('history/', views.history, name='view_history'), path('history/', views.history, name='view_history'),
path('test/<int:pk>', views.test, name='view_test'), path('test/', views.test, name='view_test'),
path('test2/', views.test2, name='view_test2'),
path('import/', import_export.import_recipe, name='view_import'), path('import/', import_export.import_recipe, name='view_import'),
path('export/', import_export.export_recipe, name='view_export'), path('export/', import_export.export_recipe, name='view_export'),

View File

@ -11,7 +11,7 @@ from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import Avg, Q from django.db.models import Avg, Q
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect, FileResponse, HttpResponse
from django.shortcuts import get_object_or_404, render, redirect from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
@ -22,8 +22,9 @@ from rest_framework.authtoken.models import Token
from cookbook.filters import RecipeFilter from cookbook.filters import RecipeFilter
from cookbook.forms import (CommentForm, Recipe, RecipeBookEntryForm, User, from cookbook.forms import (CommentForm, Recipe, RecipeBookEntryForm, User,
UserCreateForm, UserNameForm, UserPreference, UserCreateForm, UserNameForm, UserPreference,
UserPreferenceForm) UserPreferenceForm, ImportForm, NewImportForm, NewExportForm)
from cookbook.helper.permission_helper import group_required, share_link_valid, has_group_permission from cookbook.helper.permission_helper import group_required, share_link_valid, has_group_permission
from cookbook.integration.default import Default
from cookbook.models import (Comment, CookLog, InviteLink, MealPlan, from cookbook.models import (Comment, CookLog, InviteLink, MealPlan,
RecipeBook, RecipeBookEntry, ViewLog, ShoppingList) RecipeBook, RecipeBookEntry, ViewLog, ShoppingList)
from cookbook.tables import (CookLogTable, RecipeTable, RecipeTableSmall, from cookbook.tables import (CookLogTable, RecipeTable, RecipeTableSmall,
@ -485,10 +486,29 @@ def offline(request):
return render(request, 'offline.html', {}) return render(request, 'offline.html', {})
def test(request, pk): def test(request):
if not settings.DEBUG: if not settings.DEBUG:
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
recipe = Recipe.objects.get(pk=pk) if request.method == "POST":
form = NewImportForm(request.POST)
else:
form = NewImportForm()
return render(request, 'test.html', {'recipe': recipe}) return render(request, 'test.html', {'form': form})
def test2(request):
if not settings.DEBUG:
return HttpResponseRedirect(reverse('index'))
if request.method == "POST":
form = NewExportForm(request.POST)
if form.is_valid():
integration = Default(request)
integration.do_export(form.cleaned_data['recipes'])
return render(request, 'test2.html', {'form': form})
else:
form = NewExportForm()
return render(request, 'test2.html', {'form': form})