some tweaks for new exporter

This commit is contained in:
vabene1111 2022-02-03 18:00:02 +01:00
parent a3fa01d8d3
commit d45e3b8e60
10 changed files with 69 additions and 93 deletions

View File

@ -1,3 +0,0 @@
[Dolphin]
Timestamp=2022,1,7,19,23,46.14
Version=4

View File

@ -152,6 +152,6 @@ REVERSE_PROXY_AUTH=0
# Disabled by default, uncomment to enable # Disabled by default, uncomment to enable
# ENABLE_PDF_EXPORT=1 # ENABLE_PDF_EXPORT=1
# Duration to keep the cached export file (in seconds) # Recipe exports are cached for a certain time by default, adjust time if needed
EXPORT_FILE_CACHE_DURATION=300 # EXPORT_FILE_CACHE_DURATION=600

View File

@ -1,18 +0,0 @@
# 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),
),
]

View File

@ -1,18 +0,0 @@
# 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),
),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 3.2.11 on 2022-01-07 20:29 # Generated by Django 3.2.11 on 2022-02-03 15:03
import cookbook.models import cookbook.models
from django.conf import settings from django.conf import settings
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0163_auto_20220105_0758'), ('cookbook', '0168_add_unit_searchfields'),
] ]
operations = [ operations = [
@ -23,6 +23,8 @@ class Migration(migrations.Migration):
('msg', models.TextField(default='')), ('msg', models.TextField(default='')),
('total_recipes', models.IntegerField(default=0)), ('total_recipes', models.IntegerField(default=0)),
('exported_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_at', models.DateTimeField(auto_now_add=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('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')), ('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),

View File

@ -0,0 +1,25 @@
import pytest
from django.contrib import auth
from django.urls import reverse
from cookbook.forms import ImportExportBase
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.models import ExportLog
@pytest.fixture
def obj_1(space_1, u1_s1):
return ExportLog.objects.create(type=ImportExportBase.DEFAULT, running=False, created_by=auth.get_user(u1_s1), space=space_1, exported_recipes=10, total_recipes=10)
@pytest.mark.parametrize("arg", [
['a_u', 302],
['g1_s1', 302],
['u1_s1', 200],
['a1_s1', 200],
['u1_s2', 404],
['a1_s2', 404],
])
def test_export_file_cache(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
assert c.get(reverse('view_export_file', args=[obj_1.pk])).status_code == arg[1]

View File

@ -73,7 +73,7 @@ urlpatterns = [
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'), path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
path('import/', import_export.import_recipe, name='view_import'), path('import/', import_export.import_recipe, name='view_import'),
path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),\ path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),
path('export/', import_export.export_recipe, name='view_export'), path('export/', import_export.export_recipe, name='view_export'),
path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'), path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'),
path('export-file/<int:pk>/', import_export.export_file, name='view_export_file'), path('export-file/<int:pk>/', import_export.export_file, name='view_export_file'),

View File

@ -5,7 +5,7 @@ from django.core.cache import cache
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render from django.shortcuts import render, get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -125,7 +125,10 @@ def export_recipe(request):
recipes = Recipe.objects.filter(space=request.space, internal=True).all() recipes = Recipe.objects.filter(space=request.space, internal=True).all()
integration = get_integration(request, form.cleaned_data['type']) integration = get_integration(request, form.cleaned_data['type'])
if form.cleaned_data['type'] == ImportExportBase.PDF and not settings.ENABLE_PDF_EXPORT:
return JsonResponse({'error': _('The PDF Exporter is not enabled on this instance as it is still in an experimental state.')})
el = ExportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space) 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 = threading.Thread(target=integration.do_export, args=[recipes, el])
@ -141,7 +144,6 @@ def export_recipe(request):
}, },
status=400 status=400
) )
else: else:
pk = '' pk = ''
recipe = request.GET.get('r') recipe = request.GET.get('r')
@ -156,6 +158,7 @@ def export_recipe(request):
def import_response(request, pk): def import_response(request, pk):
return render(request, 'import_response.html', {'pk': pk}) return render(request, 'import_response.html', {'pk': pk})
@group_required('user') @group_required('user')
def export_response(request, pk): def export_response(request, pk):
return render(request, 'export_response.html', {'pk': pk}) return render(request, 'export_response.html', {'pk': pk})
@ -163,16 +166,15 @@ def export_response(request, pk):
@group_required('user') @group_required('user')
def export_file(request, pk): def export_file(request, pk):
el = get_object_or_404(ExportLog, pk=pk, space=request.space)
cacheData = cache.get('export_file_'+str(pk))
cacheData = cache.get(f'export_file_{el.pk}')
if cacheData is None: if cacheData is None:
el = ExportLog.objects.get(pk=pk) el.possibly_not_expired = False
el.possibly_not_expired = False;
el.save() el.save()
return render(request, 'export_response.html', {'pk': pk}) return render(request, 'export_response.html', {'pk': pk})
response = HttpResponse(cacheData['file'], content_type='application/force-download') response = HttpResponse(cacheData['file'], content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="'+cacheData['filename']+'"' response['Content-Disposition'] = 'attachment; filename="' + cacheData['filename'] + '"'
return response return response

View File

@ -21,26 +21,26 @@ if your favorite one is missing.
Overview of the capabilities of the different integrations. Overview of the capabilities of the different integrations.
| Integration | Import | Export | Images | | Integration | Import | Export | Images |
|--------------------| ------ | ------ | ------ | |--------------------| ------ | -- | ------ |
| Default | ✔️ | ✔️ | ✔️ | | Default | ✔️ | ✔️ | ✔️ |
| Nextcloud | ✔️ | ⌚ | ✔️ | | Nextcloud | ✔️ | ⌚ | ✔️ |
| Mealie | ✔️ | ⌚ | ✔️ | | Mealie | ✔️ | ⌚ | ✔️ |
| Chowdown | ✔️ | ⌚ | ✔️ | | Chowdown | ✔️ | ⌚ | ✔️ |
| Safron | ✔️ | ✔ | ❌ | | Safron | ✔️ | ✔ | ❌ |
| Paprika | ✔️ | ⌚ | ✔️ | | Paprika | ✔️ | ⌚ | ✔️ |
| ChefTap | ✔️ | ❌ | ❌ | | ChefTap | ✔️ | ❌ | ❌ |
| Pepperplate | ✔️ | ⌚ | ❌ | | Pepperplate | ✔️ | ⌚ | ❌ |
| RecipeSage | ✔️ | ✔️ | ✔️ | | RecipeSage | ✔️ | ✔️ | ✔️ |
| Domestica | ✔️ | ⌚ | ✔️ | | Domestica | ✔️ | ⌚ | ✔️ |
| MealMaster | ✔️ | ❌ | ❌ | | MealMaster | ✔️ | ❌ | ❌ |
| RezKonv | ✔️ | ❌ | ❌ | | RezKonv | ✔️ | ❌ | ❌ |
| OpenEats | ✔️ | ❌ | ⌚ | | OpenEats | ✔️ | ❌ | ⌚ |
| Plantoeat | ✔️ | ❌ | ✔ | | Plantoeat | ✔️ | ❌ | ✔ |
| CookBookApp | ✔️ | ⌚ | ✔️ | | CookBookApp | ✔️ | ⌚ | ✔️ |
| CopyMeThat | ✔️ | ❌ | ✔️ | | CopyMeThat | ✔️ | ❌ | ✔️ |
| PDF (experimental) | ⌚️ | ✔ | ✔️ | | PDF (experimental) | ⌚️ | ✔ | ✔️ |
✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
## Default ## Default
The default integration is the built in (and preferred) way to import and export recipes. The default integration is the built in (and preferred) way to import and export recipes.

View File

@ -9,24 +9,9 @@
<!-- TODO get option dynamicaly --> <!-- TODO get option dynamicaly -->
<select class="form-control" v-model="recipe_app"> <select class="form-control" v-model="recipe_app">
<option value="DEFAULT">Default</option> <option value="DEFAULT">Default</option>
<option value="PAPRIKA">Paprika</option>
<option value="NEXTCLOUD">Nextcloud Cookbook</option>
<option value="MEALIE">Mealie</option>
<option value="CHOWDOWN">Chowdown</option>
<option value="SAFFRON">Saffron</option> <option value="SAFFRON">Saffron</option>
<option value="CHEFTAP">ChefTap</option>
<option value="PEPPERPLATE">Pepperplate</option>
<option value="RECETTETEK">RecetteTek</option>
<option value="RECIPESAGE">Recipe Sage</option> <option value="RECIPESAGE">Recipe Sage</option>
<option value="DOMESTICA">Domestica</option> <option value="PDF">PDF (experimental)</option>
<option value="MEALMASTER">MealMaster</option>
<option value="REZKONV">RezKonv</option>
<option value="OPENEATS">Openeats</option>
<option value="RECIPEKEEPER">Recipe Keeper</option>
<option value="PLANTOEAT">Plantoeat</option>
<option value="COOKBOOKAPP">CookBookApp</option>
<option value="COPYMETHAT">CopyMeThat</option>
<option value="PDF">PDF</option>
</select> </select>
<br/> <br/>
@ -53,8 +38,6 @@
@search-change="searchRecipes"> @search-change="searchRecipes">
</multiselect> </multiselect>
<br/> <br/>
<button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t('Export') }} <button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t('Export') }}
</button> </button>
@ -164,14 +147,17 @@ export default {
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.post(resolveDjangoUrl('view_export',), formData).then((response) => { axios.post(resolveDjangoUrl('view_export',), formData).then((response) => {
if (response.data['error'] !== undefined){
window.location.href = resolveDjangoUrl('view_export_response', response.data['export_id']) makeToast(this.$t("Error"), response.data['error'],"warning")
}else{
window.location.href = resolveDjangoUrl('view_export_response', response.data['export_id'])
}
}).catch((err) => { }).catch((err) => {
this.error = err.data this.error = err.data
this.loading = false this.loading = false
console.log(err) console.log(err)
makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "danger") makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "warning")
}) })
}, },