diff --git a/cookbook/admin.py b/cookbook/admin.py index b96fa2e47..85bfbe01f 100644 --- a/cookbook/admin.py +++ b/cookbook/admin.py @@ -7,7 +7,7 @@ from .models import (Comment, CookLog, Food, Ingredient, InviteLink, Keyword, RecipeBook, RecipeBookEntry, RecipeImport, ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage, Sync, SyncLog, Unit, UserPreference, - ViewLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation) + ViewLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation, ImportLog) class CustomUserAdmin(UserAdmin): @@ -213,3 +213,10 @@ class NutritionInformationAdmin(admin.ModelAdmin): admin.site.register(NutritionInformation, NutritionInformationAdmin) + + +class ImportLogAdmin(admin.ModelAdmin): + list_display = ('id', 'type', 'running', 'created_by', 'created_at',) + + +admin.site.register(ImportLog, ImportLogAdmin) diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index db543ac1c..1745de449 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -20,14 +20,14 @@ class Integration: keyword = None files = None - def __init__(self, request): + def __init__(self, request, export_type): """ Integration for importing and exporting recipes :param request: request context of import session (used to link user to created objects) """ self.request = request self.keyword = Keyword.objects.create( - name=f'Import {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}.{datetime.datetime.now().strftime("%S")}', + name=f'Import {export_type} {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}.{datetime.datetime.now().strftime("%S")}', description=f'Imported by {request.user.get_user_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}', icon='📥', space=request.space @@ -77,10 +77,11 @@ class Integration: """ return True - def do_import(self, files): + def do_import(self, files, il): """ Imports given files :param files: List of in memory files + :param il: Import Log object to refresh while running :return: HttpResponseRedirect to the recipe search showing all imported recipes """ with scope(space=self.request.space): @@ -94,20 +95,26 @@ class Integration: if self.import_file_name_filter(z): 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' if duplicate := self.is_duplicate(recipe): ignored_recipes.append(duplicate) import_zip.close() else: recipe = self.get_recipe_from_file(f['file']) recipe.keywords.add(self.keyword) + il.msg += f'{recipe.pk} - {recipe.name} \n' if duplicate := self.is_duplicate(recipe): ignored_recipes.append(duplicate) except BadZipFile: - messages.add_message(self.request, messages.ERROR, _('Importer expected a .zip file. Did you choose the correct importer type for your data ?')) + il.msg += 'ERROR ' + _('Importer expected a .zip file. Did you choose the correct importer type for your data ?') + '\n' if len(ignored_recipes) > 0: - messages.add_message(self.request, messages.WARNING, _('The following recipes were ignored because they already existed:') + ' ' + ', '.join(ignored_recipes)) - return HttpResponseRedirect(reverse('view_search') + '?keywords=' + str(self.keyword.pk)) + il.msg += _('The following recipes were ignored because they already existed:') + ' ' + ', '.join(ignored_recipes) + '\n' + + il.keyword = self.keyword + il.msg += (_('Imported %s recipes.') % Recipe.objects.filter(keywords=self.keyword).count()) + '\n' + il.running = False + il.save() def is_duplicate(self, recipe): """ diff --git a/cookbook/migrations/0114_importlog.py b/cookbook/migrations/0114_importlog.py new file mode 100644 index 000000000..df7f9c55d --- /dev/null +++ b/cookbook/migrations/0114_importlog.py @@ -0,0 +1,31 @@ +# Generated by Django 3.1.7 on 2021-03-18 17:23 + +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', '0113_auto_20210317_2017'), + ] + + operations = [ + migrations.CreateModel( + name='ImportLog', + 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='')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('keyword', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cookbook.keyword')), + ('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')), + ], + bases=(models.Model, cookbook.models.PermissionModelMixin), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index ba97c5601..6295fb926 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -241,20 +241,6 @@ class SyncLog(models.Model, PermissionModelMixin): return f"{self.created_at}:{self.sync} - {self.status}" -class ImportLog(models.Model, PermissionModelMixin): - type = models.CharField(max_length=32) - running = models.BooleanField(default=True) - msg = models.TextField(default="") - 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} - {self.msg}" - - class Keyword(models.Model, PermissionModelMixin): name = models.CharField(max_length=64) icon = models.CharField(max_length=16, blank=True, null=True) @@ -655,3 +641,18 @@ class ViewLog(models.Model, PermissionModelMixin): def __str__(self): return self.recipe.name + + +class ImportLog(models.Model, PermissionModelMixin): + type = models.CharField(max_length=32) + running = models.BooleanField(default=True) + msg = models.TextField(default="") + keyword = models.ForeignKey(Keyword, null=True, blank=True, on_delete=models.SET_NULL) + 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}" diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index d70b2d07d..b067afd50 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -14,22 +14,22 @@ from cookbook.integration.mealie import Mealie from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.paprika import Paprika from cookbook.integration.safron import Safron -from cookbook.models import Recipe +from cookbook.models import Recipe, ImportLog def get_integration(request, export_type): if export_type == ImportExportBase.DEFAULT: - return Default(request) + return Default(request, export_type) if export_type == ImportExportBase.PAPRIKA: - return Paprika(request) + return Paprika(request, export_type) if export_type == ImportExportBase.NEXTCLOUD: - return NextcloudCookbook(request) + return NextcloudCookbook(request, export_type) if export_type == ImportExportBase.MEALIE: - return Mealie(request) + return Mealie(request, export_type) if export_type == ImportExportBase.CHOWDOWN: - return Chowdown(request) + return Chowdown(request, export_type) if export_type == ImportExportBase.SAFRON: - return Safron(request) + return Safron(request, export_type) @group_required('user') @@ -39,10 +39,12 @@ def import_recipe(request): if form.is_valid(): try: integration = get_integration(request, form.cleaned_data['type']) + + il = ImportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space) files = [] for f in request.FILES.getlist('files'): files.append({'file': BytesIO(f.read()), 'name': f.name}) - t = threading.Thread(target=integration.do_import, args=[files]) + t = threading.Thread(target=integration.do_import, args=[files, il]) t.setDaemon(True) t.start()