From ece44f27265d2beb7bc186ba3563c143fff2937f Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Wed, 22 Jan 2025 01:41:17 -0300 Subject: [PATCH] feat(import): more UI and endpoints --- app/apps/import_app/forms.py | 9 +- app/apps/import_app/services/v1.py | 18 ++- app/apps/import_app/urls.py | 22 +++- app/apps/import_app/views.py | 60 +++++++++- .../import_app/fragments/profiles/list.html | 38 ++++-- .../import_app/fragments/runs/add.html | 4 +- .../import_app/fragments/runs/list.html | 111 ++++++++++++++++++ 7 files changed, 228 insertions(+), 34 deletions(-) diff --git a/app/apps/import_app/forms.py b/app/apps/import_app/forms.py index 78ee3d7..f300721 100644 --- a/app/apps/import_app/forms.py +++ b/app/apps/import_app/forms.py @@ -55,4 +55,11 @@ class ImportRunFileUploadForm(forms.Form): self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" - self.helper.layout = Layout("file") + self.helper.layout = Layout( + "file", + FormActions( + NoClassSubmit( + "submit", _("Import"), css_class="btn btn-outline-primary w-100" + ), + ), + ) diff --git a/app/apps/import_app/services/v1.py b/app/apps/import_app/services/v1.py index 0416caf..abda751 100644 --- a/app/apps/import_app/services/v1.py +++ b/app/apps/import_app/services/v1.py @@ -491,7 +491,6 @@ class ImportService: for row_number, row in enumerate(reader, start=1): self._process_row(row, row_number) - self._increment_totals("processed", value=1) def _validate_file_path(self, file_path: str) -> str: """ @@ -518,15 +517,14 @@ class ImportService: if self.settings.file_type == "csv": self._process_csv(file_path) - if self.import_run.processed_rows == self.import_run.total_rows: - self._update_status("FINISHED") - self._log( - "info", - f"Import completed successfully. " - f"Successful: {self.import_run.successful_rows}, " - f"Failed: {self.import_run.failed_rows}, " - f"Skipped: {self.import_run.skipped_rows}", - ) + self._update_status("FINISHED") + self._log( + "info", + f"Import completed successfully. " + f"Successful: {self.import_run.successful_rows}, " + f"Failed: {self.import_run.failed_rows}, " + f"Skipped: {self.import_run.skipped_rows}", + ) except Exception as e: self._update_status("FAILED") diff --git a/app/apps/import_app/urls.py b/app/apps/import_app/urls.py index c2608a3..beb65ba 100644 --- a/app/apps/import_app/urls.py +++ b/app/apps/import_app/urls.py @@ -13,6 +13,11 @@ urlpatterns = [ views.import_profile_list, name="import_profiles_list", ), + path( + "import/profiles//delete/", + views.import_profile_delete, + name="import_profile_delete", + ), path( "import/profiles/add/", views.import_profile_add, @@ -24,14 +29,19 @@ urlpatterns = [ name="import_profile_edit", ), path( - "import/profiles//runs/", - views.import_run_add, - name="import_profile_runs_index", + "import/profiles//runs/list/", + views.import_runs_list, + name="import_profile_runs_list", ), path( - "import/profiles//runs/list/", - views.import_run_add, - name="import_profile_runs_list", + "import/profiles//runs//log/", + views.import_run_log, + name="import_run_log", + ), + path( + "import/profiles//runs//delete/", + views.import_run_delete, + name="import_run_delete", ), path( "import/profiles//runs/add/", diff --git a/app/apps/import_app/views.py b/app/apps/import_app/views.py index ce65a2b..6b869fd 100644 --- a/app/apps/import_app/views.py +++ b/app/apps/import_app/views.py @@ -5,6 +5,7 @@ from django.contrib.auth.decorators import login_required from django.core.files.storage import FileSystemStorage from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 +from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.utils.translation import gettext_lazy as _ @@ -107,11 +108,30 @@ def import_profile_edit(request, profile_id): @only_htmx @login_required -@require_http_methods(["GET", "POST"]) -def import_run_list(request, profile_id): +@csrf_exempt +@require_http_methods(["DELETE"]) +def import_profile_delete(request, profile_id): profile = ImportProfile.objects.get(id=profile_id) - runs = ImportRun.objects.filter(profile=profile).order_by("id") + profile.delete() + + messages.success(request, _("Import Profile deleted successfully")) + + return HttpResponse( + status=204, + headers={ + "HX-Trigger": "updated", + }, + ) + + +@only_htmx +@login_required +@require_http_methods(["GET", "POST"]) +def import_runs_list(request, profile_id): + profile = ImportProfile.objects.get(id=profile_id) + + runs = ImportRun.objects.filter(profile=profile).order_by("-id") return render( request, @@ -120,6 +140,19 @@ def import_run_list(request, profile_id): ) +@only_htmx +@login_required +@require_http_methods(["GET", "POST"]) +def import_run_log(request, profile_id, run_id): + run = ImportRun.objects.get(profile__id=profile_id, id=run_id) + + return render( + request, + "import_app/fragments/runs/log.html", + {"run": run}, + ) + + @only_htmx @login_required @require_http_methods(["GET", "POST"]) @@ -140,6 +173,8 @@ def import_run_add(request, profile_id): # Defer the procrastinate task process_import.defer(import_run_id=import_run.id, file_path=file_path) + messages.success(request, _("Import Run queued successfully")) + return HttpResponse( status=204, headers={ @@ -154,3 +189,22 @@ def import_run_add(request, profile_id): "import_app/fragments/runs/add.html", {"form": form, "profile": profile}, ) + + +@only_htmx +@login_required +@csrf_exempt +@require_http_methods(["DELETE"]) +def import_run_delete(request, profile_id, run_id): + run = ImportRun.objects.get(profile__id=profile_id, id=run_id) + + run.delete() + + messages.success(request, _("Run deleted successfully")) + + return HttpResponse( + status=204, + headers={ + "HX-Trigger": "updated", + }, + ) diff --git a/app/templates/import_app/fragments/profiles/list.html b/app/templates/import_app/fragments/profiles/list.html index f1f34d2..2872897 100644 --- a/app/templates/import_app/fragments/profiles/list.html +++ b/app/templates/import_app/fragments/profiles/list.html @@ -38,18 +38,32 @@ hx-get="{% url 'import_profile_edit' profile_id=profile.id %}" hx-target="#generic-offcanvas"> -{# #} -{# #} + + + + + + {{ profile.name }} {{ profile.get_version_display }} diff --git a/app/templates/import_app/fragments/runs/add.html b/app/templates/import_app/fragments/runs/add.html index d5a5b89..9997044 100644 --- a/app/templates/import_app/fragments/runs/add.html +++ b/app/templates/import_app/fragments/runs/add.html @@ -2,10 +2,10 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block title %}{% translate 'Import file' %}{% endblock %} +{% block title %}{% translate 'Import file with profile' %} {{ profile.name }}{% endblock %} {% block body %} -
+ {% crispy form %}
{% endblock %} diff --git a/app/templates/import_app/fragments/runs/list.html b/app/templates/import_app/fragments/runs/list.html index 0697d26..f67054c 100644 --- a/app/templates/import_app/fragments/runs/list.html +++ b/app/templates/import_app/fragments/runs/list.html @@ -5,5 +5,116 @@ {% block title %}{% translate 'Runs for ' %}{{ profile.name }}{% endblock %} {% block body %} +
+ {% if runs %} +
+ {% for run in runs %} +
+
+
+ {{ run.get_status_display }} +
+
+
{{ run.id }}({{ run.file_name }})
+
+
+
+
+
+
+ {% trans 'Total Items' %} +
+
+ {{ run.total_rows }} +
+
+
+
+
+
+
+
+ {% trans 'Processed Items' %} +
+
+ {{ run.processed_rows }} +
+
+
+
+ +
+
+
+
+ {% trans 'Skipped Items' %} +
+
+ {{ run.skipped_rows }} +
+
+
+
+ +
+
+
+
+ {% trans 'Failed Items' %} +
+
+ {{ run.failed_rows }} +
+
+
+
+ +
+
+
+
+ {% trans 'Successful Items' %} +
+
+ {{ run.successful_rows }} +
+
+
+
+ +
+
+ +
+
+ {% endfor %} + {% else %} + + {% endif %} +
+
{% endblock %}