diff --git a/.directory b/.directory
new file mode 100644
index 00000000..280ecee9
--- /dev/null
+++ b/.directory
@@ -0,0 +1,3 @@
+[Dolphin]
+Timestamp=2022,1,7,19,23,46.14
+Version=4
diff --git a/.env.template b/.env.template
index 0a56eec6..e825cbbf 100644
--- a/.env.template
+++ b/.env.template
@@ -151,3 +151,7 @@ REVERSE_PROXY_AUTH=0
# Enables exporting PDF (see export docs)
# Disabled by default, uncomment to enable
# ENABLE_PDF_EXPORT=1
+
+# Duration to keep the cached export file (in seconds)
+EXPORT_FILE_CACHE_DURATION=300
+
diff --git a/cookbook/integration/default.py b/cookbook/integration/default.py
index 39c0bc66..951a5312 100644
--- a/cookbook/integration/default.py
+++ b/cookbook/integration/default.py
@@ -32,11 +32,12 @@ class Default(Integration):
return None
def get_file_from_recipe(self, recipe):
+
export = RecipeExportSerializer(recipe).data
return 'recipe.json', JSONRenderer().render(export).decode("utf-8")
- def get_files_from_recipes(self, recipes, cookie):
+ def get_files_from_recipes(self, recipes, el, cookie):
export_zip_stream = BytesIO()
export_zip_obj = ZipFile(export_zip_stream, 'w')
@@ -50,13 +51,20 @@ class Default(Integration):
recipe_stream.write(data)
recipe_zip_obj.writestr(filename, recipe_stream.getvalue())
recipe_stream.close()
+
try:
recipe_zip_obj.writestr(f'image{get_filetype(r.image.file.name)}', r.image.file.read())
except ValueError:
pass
recipe_zip_obj.close()
+
export_zip_obj.writestr(str(r.pk) + '.zip', recipe_zip_stream.getvalue())
+
+ el.exported_recipes += 1
+ el.msg += self.get_recipe_processed_msg(r)
+ el.save()
+
export_zip_obj.close()
- return [[ 'export.zip', export_zip_stream.getvalue() ]]
\ No newline at end of file
+ return [[ self.get_export_file_name(), export_zip_stream.getvalue() ]]
\ No newline at end of file
diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py
index 6fee602c..48899d36 100644
--- a/cookbook/integration/integration.py
+++ b/cookbook/integration/integration.py
@@ -1,9 +1,12 @@
+import time
import datetime
import json
import traceback
import uuid
from io import BytesIO, StringIO
from zipfile import BadZipFile, ZipFile
+from django.core.cache import cache
+import datetime
from bs4 import Tag
from django.core.exceptions import ObjectDoesNotExist
@@ -18,6 +21,7 @@ from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype, handle_image
from cookbook.models import Keyword, Recipe
from recipes.settings import DEBUG
+from recipes.settings import EXPORT_FILE_CACHE_DURATION
class Integration:
@@ -61,35 +65,44 @@ class Integration:
space=request.space
)
- def do_export(self, recipes):
- """
- Perform the export based on a list of recipes
- :param recipes: list of recipe objects
- :return: HttpResponse with the file of the requested export format that is directly downloaded (When that format involve multiple files they are zipped together)
- """
- files = self.get_files_from_recipes(recipes, self.request.COOKIES)
- if len(files) == 1:
- filename, file = files[0]
- export_filename = filename
- export_file = file
+ def do_export(self, recipes, el):
- else:
- export_filename = "export.zip"
- export_stream = BytesIO()
- export_obj = ZipFile(export_stream, 'w')
+ with scope(space=self.request.space):
+ el.total_recipes = len(recipes)
+ el.cache_duration = EXPORT_FILE_CACHE_DURATION
+ el.save()
- for filename, file in files:
- export_obj.writestr(filename, file)
+ files = self.get_files_from_recipes(recipes, el, self.request.COOKIES)
- export_obj.close()
- export_file = export_stream.getvalue()
+ if len(files) == 1:
+ filename, file = files[0]
+ export_filename = filename
+ export_file = file
+
+ else:
+ #zip the files if there is more then one file
+ export_filename = self.get_export_file_name()
+ export_stream = BytesIO()
+ export_obj = ZipFile(export_stream, 'w')
+
+ for filename, file in files:
+ export_obj.writestr(filename, file)
+
+ export_obj.close()
+ export_file = export_stream.getvalue()
+
+
+ cache.set('export_file_'+str(el.pk), {'filename': export_filename, 'file': export_file}, EXPORT_FILE_CACHE_DURATION)
+ el.running = False
+ el.save()
response = HttpResponse(export_file, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="' + export_filename + '"'
return response
+
def import_file_name_filter(self, zip_info_object):
"""
Since zipfile.namelist() returns all files in all subdirectories this function allows filtering of files
@@ -126,7 +139,7 @@ class Integration:
for d in data_list:
recipe = self.get_recipe_from_file(d)
recipe.keywords.add(self.keyword)
- il.msg += f'{recipe.pk} - {recipe.name} \n'
+ il.msg += self.get_recipe_processed_msg(recipe)
self.handle_duplicates(recipe, import_duplicates)
il.imported_recipes += 1
il.save()
@@ -151,7 +164,7 @@ class Integration:
else:
recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename)))
recipe.keywords.add(self.keyword)
- il.msg += f'{recipe.pk} - {recipe.name} \n'
+ il.msg += self.get_recipe_processed_msg(recipe)
self.handle_duplicates(recipe, import_duplicates)
il.imported_recipes += 1
il.save()
@@ -166,7 +179,7 @@ class Integration:
try:
recipe = self.get_recipe_from_file(d)
recipe.keywords.add(self.keyword)
- il.msg += f'{recipe.pk} - {recipe.name} \n'
+ il.msg += self.get_recipe_processed_msg(recipe)
self.handle_duplicates(recipe, import_duplicates)
il.imported_recipes += 1
il.save()
@@ -183,7 +196,7 @@ class Integration:
try:
recipe = self.get_recipe_from_file(d)
recipe.keywords.add(self.keyword)
- il.msg += f'{recipe.pk} - {recipe.name} \n'
+ il.msg += self.get_recipe_processed_msg(recipe)
self.handle_duplicates(recipe, import_duplicates)
il.imported_recipes += 1
il.save()
@@ -193,7 +206,7 @@ class Integration:
else:
recipe = self.get_recipe_from_file(f['file'])
recipe.keywords.add(self.keyword)
- il.msg += f'{recipe.pk} - {recipe.name} \n'
+ il.msg += self.get_recipe_processed_msg(recipe)
self.handle_duplicates(recipe, import_duplicates)
except BadZipFile:
il.msg += 'ERROR ' + _(
@@ -260,7 +273,7 @@ class Integration:
"""
raise NotImplementedError('Method not implemented in integration')
- def get_files_from_recipes(self, recipes, cookie):
+ def get_files_from_recipes(self, recipes, el, cookie):
"""
Takes a list of recipe object and converts it to a array containing each file.
Each file is represented as an array [filename, data] where data is a string of the content of the file.
@@ -279,3 +292,10 @@ class Integration:
log.msg += exception.msg
if DEBUG:
traceback.print_exc()
+
+
+ def get_export_file_name(self, format='zip'):
+ return "export_{}.{}".format(datetime.datetime.now().strftime("%Y-%m-%d"), format)
+
+ def get_recipe_processed_msg(self, recipe):
+ return f'{recipe.pk} - {recipe.name} \n'
diff --git a/cookbook/integration/pdfexport.py b/cookbook/integration/pdfexport.py
index b982f24d..fca78247 100644
--- a/cookbook/integration/pdfexport.py
+++ b/cookbook/integration/pdfexport.py
@@ -11,22 +11,25 @@ from cookbook.helper.image_processing import get_filetype
from cookbook.integration.integration import Integration
from cookbook.serializer import RecipeExportSerializer
-import django.core.management.commands.runserver as runserver
+from cookbook.models import ExportLog
+from asgiref.sync import sync_to_async
+import django.core.management.commands.runserver as runserver
+import logging
class PDFexport(Integration):
def get_recipe_from_file(self, file):
raise NotImplementedError('Method not implemented in storage integration')
- async def get_files_from_recipes_async(self, recipes, cookie):
+ async def get_files_from_recipes_async(self, recipes, el, cookie):
cmd = runserver.Command()
browser = await launch(
handleSIGINT=False,
handleSIGTERM=False,
handleSIGHUP=False,
- ignoreHTTPSErrors=True
+ ignoreHTTPSErrors=True,
)
cookies = {'domain': cmd.default_addr, 'name': 'sessionid', 'value': cookie['sessionid'], }
@@ -39,17 +42,28 @@ class PDFexport(Integration):
}
}
- page = await browser.newPage()
- await page.emulateMedia('print')
- await page.setCookie(cookies)
files = []
for recipe in recipes:
- await page.goto('http://' + cmd.default_addr + ':' + cmd.default_port + '/view/recipe/' + str(recipe.id), {'waitUntil': 'networkidle0', })
+
+ page = await browser.newPage()
+ await page.emulateMedia('print')
+ await page.setCookie(cookies)
+
+ await page.goto('http://'+cmd.default_addr+':'+cmd.default_port+'/view/recipe/'+str(recipe.id), {'waitUntil': 'domcontentloaded'})
+ await page.waitForSelector('#printReady');
+
files.append([recipe.name + '.pdf', await page.pdf(options)])
+ await page.close();
+
+ el.exported_recipes += 1
+ el.msg += self.get_recipe_processed_msg(recipe)
+ await sync_to_async(el.save, thread_sensitive=True)()
+
await browser.close()
return files
- def get_files_from_recipes(self, recipes, cookie):
- return asyncio.run(self.get_files_from_recipes_async(recipes, cookie))
+
+ def get_files_from_recipes(self, recipes, el, cookie):
+ return asyncio.run(self.get_files_from_recipes_async(recipes, el, cookie))
diff --git a/cookbook/integration/recipesage.py b/cookbook/integration/recipesage.py
index 0ca32194..0bc6704b 100644
--- a/cookbook/integration/recipesage.py
+++ b/cookbook/integration/recipesage.py
@@ -88,12 +88,16 @@ class RecipeSage(Integration):
return data
- def get_files_from_recipes(self, recipes, cookie):
+ def get_files_from_recipes(self, recipes, el, cookie):
json_list = []
for r in recipes:
json_list.append(self.get_file_from_recipe(r))
- return [['export.json', json.dumps(json_list)]]
+ el.exported_recipes += 1
+ el.msg += self.get_recipe_processed_msg(r)
+ el.save()
+
+ return [[self.get_export_file_name('json'), json.dumps(json_list)]]
def split_recipe_file(self, file):
return json.loads(file.read().decode("utf-8"))
diff --git a/cookbook/integration/saffron.py b/cookbook/integration/saffron.py
index 16a93a0c..058f2a8f 100644
--- a/cookbook/integration/saffron.py
+++ b/cookbook/integration/saffron.py
@@ -87,10 +87,14 @@ class Saffron(Integration):
return recipe.name+'.txt', data
- def get_files_from_recipes(self, recipes, cookie):
+ def get_files_from_recipes(self, recipes, el, cookie):
files = []
for r in recipes:
filename, data = self.get_file_from_recipe(r)
files.append([ filename, data ])
+ el.exported_recipes += 1
+ el.msg += self.get_recipe_processed_msg(r)
+ el.save()
+
return files
\ No newline at end of file
diff --git a/cookbook/migrations/0164_exportlog.py b/cookbook/migrations/0164_exportlog.py
new file mode 100644
index 00000000..952c9372
--- /dev/null
+++ b/cookbook/migrations/0164_exportlog.py
@@ -0,0 +1,32 @@
+# Generated by Django 3.2.11 on 2022-01-07 20:29
+
+import cookbook.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('cookbook', '0163_auto_20220105_0758'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ExportLog',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('type', models.CharField(max_length=32)),
+ ('running', models.BooleanField(default=True)),
+ ('msg', models.TextField(default='')),
+ ('total_recipes', models.IntegerField(default=0)),
+ ('exported_recipes', models.IntegerField(default=0)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
+ ],
+ bases=(models.Model, cookbook.models.PermissionModelMixin),
+ ),
+ ]
diff --git a/cookbook/migrations/0165_exportlog_cache_duration.py b/cookbook/migrations/0165_exportlog_cache_duration.py
new file mode 100644
index 00000000..8005a4b2
--- /dev/null
+++ b/cookbook/migrations/0165_exportlog_cache_duration.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.11 on 2022-01-08 00:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('cookbook', '0164_exportlog'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='exportlog',
+ name='cache_duration',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/cookbook/migrations/0166_exportlog_possibly_not_expired.py b/cookbook/migrations/0166_exportlog_possibly_not_expired.py
new file mode 100644
index 00000000..15e19e13
--- /dev/null
+++ b/cookbook/migrations/0166_exportlog_possibly_not_expired.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.11 on 2022-01-08 00:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('cookbook', '0165_exportlog_cache_duration'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='exportlog',
+ name='possibly_not_expired',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/cookbook/models.py b/cookbook/models.py
index 1a97d755..3d2eb4e6 100644
--- a/cookbook/models.py
+++ b/cookbook/models.py
@@ -1002,6 +1002,25 @@ class ImportLog(models.Model, PermissionModelMixin):
def __str__(self):
return f"{self.created_at}:{self.type}"
+class ExportLog(models.Model, PermissionModelMixin):
+ type = models.CharField(max_length=32)
+ running = models.BooleanField(default=True)
+ msg = models.TextField(default="")
+
+ total_recipes = models.IntegerField(default=0)
+ exported_recipes = models.IntegerField(default=0)
+ cache_duration = models.IntegerField(default=0)
+ possibly_not_expired = models.BooleanField(default=True)
+
+ created_at = models.DateTimeField(auto_now_add=True)
+ created_by = models.ForeignKey(User, on_delete=models.CASCADE)
+
+ objects = ScopedManager(space='space')
+ space = models.ForeignKey(Space, on_delete=models.CASCADE)
+
+ def __str__(self):
+ return f"{self.created_at}:{self.type}"
+
class BookmarkletImport(ExportModelOperationsMixin('bookmarklet_import'), models.Model, PermissionModelMixin):
html = models.TextField()
diff --git a/cookbook/serializer.py b/cookbook/serializer.py
index baf77d05..9f86906f 100644
--- a/cookbook/serializer.py
+++ b/cookbook/serializer.py
@@ -15,7 +15,7 @@ from rest_framework.fields import empty
from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.shopping_helper import RecipeShoppingEditor
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food,
- FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
+ FoodInheritField, ImportLog, ExportLog, Ingredient, Keyword, MealPlan, MealType,
NutritionInformation, Recipe, RecipeBook, RecipeBookEntry,
RecipeImport, ShareLink, ShoppingList, ShoppingListEntry,
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
@@ -850,6 +850,20 @@ class ImportLogSerializer(serializers.ModelSerializer):
read_only_fields = ('created_by',)
+class ExportLogSerializer(serializers.ModelSerializer):
+
+ def create(self, validated_data):
+ validated_data['created_by'] = self.context['request'].user
+ validated_data['space'] = self.context['request'].space
+ return super().create(validated_data)
+
+ class Meta:
+ model = ExportLog
+ fields = ('id', 'type', 'msg', 'running', 'total_recipes', 'exported_recipes', 'cache_duration', 'possibly_not_expired', 'created_by', 'created_at')
+ read_only_fields = ('created_by',)
+
+
+
class AutomationSerializer(serializers.ModelSerializer):
def create(self, validated_data):
diff --git a/cookbook/static/css/app.min.css b/cookbook/static/css/app.min.css
index 7c680a78..a100e2c2 100644
--- a/cookbook/static/css/app.min.css
+++ b/cookbook/static/css/app.min.css
@@ -1140,3 +1140,10 @@
min-width: 28rem;
}
}
+
+@media print{
+ #switcher{
+ display: none;
+ }
+
+}
\ No newline at end of file
diff --git a/cookbook/templates/export.html b/cookbook/templates/export.html
index 4133da93..787fc81c 100644
--- a/cookbook/templates/export.html
+++ b/cookbook/templates/export.html
@@ -1,25 +1,33 @@
{% extends "base.html" %}
-{% load crispy_forms_filters %}
-{% load i18n %}
+{% load render_bundle from webpack_loader %}
{% load static %}
+{% load i18n %}
+{% load l10n %}
+
{% block title %}{% trans 'Export Recipes' %}{% endblock %}
-{% block extra_head %}
- {{ form.media }}
+
+{% block content %}
+
+
+
+{% endblock %}
+
+
+{% block script %}
+ {% if debug %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% render_bundle 'export_view' %}
{% endblock %}
-{% block content %}
- {% trans 'Export' %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/cookbook/templates/export_response.html b/cookbook/templates/export_response.html
new file mode 100644
index 00000000..1f438632
--- /dev/null
+++ b/cookbook/templates/export_response.html
@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+{% load render_bundle from webpack_loader %}
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+
+{% block title %}{% trans 'Export' %}{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+{% endblock %}
+
+
+{% block script %}
+ {% if debug %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% render_bundle 'export_response_view' %}
+{% endblock %}
\ No newline at end of file
diff --git a/cookbook/urls.py b/cookbook/urls.py
index 68ac160b..ac7dd838 100644
--- a/cookbook/urls.py
+++ b/cookbook/urls.py
@@ -21,6 +21,7 @@ router.register(r'cook-log', api.CookLogViewSet)
router.register(r'food', api.FoodViewSet)
router.register(r'food-inherit-field', api.FoodInheritFieldViewSet)
router.register(r'import-log', api.ImportLogViewSet)
+router.register(r'export-log', api.ExportLogViewSet)
router.register(r'ingredient', api.IngredientViewSet)
router.register(r'keyword', api.KeywordViewSet)
router.register(r'meal-plan', api.MealPlanViewSet)
@@ -72,8 +73,10 @@ urlpatterns = [
path('abuse/', views.report_share_abuse, name='view_report_share_abuse'),
path('import/', import_export.import_recipe, name='view_import'),
- path('import-response//', import_export.import_response, name='view_import_response'),
+ path('import-response//', import_export.import_response, name='view_import_response'),\
path('export/', import_export.export_recipe, name='view_export'),
+ path('export-response//', import_export.export_response, name='view_export_response'),
+ path('export-file//', import_export.export_file, name='view_export_file'),
path('view/recipe/', views.recipe_view, name='view_recipe'),
path('view/recipe//', views.recipe_view, name='view_recipe'),
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index 878c7118..7f13bca4 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -43,7 +43,7 @@ from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.recipe_url_import import get_from_scraper
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
from cookbook.models import (Automation, BookmarkletImport, CookLog, Food, FoodInheritField,
- ImportLog, Ingredient, Keyword, MealPlan, MealType, Recipe, RecipeBook,
+ ImportLog, ExportLog, Ingredient, Keyword, MealPlan, MealType, Recipe, RecipeBook,
RecipeBookEntry, ShareLink, ShoppingList, ShoppingListEntry,
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
SupermarketCategoryRelation, Sync, SyncLog, Unit, UserFile,
@@ -54,7 +54,7 @@ from cookbook.provider.nextcloud import Nextcloud
from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema
from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializer,
CookLogSerializer, FoodInheritFieldSerializer, FoodSerializer,
- FoodShoppingUpdateSerializer, ImportLogSerializer,
+ FoodShoppingUpdateSerializer, ImportLogSerializer, ExportLogSerializer,
IngredientSerializer, KeywordSerializer, MealPlanSerializer,
MealTypeSerializer, RecipeBookEntrySerializer,
RecipeBookSerializer, RecipeImageSerializer,
@@ -861,6 +861,17 @@ class ImportLogViewSet(viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
+class ExportLogViewSet(viewsets.ModelViewSet):
+ queryset = ExportLog.objects
+ serializer_class = ExportLogSerializer
+ permission_classes = [CustomIsUser]
+ pagination_class = DefaultPagination
+
+ def get_queryset(self):
+ return self.queryset.filter(space=self.request.space)
+
+
+
class BookmarkletImportViewSet(viewsets.ModelViewSet):
queryset = BookmarkletImport.objects
serializer_class = BookmarkletImportSerializer
diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py
index 9fd810c1..9d8fec20 100644
--- a/cookbook/views/import_export.py
+++ b/cookbook/views/import_export.py
@@ -1,9 +1,10 @@
import re
import threading
from io import BytesIO
+from django.core.cache import cache
from django.contrib import messages
-from django.http import HttpResponseRedirect, JsonResponse
+from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext as _
@@ -29,7 +30,7 @@ from cookbook.integration.recipesage import RecipeSage
from cookbook.integration.rezkonv import RezKonv
from cookbook.integration.saffron import Saffron
from cookbook.integration.pdfexport import PDFexport
-from cookbook.models import Recipe, ImportLog, UserPreference
+from cookbook.models import Recipe, ImportLog, ExportLog, UserPreference
from recipes import settings
@@ -123,25 +124,55 @@ def export_recipe(request):
if form.cleaned_data['all']:
recipes = Recipe.objects.filter(space=request.space, internal=True).all()
- if form.cleaned_data['type'] == ImportExportBase.PDF and not settings.ENABLE_PDF_EXPORT:
- messages.add_message(request, messages.ERROR, _('The PDF Exporter is not enabled on this instance as it is still in an experimental state.'))
- return render(request, 'export.html', {'form': form})
integration = get_integration(request, form.cleaned_data['type'])
- return integration.do_export(recipes)
+
+ el = ExportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
+
+ t = threading.Thread(target=integration.do_export, args=[recipes, el])
+ t.setDaemon(True)
+ t.start()
+
+ return JsonResponse({'export_id': el.pk})
except NotImplementedError:
- messages.add_message(request, messages.ERROR, _('Exporting is not implemented for this provider'))
+ return JsonResponse(
+ {
+ 'error': True,
+ 'msg': _('Importing is not implemented for this provider')
+ },
+ status=400
+ )
else:
- form = ExportForm(space=request.space)
+ pk = ''
recipe = request.GET.get('r')
if recipe:
if re.match(r'^([0-9])+$', recipe):
- if recipe := Recipe.objects.filter(pk=int(recipe), space=request.space).first():
- form = ExportForm(initial={'recipes': recipe}, space=request.space)
+ pk = Recipe.objects.filter(pk=int(recipe), space=request.space).first().pk
- return render(request, 'export.html', {'form': form})
+ return render(request, 'export.html', {'pk': pk})
@group_required('user')
def import_response(request, pk):
return render(request, 'import_response.html', {'pk': pk})
+
+@group_required('user')
+def export_response(request, pk):
+ return render(request, 'export_response.html', {'pk': pk})
+
+
+@group_required('user')
+def export_file(request, pk):
+
+ cacheData = cache.get('export_file_'+str(pk))
+
+ if cacheData is None:
+ el = ExportLog.objects.get(pk=pk)
+ el.possibly_not_expired = False;
+ el.save()
+ return render(request, 'export_response.html', {'pk': pk})
+
+ response = HttpResponse(cacheData['file'], content_type='application/force-download')
+ response['Content-Disposition'] = 'attachment; filename="'+cacheData['filename']+'"'
+ return response
+
diff --git a/recipes/settings.py b/recipes/settings.py
index c9ffd32c..3ea147a1 100644
--- a/recipes/settings.py
+++ b/recipes/settings.py
@@ -138,6 +138,7 @@ ENABLE_SIGNUP = bool(int(os.getenv('ENABLE_SIGNUP', False)))
ENABLE_METRICS = bool(int(os.getenv('ENABLE_METRICS', False)))
ENABLE_PDF_EXPORT = bool(int(os.getenv('ENABLE_PDF_EXPORT', False)))
+EXPORT_FILE_CACHE_DURATION = int(os.getenv('EXPORT_FILE_CACHE_DURATION', 600))
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
@@ -427,3 +428,4 @@ EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
EMAIL_USE_SSL = bool(int(os.getenv('EMAIL_USE_SSL', False)))
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv('ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
+
diff --git a/vue/src/apps/ExportResponseView/ExportResponseView.vue b/vue/src/apps/ExportResponseView/ExportResponseView.vue
new file mode 100644
index 00000000..913f4366
--- /dev/null
+++ b/vue/src/apps/ExportResponseView/ExportResponseView.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+ {{ $t('Exporting') }}...
+
+
+
+
+
+
+
+
+
+
+
{{ $t('Export_finished') }}! {{ $t('Return to export') }}
+
+ {{ $t('If download did not start automatically: ') }}
+
+
+ {{ $t('Download') }} ({{ $t('Expired') }})
+
+
{{ $t('Download') }}
+
+
+ {{ $t('The link will remain active for') }}
+
+
+ {{ export_info.cache_duration/3600 }}{{ $t('hr') }}
+
+
+ {{ export_info.cache_duration/60 }}{{ $t('min') }}
+
+
+ {{ export_info.cache_duration }}{{ $t('sec') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vue/src/apps/ExportResponseView/main.js b/vue/src/apps/ExportResponseView/main.js
new file mode 100644
index 00000000..220ac3be
--- /dev/null
+++ b/vue/src/apps/ExportResponseView/main.js
@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import App from './ExportResponseView.vue'
+import i18n from '@/i18n'
+
+Vue.config.productionTip = false
+
+// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
+let publicPath = localStorage.STATIC_URL + 'vue/'
+if (process.env.NODE_ENV === 'development') {
+ publicPath = 'http://localhost:8080/'
+}
+export default __webpack_public_path__ = publicPath // eslint-disable-line
+
+
+new Vue({
+ i18n,
+ render: h => h(App),
+}).$mount('#app')
diff --git a/vue/src/apps/ExportView/ExportView.vue b/vue/src/apps/ExportView/ExportView.vue
new file mode 100644
index 00000000..5db26da2
--- /dev/null
+++ b/vue/src/apps/ExportView/ExportView.vue
@@ -0,0 +1,188 @@
+
+
+
+
{{ $t('Export') }}
+
+
+
+
+
+
+
+
+
+ {{ $t('All recipes') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vue/src/apps/ExportView/main.js b/vue/src/apps/ExportView/main.js
new file mode 100644
index 00000000..8c8af8e5
--- /dev/null
+++ b/vue/src/apps/ExportView/main.js
@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import App from './ExportView.vue'
+import i18n from '@/i18n'
+
+Vue.config.productionTip = false
+
+// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
+let publicPath = localStorage.STATIC_URL + 'vue/'
+if (process.env.NODE_ENV === 'development') {
+ publicPath = 'http://localhost:8080/'
+}
+export default __webpack_public_path__ = publicPath // eslint-disable-line
+
+
+new Vue({
+ i18n,
+ render: h => h(App),
+}).$mount('#app')
diff --git a/vue/src/apps/RecipeView/RecipeView.vue b/vue/src/apps/RecipeView/RecipeView.vue
index 49f15240..7206c835 100644
--- a/vue/src/apps/RecipeView/RecipeView.vue
+++ b/vue/src/apps/RecipeView/RecipeView.vue
@@ -100,7 +100,7 @@
-
![]()
+
@@ -241,6 +241,9 @@ export default {
this.start_time = moment().format("yyyy-MM-DDTHH:mm")
}
+
+ if(recipe.image === null) this.printReady()
+
this.recipe = this.rootrecipe = recipe
this.servings = this.servings_cache[this.rootrecipe.id] = recipe.servings
this.loading = false
@@ -267,6 +270,14 @@ export default {
this.servings = this.servings_cache?.[e.id] ?? e.servings
}
},
+ printReady: function(){
+ const template = document.createElement("template");
+ template.id = "printReady";
+ document.body.appendChild(template);
+ },
+ onImgLoad: function(){
+ this.printReady()
+ },
},
}
diff --git a/vue/src/utils/openapi/api.ts b/vue/src/utils/openapi/api.ts
index 43b5182c..cd50a936 100644
--- a/vue/src/utils/openapi/api.ts
+++ b/vue/src/utils/openapi/api.ts
@@ -173,6 +173,73 @@ export interface CookLog {
*/
created_at?: string;
}
+/**
+ *
+ * @export
+ * @interface ExportLog
+ */
+export interface ExportLog {
+ /**
+ *
+ * @type {number}
+ * @memberof ExportLog
+ */
+ id?: number;
+ /**
+ *
+ * @type {string}
+ * @memberof ExportLog
+ */
+ type: string;
+ /**
+ *
+ * @type {string}
+ * @memberof ExportLog
+ */
+ msg?: string;
+ /**
+ *
+ * @type {boolean}
+ * @memberof ExportLog
+ */
+ running?: boolean;
+ /**
+ *
+ * @type {number}
+ * @memberof ExportLog
+ */
+ total_recipes?: number;
+ /**
+ *
+ * @type {number}
+ * @memberof ExportLog
+ */
+ exported_recipes?: number;
+ /**
+ *
+ * @type {number}
+ * @memberof ExportLog
+ */
+ cache_duration?: number;
+ /**
+ *
+ * @type {boolean}
+ * @memberof ExportLog
+ */
+ possibly_not_expired?: boolean;
+ /**
+ *
+ * @type {string}
+ * @memberof ExportLog
+ */
+ created_by?: string;
+ /**
+ *
+ * @type {string}
+ * @memberof ExportLog
+ */
+ created_at?: string;
+}
/**
*
* @export
@@ -716,6 +783,37 @@ export interface InlineResponse2001 {
*/
results?: Array
;
}
+/**
+ *
+ * @export
+ * @interface InlineResponse20010
+ */
+export interface InlineResponse20010 {
+ /**
+ *
+ * @type {number}
+ * @memberof InlineResponse20010
+ */
+ count?: number;
+ /**
+ *
+ * @type {string}
+ * @memberof InlineResponse20010
+ */
+ next?: string | null;
+ /**
+ *
+ * @type {string}
+ * @memberof InlineResponse20010
+ */
+ previous?: string | null;
+ /**
+ *
+ * @type {Array}
+ * @memberof InlineResponse20010
+ */
+ results?: Array;
+}
/**
*
* @export
@@ -773,10 +871,10 @@ export interface InlineResponse2003 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2003
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -804,10 +902,10 @@ export interface InlineResponse2004 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2004
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -835,10 +933,10 @@ export interface InlineResponse2005 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2005
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -866,10 +964,10 @@ export interface InlineResponse2006 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2006
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -897,10 +995,10 @@ export interface InlineResponse2007 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2007
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -928,10 +1026,10 @@ export interface InlineResponse2008 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2008
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -959,10 +1057,10 @@ export interface InlineResponse2009 {
previous?: string | null;
/**
*
- * @type {Array}
+ * @type {Array}
* @memberof InlineResponse2009
*/
- results?: Array;
+ results?: Array;
}
/**
*
@@ -3054,6 +3152,7 @@ export interface UserPreference {
* @memberof UserPreference
*/
shopping_add_onhand?: boolean;
+
/**
*
* @type {boolean}
@@ -3243,6 +3342,39 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
+ /**
+ *
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ createExportLog: async (exportLog?: ExportLog, options: any = {}): Promise => {
+ const localVarPath = `/api/export-log/`;
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+
+
+ localVarHeaderParameter['Content-Type'] = 'application/json';
+
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+ localVarRequestOptions.data = serializeDataIfNeeded(exportLog, localVarRequestOptions, configuration)
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
/**
*
* @param {Food} [food]
@@ -4081,6 +4213,39 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ destroyExportLog: async (id: string, options: any = {}): Promise => {
+ // verify required parameter 'id' is not null or undefined
+ assertParamExists('destroyExportLog', 'id', id)
+ const localVarPath = `/api/export-log/{id}/`
+ .replace(`{${"id"}}`, encodeURIComponent(String(id)));
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+
+
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@@ -4946,6 +5111,45 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
+ /**
+ *
+ * @param {number} [page] A page number within the paginated result set.
+ * @param {number} [pageSize] Number of results to return per page.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ listExportLogs: async (page?: number, pageSize?: number, options: any = {}): Promise => {
+ const localVarPath = `/api/export-log/`;
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+ if (page !== undefined) {
+ localVarQueryParameter['page'] = page;
+ }
+
+ if (pageSize !== undefined) {
+ localVarQueryParameter['page_size'] = pageSize;
+ }
+
+
+
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@@ -6206,6 +6410,43 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ partialUpdateExportLog: async (id: string, exportLog?: ExportLog, options: any = {}): Promise => {
+ // verify required parameter 'id' is not null or undefined
+ assertParamExists('partialUpdateExportLog', 'id', id)
+ const localVarPath = `/api/export-log/{id}/`
+ .replace(`{${"id"}}`, encodeURIComponent(String(id)));
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+
+
+ localVarHeaderParameter['Content-Type'] = 'application/json';
+
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+ localVarRequestOptions.data = serializeDataIfNeeded(exportLog, localVarRequestOptions, configuration)
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -7165,6 +7406,39 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ retrieveExportLog: async (id: string, options: any = {}): Promise => {
+ // verify required parameter 'id' is not null or undefined
+ assertParamExists('retrieveExportLog', 'id', id)
+ const localVarPath = `/api/export-log/{id}/`
+ .replace(`{${"id"}}`, encodeURIComponent(String(id)));
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+
+
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@@ -8184,6 +8458,43 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ updateExportLog: async (id: string, exportLog?: ExportLog, options: any = {}): Promise => {
+ // verify required parameter 'id' is not null or undefined
+ assertParamExists('updateExportLog', 'id', id)
+ const localVarPath = `/api/export-log/{id}/`
+ .replace(`{${"id"}}`, encodeURIComponent(String(id)));
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+
+ const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+
+
+ localVarHeaderParameter['Content-Type'] = 'application/json';
+
+ setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+ localVarRequestOptions.data = serializeDataIfNeeded(exportLog, localVarRequestOptions, configuration)
+
+ return {
+ url: toPathString(localVarUrlObj),
+ options: localVarRequestOptions,
+ };
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -9060,6 +9371,16 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.createCookLog(cookLog, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async createExportLog(exportLog?: ExportLog, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.createExportLog(exportLog, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {Food} [food]
@@ -9313,6 +9634,16 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.destroyCookLog(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async destroyExportLog(id: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.destroyExportLog(id, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -9573,6 +9904,17 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.listCookLogs(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {number} [page] A page number within the paginated result set.
+ * @param {number} [pageSize] Number of results to return per page.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async listExportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.listExportLogs(page, pageSize, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {*} [options] Override http request option.
@@ -9626,7 +9968,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listKeywords(query, root, tree, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9685,7 +10027,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listRecipes(query?: string, keywords?: number, foods?: number, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listRecipes(query?: string, keywords?: number, foods?: number, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, foods, units, rating, books, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9728,7 +10070,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(recipe, query, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9748,7 +10090,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9777,7 +10119,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listSyncLogs(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9798,7 +10140,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listUnits(query, page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9836,7 +10178,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@@ -9933,6 +10275,17 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.partialUpdateCookLog(id, cookLog, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async partialUpdateExportLog(id: string, exportLog?: ExportLog, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.partialUpdateExportLog(id, exportLog, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -10218,6 +10571,16 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveCookLog(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async retrieveExportLog(id: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveExportLog(id, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -10523,6 +10886,17 @@ export const ApiApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.updateCookLog(id, cookLog, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async updateExportLog(id: string, exportLog?: ExportLog, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.updateExportLog(id, exportLog, options);
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -10805,6 +11179,15 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
createCookLog(cookLog?: CookLog, options?: any): AxiosPromise {
return localVarFp.createCookLog(cookLog, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ createExportLog(exportLog?: ExportLog, options?: any): AxiosPromise {
+ return localVarFp.createExportLog(exportLog, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {Food} [food]
@@ -11033,6 +11416,15 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
destroyCookLog(id: string, options?: any): AxiosPromise {
return localVarFp.destroyCookLog(id, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ destroyExportLog(id: string, options?: any): AxiosPromise {
+ return localVarFp.destroyExportLog(id, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -11267,6 +11659,16 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
listCookLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listCookLogs(page, pageSize, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {number} [page] A page number within the paginated result set.
+ * @param {number} [pageSize] Number of results to return per page.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ listExportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
+ return localVarFp.listExportLogs(page, pageSize, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {*} [options] Override http request option.
@@ -11316,7 +11718,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listKeywords(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11370,7 +11772,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listRecipes(query?: string, keywords?: number, foods?: number, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listRecipes(query?: string, keywords?: number, foods?: number, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listRecipes(query, keywords, foods, units, rating, books, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11409,7 +11811,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listSteps(recipe, query, page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11427,7 +11829,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11453,7 +11855,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listSyncLogs(page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11472,7 +11874,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listUnits(query, page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11506,7 +11908,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
+ listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise {
return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath));
},
/**
@@ -11594,6 +11996,16 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
partialUpdateCookLog(id: string, cookLog?: CookLog, options?: any): AxiosPromise {
return localVarFp.partialUpdateCookLog(id, cookLog, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ partialUpdateExportLog(id: string, exportLog?: ExportLog, options?: any): AxiosPromise {
+ return localVarFp.partialUpdateExportLog(id, exportLog, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -11853,6 +12265,15 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
retrieveCookLog(id: string, options?: any): AxiosPromise {
return localVarFp.retrieveCookLog(id, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ retrieveExportLog(id: string, options?: any): AxiosPromise {
+ return localVarFp.retrieveExportLog(id, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -12128,6 +12549,16 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
updateCookLog(id: string, cookLog?: CookLog, options?: any): AxiosPromise {
return localVarFp.updateCookLog(id, cookLog, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ updateExportLog(id: string, exportLog?: ExportLog, options?: any): AxiosPromise {
+ return localVarFp.updateExportLog(id, exportLog, options).then((request) => request(axios, basePath));
+ },
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -12394,6 +12825,17 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).createCookLog(cookLog, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public createExportLog(exportLog?: ExportLog, options?: any) {
+ return ApiApiFp(this.configuration).createExportLog(exportLog, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {Food} [food]
@@ -12672,6 +13114,17 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).destroyCookLog(id, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public destroyExportLog(id: string, options?: any) {
+ return ApiApiFp(this.configuration).destroyExportLog(id, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -12958,6 +13411,18 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).listCookLogs(page, pageSize, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {number} [page] A page number within the paginated result set.
+ * @param {number} [pageSize] Number of results to return per page.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public listExportLogs(page?: number, pageSize?: number, options?: any) {
+ return ApiApiFp(this.configuration).listExportLogs(page, pageSize, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {*} [options] Override http request option.
@@ -13351,6 +13816,18 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).partialUpdateCookLog(id, cookLog, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public partialUpdateExportLog(id: string, exportLog?: ExportLog, options?: any) {
+ return ApiApiFp(this.configuration).partialUpdateExportLog(id, exportLog, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -13662,6 +14139,17 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).retrieveCookLog(id, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public retrieveExportLog(id: string, options?: any) {
+ return ApiApiFp(this.configuration).retrieveExportLog(id, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {string} id A unique integer value identifying this food.
@@ -13997,6 +14485,18 @@ export class ApiApi extends BaseAPI {
return ApiApiFp(this.configuration).updateCookLog(id, cookLog, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @param {string} id A unique integer value identifying this export log.
+ * @param {ExportLog} [exportLog]
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof ApiApi
+ */
+ public updateExportLog(id: string, exportLog?: ExportLog, options?: any) {
+ return ApiApiFp(this.configuration).updateExportLog(id, exportLog, options).then((request) => request(this.axios, this.basePath));
+ }
+
/**
*
* @param {string} id A unique integer value identifying this food.
diff --git a/vue/vue.config.js b/vue/vue.config.js
index 01460e04..a94390ce 100644
--- a/vue/vue.config.js
+++ b/vue/vue.config.js
@@ -17,6 +17,14 @@ const pages = {
entry: "./src/apps/ImportResponseView/main.js",
chunks: ["chunk-vendors"],
},
+ export_response_view: {
+ entry: "./src/apps/ExportResponseView/main.js",
+ chunks: ["chunk-vendors"],
+ },
+ export_view: {
+ entry: "./src/apps/ExportView/main.js",
+ chunks: ["chunk-vendors"],
+ },
supermarket_view: {
entry: "./src/apps/SupermarketView/main.js",
chunks: ["chunk-vendors"],