From 24817f770b4766bbd6ae316c1f32e506389a3fd5 Mon Sep 17 00:00:00 2001 From: tdruez Date: Mon, 29 Apr 2024 20:55:02 +0400 Subject: [PATCH 1/3] Enhance Package Import to support modifications #84 Signed-off-by: tdruez --- component_catalog/importers.py | 1 + dje/importers.py | 41 ++++++++++++++++++++------ dje/templates/admin/object_import.html | 10 ++++++- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/component_catalog/importers.py b/component_catalog/importers.py index 70153e72..41ca2bf9 100644 --- a/component_catalog/importers.py +++ b/component_catalog/importers.py @@ -298,6 +298,7 @@ class PackageImporter(BaseImporter): model_form = PackageImportForm add_to_product_perm = "product_portfolio.add_productpackage" relation_model = ProductPackage + update_existing = True def prepare_data_json(self, data): """ diff --git a/dje/importers.py b/dje/importers.py index 082a02b2..f610c2af 100644 --- a/dje/importers.py +++ b/dje/importers.py @@ -154,7 +154,7 @@ def pre_process_form(self, data, **kwargs): identifier_fields = model_class.get_identifier_fields() # Crafting the list of unique filters to match the instance for field_name in identifier_fields: - value = data.get(prefix + field_name, None) + value = data.get(prefix + field_name, "") if value: value = value.strip() filters.update({field_name: value}) @@ -310,6 +310,7 @@ class BaseImporter: model_form = None formset_class = BaseImportModelFormSet add_to_product = False + update_existing = False def __init__(self, user, file_location=None, formset_data=None): if not self.model_form and not isinstance(self.model_form, BaseImportModelForm): @@ -564,7 +565,7 @@ def get_key_sorted_fields(self): """ model_form_instance = self.model_form(dataspace=self.dataspace, user=self.user) - for _, field in model_form_instance.fields.items(): + for field in model_form_instance.fields.values(): field.supported_values = self.get_supported_values(field) return sorted(model_form_instance.fields.items()) @@ -582,14 +583,31 @@ def save_all(self): if not self.formset.is_valid(): # Just in case... return - self.results = {"added": [], "unmodified": []} + self.results = {"added": [], "modified": [], "unmodified": []} for form in self.formset: - if not form.instance.pk: # Save only addition for now - saved_instance = form.save() - self.results["added"].append(saved_instance) - History.log_addition(self.user, saved_instance) - else: - self.results["unmodified"].append(form.instance) + self.save_form(form) + + def save_form(self, form): + instance = form.instance + + if not instance.pk: # Save only addition for now + saved_instance = form.save() + self.results["added"].append(saved_instance) + History.log_addition(self.user, saved_instance) + return + + elif self.update_existing: + # We need to refresh the instance from the db because form.instance has + # the unsaved form.cleaned_data modification at that stage. + instance.refresh_from_db() + updated_fields = instance.update_from_data(self.user, form.cleaned_data, override=False) + if updated_fields: + self.results["modified"].append(instance) + msg = f'Updated {", ".join(updated_fields)} from import' + History.log_change(self.user, instance, message=msg) + return + + self.results["unmodified"].append(instance) def get_added_instance_ids(self): """Return the list of added instance ids.""" @@ -631,6 +649,11 @@ class ImportableUploadFileForm(forms.Form): class PackageImportableUploadFileForm(forms.Form): file = SmartFileField(extensions=["csv", "json"]) + update_existing_packages = forms.BooleanField( + label="Update existing packages with import data", + required=False, + initial=True, + ) @property def header(self): diff --git a/dje/templates/admin/object_import.html b/dje/templates/admin/object_import.html index 926c17a5..2dc8c264 100644 --- a/dje/templates/admin/object_import.html +++ b/dje/templates/admin/object_import.html @@ -30,7 +30,15 @@

Help for {{ importer.verbose_name|title }} Import:
@@ -273,7 +277,7 @@

Step 1: Select your file

{% endif %}
{% csrf_token %} -

+

{{ file_form.file }}

From 126b318c081e191e053caf909f8fe7180e596ecb Mon Sep 17 00:00:00 2001 From: tdruez Date: Wed, 15 May 2024 16:48:28 +0400 Subject: [PATCH 3/3] Add unit test for the Package import modifications #84 Signed-off-by: tdruez --- component_catalog/tests/test_importers.py | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/component_catalog/tests/test_importers.py b/component_catalog/tests/test_importers.py index 5060739b..0edaacc6 100644 --- a/component_catalog/tests/test_importers.py +++ b/component_catalog/tests/test_importers.py @@ -1318,6 +1318,40 @@ def test_package_import_prepare_package(self): prepared_package = PackageImporter.prepare_package(package_data) self.assertEqual(expected, prepared_package) + def test_package_import_update_existing(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "filename.zip", + } + + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["unmodified"])) + + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "filename.zip", + "form-0-notes": "Notes", + } + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["modified"])) + modified_package = importer.results["modified"][0] + self.assertEqual("Notes", modified_package.notes) + + history_entry = History.objects.get_for_object(modified_package).get() + expected_messages = "Updated notes from import" + self.assertEqual(expected_messages, history_entry.change_message) + def test_package_import_add_to_product(self): admin_user = create_admin("admin_user", self.dataspace) self.client.login(username=admin_user.username, password="secret")