diff --git a/README.rst b/README.rst index f5394d3..8391502 100644 --- a/README.rst +++ b/README.rst @@ -169,6 +169,14 @@ To exclude or disable file formats from the admin site, configure `IMPORT_EXPORT IMPORT_EXPORT_CELERY_EXCLUDED_FORMATS = ["csv", "xls"] +Add Custom export file formats in the admin site +------------------------------------------------ + +To include custom file formats from the admin site, configure `IMPORT_EXPORT_CELERY_CUSTOM_FORMATS` django settings variable. This variable is a a list of string path to custom format classes. + + :: + + IMPORT_EXPORT_CELERY_CUSTOM_FORMATS = ["path.to.custom_format.CustomFormat", "path.to.custom_format.CustomFormat2] Customizing File Storage Backend -------------------------------- diff --git a/import_export_celery/admin_actions.py b/import_export_celery/admin_actions.py index 2c06434..86ca055 100644 --- a/import_export_celery/admin_actions.py +++ b/import_export_celery/admin_actions.py @@ -7,7 +7,7 @@ from django.shortcuts import redirect from .models import ExportJob - +from .utils import get_formats from . import tasks @@ -53,6 +53,15 @@ def create_export_job_action(modeladmin, request, queryset): ), site_of_origin=request.scheme + "://" + request.get_host(), ) + + # Load the ExportJob if only one Resource + resource_choices = ej.get_resource_choices() + format_choices = get_formats() + if len(resource_choices) == 1 and len(format_choices) == 1: + ej.resource = resource_choices[0][0] + ej.format = format_choices[0].CONTENT_TYPE + ej.save() + rurl = reverse( "admin:%s_%s_change" % ( diff --git a/import_export_celery/locale/fr/LC_MESSAGES/django.po b/import_export_celery/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..3466f8a --- /dev/null +++ b/import_export_celery/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,150 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Dan , 2020. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-05-17 20:55+0000\n" +"PO-Revision-Date: 2023-05-17 21:56+0100\n" +"Last-Translator: Daniel Pluth \n" +"Language-Team: \n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Poedit 3.3.1\n" + +#: import_export_celery/admin.py:12 +msgid "Job status info" +msgstr "Statut de l'export" + +#: import_export_celery/admin.py:22 import_export_celery/models/importjob.py:62 +msgid "Name of model to import to" +msgstr "Nom du modèle à importer" + +#: import_export_celery/admin_actions.py:20 +msgid "Perform import" +msgstr "Effectuer l'importation" + +#: import_export_celery/admin_actions.py:29 +msgid "Perform dry import" +msgstr "Effectuer une importation à blanc" + +#: import_export_celery/admin_actions.py:39 +msgid "Run export job" +msgstr "Lancer le travail d'exportation" + +#: import_export_celery/admin_actions.py:67 +msgid "Export with celery" +msgstr "Exporter avec Celery" + +#: import_export_celery/apps.py:7 +msgid "Import Export Celery" +msgstr "Exports" + +#: import_export_celery/models/exportjob.py:27 +msgid "exported file" +msgstr "fichier exporté" + +#: import_export_celery/models/exportjob.py:35 +#: import_export_celery/models/importjob.py:30 +msgid "Have we started processing the file? If so when?" +msgstr "Démarrage de l'export" + +#: import_export_celery/models/exportjob.py:42 +#: import_export_celery/models/importjob.py:67 +msgid "Status of the job" +msgstr "Statut du travail" + +#: import_export_celery/models/exportjob.py:48 +msgid "Format of file to be exported" +msgstr "Format du fichier à exporter" + +#: import_export_celery/models/exportjob.py:55 +msgid "App label of model to export from" +msgstr "Nom de l'application du modèle à exporter" + +#: import_export_celery/models/exportjob.py:60 +msgid "Name of model to export from" +msgstr "Nom du modèle à exporter" + +#: import_export_celery/models/exportjob.py:65 +msgid "Resource to use when exporting" +msgstr "Ressource à utiliser lors de l'exportation" + +#: import_export_celery/models/exportjob.py:71 +msgid "JSON list of pks to export" +msgstr "Liste JSON des clés primaires à exporter" + +#: import_export_celery/models/exportjob.py:76 +msgid "Send me an email when this export job is complete" +msgstr "Envoyez-moi un email lorsque ce travail d'exportation est terminé" + +#: import_export_celery/models/exportjob.py:81 +msgid "Site of origin" +msgstr "Site d'origine" + +#: import_export_celery/models/exportjob.py:87 +msgid "Export job" +msgstr "Travail d'exportation" + +#: import_export_celery/models/exportjob.py:88 +msgid "Export jobs" +msgstr "Exports" + +#: import_export_celery/models/importjob.py:22 +msgid "File to be imported" +msgstr "Fichier à importer" + +#: import_export_celery/models/importjob.py:37 +msgid "Has the import been completed? If so when?" +msgstr "L'importation a-t-elle été complétée ? Si oui, quand ?" + +#: import_export_celery/models/importjob.py:44 +msgid "Format of file to be imported" +msgstr "Format du fichier à importer" + +#: import_export_celery/models/importjob.py:49 +msgid "Summary of changes made by this import" +msgstr "Résumé des changements effectués par cette importation" + +#: import_export_celery/models/importjob.py:56 +msgid "Errors" +msgstr "Erreurs" + +#: import_export_celery/models/importjob.py:73 +msgid "Import job" +msgstr "Travail d'importation" + +#: import_export_celery/models/importjob.py:74 +msgid "Import jobs" +msgstr "Travaux d'importation" + +#: import_export_celery/tasks.py:61 +#, python-format +msgid "Imported file has a wrong encoding: %s" +msgstr "Le fichier importé a un encodage incorrect : %s" + +#: import_export_celery/tasks.py:68 +#, python-format +msgid "Error reading file: %s" +msgstr "Erreur de lecture du fichier : %s" + +#: import_export_celery/tasks.py:101 +#, python-format +msgid "" +"Line: %s - %s\n" +"\t%s\n" +"%s" +msgstr "" +"Ligne : %s - %s\n" +"\t%s\n" +"%s" + +#: import_export_celery/tasks.py:190 +#, python-format +msgid "Import error %s" +msgstr "Erreur d'importation %s" diff --git a/import_export_celery/migrations/0011_exportjob_created_at_alter_exportjob_id_and_more.py b/import_export_celery/migrations/0011_exportjob_created_at_alter_exportjob_id_and_more.py new file mode 100644 index 0000000..2f2203d --- /dev/null +++ b/import_export_celery/migrations/0011_exportjob_created_at_alter_exportjob_id_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2 on 2024-04-12 14:09 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ("import_export_celery", "0010_auto_20231013_0904"), + ] + + operations = [ + migrations.AddField( + model_name="exportjob", + name="created_at", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + migrations.AlterField( + model_name="exportjob", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="importjob", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/import_export_celery/models/exportjob.py b/import_export_celery/models/exportjob.py index 66f5691..cdb273a 100644 --- a/import_export_celery/models/exportjob.py +++ b/import_export_celery/models/exportjob.py @@ -83,10 +83,15 @@ def __init__(self, *args, **kwargs): default="", ) + created_at = models.DateTimeField(auto_now_add=True) + class Meta: verbose_name = _("Export job") verbose_name_plural = _("Export jobs") + def __str__(self): + return f"Export {self.model.title()} - par {self.author} - le {self.created_at.date()} - {self.job_status}" + def get_resource_class(self): if self.resource: return ( diff --git a/import_export_celery/tasks.py b/import_export_celery/tasks.py index ef3083b..46e9a7b 100644 --- a/import_export_celery/tasks.py +++ b/import_export_celery/tasks.py @@ -228,7 +228,7 @@ def export_resource(self, *args, **kwargs): change_job_status( export_job, "export", - f"Exporting row {self.row_number}/{qs_len}", + f"En cours ({self.row_number}/{qs_len} lignes)", ) self.row_number += 1 return super().export_resource(*args, **kwargs) @@ -238,11 +238,10 @@ def export_resource(self, *args, **kwargs): data = resource.export(queryset) format = get_format(export_job) serialized = format.export_data(data) - change_job_status(export_job, "export", "Export complete") - filename = "{app}-{model}-{date}.{extension}".format( - app=export_job.app_label, - model=export_job.model, - date=str(timezone.now()), + change_job_status(export_job, "export", "Export terminé") + filename = "Export {model} - {date}.{extension}".format( + model=export_job.model.title(), + date=str(timezone.now().strftime("%Y-%m-%d-%H-%M-%S")), extension=format.get_extension(), ) if not format.is_binary(): diff --git a/import_export_celery/utils.py b/import_export_celery/utils.py index 2cd4659..0591cbf 100644 --- a/import_export_celery/utils.py +++ b/import_export_celery/utils.py @@ -4,6 +4,7 @@ from django.conf import settings from django.urls import reverse from import_export.formats.base_formats import DEFAULT_FORMATS +from importlib import import_module DEFAULT_EXPORT_JOB_COMPLETION_MAIL_SUBJECT = "Django: Export job completed" DEFAULT_EXPORT_JOB_COMPLETION_MAIL_TEMPLATE = ( @@ -15,14 +16,28 @@ [], ) +IMPORT_EXPORT_CELERY_CUSTOM_FORMATS = getattr(settings, "IMPORT_EXPORT_CELERY_CUSTOM_FORMATS", []) def get_formats(): + custom_formats = get_custom_formats() + return [ format for format in DEFAULT_FORMATS if format.TABLIB_MODULE.split(".")[-1].strip("_") not in IMPORT_EXPORT_CELERY_EXCLUDED_FORMATS - ] + ] + custom_formats + +def get_custom_formats(): + # Load custom formats if any + custom_formats = [] + if IMPORT_EXPORT_CELERY_CUSTOM_FORMATS: + for formatxx in IMPORT_EXPORT_CELERY_CUSTOM_FORMATS: + target_module_directory, target_format_name = formatxx.rsplit('.', 1) + target_module = import_module(target_module_directory) + target_format = getattr(target_module, target_format_name) + custom_formats.append(target_format) + return custom_formats def build_html_and_text_message(template_name, context={}):