From c5279623857af3ee89d403e3ed6548a6f92fd4a7 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 26 Nov 2024 14:03:51 +0100 Subject: [PATCH 1/2] [#2903] Support export of only select ZaakType configs --- src/open_inwoner/openzaak/admin.py | 23 ++-- src/open_inwoner/openzaak/import_export.py | 87 +++++++++------ .../openzaak/tests/test_import_export.py | 100 +++++++++++++++--- 3 files changed, 156 insertions(+), 54 deletions(-) diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py index c264783118..4dcd59bde7 100644 --- a/src/open_inwoner/openzaak/admin.py +++ b/src/open_inwoner/openzaak/admin.py @@ -13,15 +13,11 @@ from django.utils.html import format_html, format_html_join from django.utils.translation import gettext_lazy as _, ngettext -from import_export.admin import ImportExportMixin from privates.storages import PrivateMediaFileSystemStorage from solo.admin import SingletonModelAdmin from open_inwoner.ckeditor5.widgets import CKEditorWidget -from open_inwoner.openzaak.import_export import ( - CatalogusConfigExport, - CatalogusConfigImport, -) +from open_inwoner.openzaak.import_export import CatalogusConfigImport, ZGWConfigExport from open_inwoner.utils.forms import LimitedUploadFileField from .models import ( @@ -144,7 +140,7 @@ def get_urls(self): @admin.action(description=_("Export to file")) def export_catalogus_configs(modeladmin, request, queryset): - export = CatalogusConfigExport.from_catalogus_configs(queryset) + export = ZGWConfigExport.from_catalogus_configs(queryset) response = StreamingHttpResponse( export.as_jsonl_iter(), content_type="application/json", @@ -374,7 +370,7 @@ def has_delete_permission(self, request, obj=None): @admin.register(ZaakTypeConfig) -class ZaakTypeConfigAdmin(ImportExportMixin, admin.ModelAdmin): +class ZaakTypeConfigAdmin(admin.ModelAdmin): inlines = [ ZaakTypeInformatieObjectTypeConfigInline, ZaakTypeStatusTypeConfigInline, @@ -383,6 +379,7 @@ class ZaakTypeConfigAdmin(ImportExportMixin, admin.ModelAdmin): actions = [ "mark_as_notify_status_changes", "mark_as_not_notify_status_changes", + "export_zaaktype_configs", ] fields = [ "urls", @@ -437,6 +434,18 @@ class ZaakTypeConfigAdmin(ImportExportMixin, admin.ModelAdmin): ] ordering = ("identificatie", "catalogus__domein") + @admin.action(description=_("Export to file")) + def export_zaaktype_configs(modeladmin, request, queryset): + export = ZGWConfigExport.from_zaaktype_configs(queryset) + response = StreamingHttpResponse( + export.as_jsonl_iter(), + content_type="application/json", + ) + response[ + "Content-Disposition" + ] = 'attachment; filename="zgw-zaaktype-export.json"' + return response + def has_add_permission(self, request): return False diff --git a/src/open_inwoner/openzaak/import_export.py b/src/open_inwoner/openzaak/import_export.py index cc1f285866..06a71d01d5 100644 --- a/src/open_inwoner/openzaak/import_export.py +++ b/src/open_inwoner/openzaak/import_export.py @@ -166,11 +166,9 @@ def _update_nested_zgw_config( @dataclasses.dataclass(frozen=True) -class CatalogusConfigExport: - """Gather and export CatalogusConfig(s) and all associated relations.""" - +class ZGWConfigExport: catalogus_configs: QuerySet - zaak_type_configs: QuerySet + zaaktype_configs: QuerySet zaak_informatie_object_type_configs: QuerySet zaak_status_type_configs: QuerySet zaak_resultaat_type_configs: QuerySet @@ -178,7 +176,7 @@ class CatalogusConfigExport: def __iter__(self) -> Generator[QuerySet, Any, None]: yield from ( self.catalogus_configs, - self.zaak_type_configs, + self.zaaktype_configs, self.zaak_informatie_object_type_configs, self.zaak_status_type_configs, self.zaak_resultaat_type_configs, @@ -188,9 +186,32 @@ def __eq__(self, other: QuerySet) -> bool: for a, b in zip(self, other): if a.difference(b).exists(): return False - return True + def as_dicts_iter(self) -> Generator[dict, Any, None]: + for qs in self: + serialized_data = serializers.serialize( + queryset=qs, + format="json", + use_natural_foreign_keys=True, + use_natural_primary_keys=True, + ) + json_data: list[dict] = json.loads( + serialized_data, + ) + yield from json_data + + def as_jsonl_iter(self) -> Generator[str, Any, None]: + for row in self.as_dicts(): + yield json.dumps(row) + yield "\n" + + def as_dicts(self) -> list[dict]: + return list(self.as_dicts_iter()) + + def as_jsonl(self) -> str: + return "".join(self.as_jsonl_iter()) + @classmethod def from_catalogus_configs(cls, catalogus_configs: QuerySet) -> Self: if not isinstance(catalogus_configs, QuerySet): @@ -203,50 +224,56 @@ def from_catalogus_configs(cls, catalogus_configs: QuerySet) -> Self: f"`catalogus_configs` is of type {catalogus_configs.model}, not CatalogusConfig" ) - zaak_type_configs = ZaakTypeConfig.objects.filter( + zaaktype_configs = ZaakTypeConfig.objects.filter( catalogus__in=catalogus_configs ) informatie_object_types = ZaakTypeInformatieObjectTypeConfig.objects.filter( - zaaktype_config__in=zaak_type_configs + zaaktype_config__in=zaaktype_configs ) zaak_status_type_configs = ZaakTypeStatusTypeConfig.objects.filter( - zaaktype_config__in=zaak_type_configs + zaaktype_config__in=zaaktype_configs ) zaak_resultaat_type_configs = ZaakTypeResultaatTypeConfig.objects.filter( - zaaktype_config__in=zaak_type_configs + zaaktype_config__in=zaaktype_configs ) return cls( catalogus_configs=catalogus_configs, - zaak_type_configs=zaak_type_configs, + zaaktype_configs=zaaktype_configs, zaak_informatie_object_type_configs=informatie_object_types, zaak_status_type_configs=zaak_status_type_configs, zaak_resultaat_type_configs=zaak_resultaat_type_configs, ) - def as_dicts_iter(self) -> Generator[dict, Any, None]: - for qs in self: - serialized_data = serializers.serialize( - queryset=qs, - format="json", - use_natural_foreign_keys=True, - use_natural_primary_keys=True, - ) - json_data: list[dict] = json.loads( - serialized_data, + @classmethod + def from_zaaktype_configs(cls, zaaktype_configs: QuerySet) -> Self: + if not isinstance(zaaktype_configs, QuerySet): + raise TypeError( + f"`zaaktype_configs` is not a QuerySet, but a {type(zaaktype_configs)}" ) - yield from json_data - def as_jsonl_iter(self) -> Generator[str, Any, None]: - for row in self.as_dicts(): - yield json.dumps(row) - yield "\n" + if zaaktype_configs.model != ZaakTypeConfig: + raise ValueError( + f"`zaaktype_configs` is of type {zaaktype_configs.model}, not ZaakTypeConfig" + ) - def as_dicts(self) -> list[dict]: - return list(self.as_dicts_iter()) + informatie_object_types = ZaakTypeInformatieObjectTypeConfig.objects.filter( + zaaktype_config__in=zaaktype_configs + ) + zaak_status_type_configs = ZaakTypeStatusTypeConfig.objects.filter( + zaaktype_config__in=zaaktype_configs + ) + zaak_resultaat_type_configs = ZaakTypeResultaatTypeConfig.objects.filter( + zaaktype_config__in=zaaktype_configs + ) - def as_jsonl(self) -> str: - return "".join(self.as_jsonl_iter()) + return cls( + catalogus_configs=CatalogusConfig.objects.none(), + zaaktype_configs=zaaktype_configs, + zaak_informatie_object_type_configs=informatie_object_types, + zaak_status_type_configs=zaak_status_type_configs, + zaak_resultaat_type_configs=zaak_resultaat_type_configs, + ) @dataclasses.dataclass(frozen=True) diff --git a/src/open_inwoner/openzaak/tests/test_import_export.py b/src/open_inwoner/openzaak/tests/test_import_export.py index 70f3348d45..804f1f12b7 100644 --- a/src/open_inwoner/openzaak/tests/test_import_export.py +++ b/src/open_inwoner/openzaak/tests/test_import_export.py @@ -5,10 +5,7 @@ from django.core.files.storage.memory import InMemoryStorage from django.test import TestCase -from open_inwoner.openzaak.import_export import ( - CatalogusConfigExport, - CatalogusConfigImport, -) +from open_inwoner.openzaak.import_export import CatalogusConfigImport, ZGWConfigExport from open_inwoner.openzaak.models import ( CatalogusConfig, ZaakTypeConfig, @@ -94,17 +91,17 @@ def test_export_only_accepts_queryset(self): for arg in (list(), set(), tuple(), None, "", CatalogusConfig.objects.first()): with self.subTest(f"Default factory should not accept {arg}"): with self.assertRaises(TypeError): - CatalogusConfigExport.from_catalogus_configs(arg) + ZGWConfigExport.from_catalogus_configs(arg) def test_export_only_accepts_queryset_of_correct_type(self): with self.assertRaises(ValueError): - CatalogusConfigExport.from_catalogus_configs(ZaakTypeConfig.objects.all()) + ZGWConfigExport.from_catalogus_configs(ZaakTypeConfig.objects.all()) def test_equality_operator(self): - export_a = CatalogusConfigExport.from_catalogus_configs( + export_a = ZGWConfigExport.from_catalogus_configs( CatalogusConfig.objects.filter(pk=self.mocks[0].catalogus.pk) ) - export_b = CatalogusConfigExport.from_catalogus_configs( + export_b = ZGWConfigExport.from_catalogus_configs( CatalogusConfig.objects.filter(pk=self.mocks[1].catalogus.pk) ) @@ -113,14 +110,14 @@ def test_equality_operator(self): self.assertFalse(export_a == export_b) def test_only_models_related_to_exported_catalogus_config_are_included(self): - export = CatalogusConfigExport.from_catalogus_configs( + export = ZGWConfigExport.from_catalogus_configs( CatalogusConfig.objects.filter(pk=self.mocks[0].catalogus.pk) ) for export_field, mock_field in zip( ( "catalogus_configs", - "zaak_type_configs", + "zaaktype_configs", "zaak_status_type_configs", "zaak_resultaat_type_configs", "zaak_informatie_object_type_configs", @@ -148,6 +145,79 @@ def test_only_models_related_to_exported_catalogus_config_are_included(self): ) +class ZaakTypeConfigExportTest(TestCase): + def setUp(self): + self.mocks = ZGWExportImportMockData(0) + + def test_export_zaaktype_configs(self): + export = ZGWConfigExport.from_zaaktype_configs( + ZaakTypeConfig.objects.filter(pk=self.mocks.ztc.pk) + ) + rows = export.as_dicts() + + expected = [ + { + "model": "openzaak.zaaktypeconfig", + "fields": { + "urls": '["https://foo.0.maykinmedia.nl"]', + "catalogus": ["DM-0", "123456789"], + "identificatie": "ztc-id-a-0", + "omschrijving": "zaaktypeconfig", + "notify_status_changes": False, + "description": "", + "external_document_upload_url": "", + "document_upload_enabled": False, + "contact_form_enabled": False, + "contact_subject_code": "", + "relevante_zaakperiode": None, + }, + }, + { + "model": "openzaak.zaaktypeinformatieobjecttypeconfig", + "fields": { + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], + "informatieobjecttype_url": "https://foo.0.maykinmedia.nl", + "omschrijving": "informatieobject", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', + "document_upload_enabled": False, + "document_notification_enabled": False, + }, + }, + { + "model": "openzaak.zaaktypestatustypeconfig", + "fields": { + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], + "statustype_url": "https://foo.0.maykinmedia.nl", + "omschrijving": "status omschrijving", + "statustekst": "", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', + "status_indicator": "", + "status_indicator_text": "", + "document_upload_description": "", + "description": "status", + "notify_status_change": True, + "action_required": False, + "document_upload_enabled": True, + "call_to_action_url": "", + "call_to_action_text": "", + "case_link_text": "", + }, + }, + { + "model": "openzaak.zaaktyperesultaattypeconfig", + "fields": { + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], + "resultaattype_url": "https://foo.0.maykinmedia.nl", + "omschrijving": "resultaat", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', + "description": "", + }, + }, + ] + + self.assertEqual(rows, expected) + + class TestCatalogusExport(TestCase): def setUp(self): self.mocks = [ @@ -156,7 +226,7 @@ def setUp(self): ] def test_export_catalogus_configs(self): - export = CatalogusConfigExport.from_catalogus_configs( + export = ZGWConfigExport.from_catalogus_configs( CatalogusConfig.objects.filter(pk=self.mocks[0].catalogus.pk) ) rows = export.as_dicts() @@ -233,9 +303,7 @@ def test_export_catalogus_configs(self): self.assertEqual(rows, expected) def test_export_catalogus_configs_as_jsonl(self): - export = CatalogusConfigExport.from_catalogus_configs( - CatalogusConfig.objects.all() - ) + export = ZGWConfigExport.from_catalogus_configs(CatalogusConfig.objects.all()) rows = list(export.as_jsonl_iter()) expected = [ @@ -622,9 +690,7 @@ def setUp(self): ZGWExportImportMockData() def test_exports_can_be_imported(self): - export = CatalogusConfigExport.from_catalogus_configs( - CatalogusConfig.objects.all() - ) + export = ZGWConfigExport.from_catalogus_configs(CatalogusConfig.objects.all()) import_result = CatalogusConfigImport.from_jsonl_stream_or_string( export.as_jsonl() ) From f4f89ce32765780161ca3be62f01cccb9befb24f Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Wed, 4 Dec 2024 09:19:58 +0100 Subject: [PATCH 2/2] [#2903] Support import of Zaaktype configs without library --- src/open_inwoner/openzaak/admin.py | 83 ++++++++++++++++++- src/open_inwoner/openzaak/import_export.py | 2 +- src/open_inwoner/openzaak/tests/test_admin.py | 12 +-- .../openzaak/tests/test_import_export.py | 40 ++++----- .../admin/catalogusconfig_change_list.html | 2 +- .../admin/zaaktypeconfig_change_list.html | 12 +++ 6 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 src/open_inwoner/templates/admin/zaaktypeconfig_change_list.html diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py index 4dcd59bde7..10bec8e2e7 100644 --- a/src/open_inwoner/openzaak/admin.py +++ b/src/open_inwoner/openzaak/admin.py @@ -17,7 +17,7 @@ from solo.admin import SingletonModelAdmin from open_inwoner.ckeditor5.widgets import CKEditorWidget -from open_inwoner.openzaak.import_export import CatalogusConfigImport, ZGWConfigExport +from open_inwoner.openzaak.import_export import ZGWConfigExport, ZGWConfigImport from open_inwoner.utils.forms import LimitedUploadFileField from .models import ( @@ -133,7 +133,7 @@ def get_urls(self): path( "import-catalogus-dump/", self.admin_site.admin_view(self.process_file_view), - name="upload_zgw_import_file", + name="upload_catalogus_import_file", ), ] return custom_urls + urls @@ -163,7 +163,7 @@ def process_file_view(self, request): try: import_result = ( - CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( target_file_name, storage, ) @@ -371,6 +371,7 @@ def has_delete_permission(self, request, obj=None): @admin.register(ZaakTypeConfig) class ZaakTypeConfigAdmin(admin.ModelAdmin): + change_list_template = "admin/zaaktypeconfig_change_list.html" inlines = [ ZaakTypeInformatieObjectTypeConfigInline, ZaakTypeStatusTypeConfigInline, @@ -434,6 +435,17 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin): ] ordering = ("identificatie", "catalogus__domein") + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path( + "import-zaaktype-dump/", + self.admin_site.admin_view(self.process_file_view), + name="upload_zaaktype_import_file", + ), + ] + return custom_urls + urls + @admin.action(description=_("Export to file")) def export_zaaktype_configs(modeladmin, request, queryset): export = ZGWConfigExport.from_zaaktype_configs(queryset) @@ -446,6 +458,71 @@ def export_zaaktype_configs(modeladmin, request, queryset): ] = 'attachment; filename="zgw-zaaktype-export.json"' return response + def process_file_view(self, request): + form = ImportZGWExportFileForm() + + if request.method == "POST": + form = ImportZGWExportFileForm(request.POST, request.FILES) + if form.is_valid(): + storage = PrivateMediaFileSystemStorage() + timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + target_file_name = f"zgw_import_dump_{timestamp}.json" + storage.save(target_file_name, request.FILES["zgw_export_file"]) + + try: + import_result = ( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( + target_file_name, + storage, + ) + ) + self.message_user( + request, + _( + "%(num_rows)d item(s) processed in total, with %(error_rows)d failing row(s)." + % { + "num_rows": import_result.total_rows_processed, + "error_rows": len(import_result.import_errors), + } + ), + messages.SUCCESS + if not import_result.import_errors + else messages.WARNING, + ) + if errors := import_result.import_errors: + msgs_deduped = set(error.__str__() for error in errors) + error_msg_iterator = ([msg] for msg in msgs_deduped) + + error_msg_html = format_html_join( + "\n", "

- {}

", error_msg_iterator + ) + error_msg_html = format_html( + _("It was not possible to import the following items:") + + f"
{error_msg_html}
" + ) + self.message_user(request, error_msg_html, messages.ERROR) + + return HttpResponseRedirect( + reverse( + "admin:openzaak_zaaktypeconfig_changelist", + ) + ) + except Exception: + logger.exception("Unable to process ZGW import") + self.message_user( + request, + _( + "We were unable to process your upload. Please regenerate the file and try again." + ), + messages.ERROR, + ) + finally: + storage.delete(target_file_name) + + return TemplateResponse( + request, "admin/import_zgw_export_form.html", {"form": form} + ) + def has_add_permission(self, request): return False diff --git a/src/open_inwoner/openzaak/import_export.py b/src/open_inwoner/openzaak/import_export.py index 06a71d01d5..5b668a0f86 100644 --- a/src/open_inwoner/openzaak/import_export.py +++ b/src/open_inwoner/openzaak/import_export.py @@ -277,7 +277,7 @@ def from_zaaktype_configs(cls, zaaktype_configs: QuerySet) -> Self: @dataclasses.dataclass(frozen=True) -class CatalogusConfigImport: +class ZGWConfigImport: """Import CatalogusConfig(s) and all associated relations.""" total_rows_processed: int = 0 diff --git a/src/open_inwoner/openzaak/tests/test_admin.py b/src/open_inwoner/openzaak/tests/test_admin.py index 7078c62065..d7b79c7166 100644 --- a/src/open_inwoner/openzaak/tests/test_admin.py +++ b/src/open_inwoner/openzaak/tests/test_admin.py @@ -192,7 +192,7 @@ def test_import_flow_reports_success(self) -> None: form = self.app.get( reverse( - "admin:upload_zgw_import_file", + "admin:upload_catalogus_import_file", ), user=self.user, ).form @@ -219,13 +219,13 @@ def test_import_flow_reports_success(self) -> None: ) @mock.patch( - "open_inwoner.openzaak.import_export.CatalogusConfigImport.import_from_jsonl_file_in_django_storage" + "open_inwoner.openzaak.import_export.ZGWConfigImport.import_from_jsonl_file_in_django_storage" ) def test_import_flow_errors_reports_failure_to_user(self, m) -> None: m.side_effect = Exception("something went wrong") form = self.app.get( reverse( - "admin:upload_zgw_import_file", + "admin:upload_catalogus_import_file", ), user=self.user, ).form @@ -255,7 +255,7 @@ def test_import_flow_errors_reports_failure_to_user(self, m) -> None: self.assertEqual( response.request.path, reverse( - "admin:upload_zgw_import_file", + "admin:upload_catalogus_import_file", ), ) @@ -273,7 +273,7 @@ def test_import_flow_reports_errors(self) -> None: form = self.app.get( reverse( - "admin:upload_zgw_import_file", + "admin:upload_catalogus_import_file", ), user=self.user, ).form @@ -334,7 +334,7 @@ def test_import_flow_reports_partial_errors(self) -> None: form = self.app.get( reverse( - "admin:upload_zgw_import_file", + "admin:upload_catalogus_import_file", ), user=self.user, ).form diff --git a/src/open_inwoner/openzaak/tests/test_import_export.py b/src/open_inwoner/openzaak/tests/test_import_export.py index 804f1f12b7..a069b28739 100644 --- a/src/open_inwoner/openzaak/tests/test_import_export.py +++ b/src/open_inwoner/openzaak/tests/test_import_export.py @@ -5,7 +5,7 @@ from django.core.files.storage.memory import InMemoryStorage from django.test import TestCase -from open_inwoner.openzaak.import_export import CatalogusConfigImport, ZGWConfigExport +from open_inwoner.openzaak.import_export import ZGWConfigExport, ZGWConfigImport from open_inwoner.openzaak.models import ( CatalogusConfig, ZaakTypeConfig, @@ -352,14 +352,14 @@ def test_import_jsonl_update_success(self): mocks = ZGWExportImportMockData() self.storage.save("import.jsonl", io.StringIO(self.jsonl)) - import_result = CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + import_result = ZGWConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) # check import self.assertEqual( import_result, - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=5, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -439,7 +439,7 @@ def test_import_jsonl_missing_statustype_config(self): # we use `asdict` and replace the Exceptions with string representations # because for Exceptions raised from within dataclasses, equality ==/is identity import_result = dataclasses.asdict( - CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) ) @@ -448,7 +448,7 @@ def test_import_jsonl_missing_statustype_config(self): "ZaakTypeConfig identificatie = 'ztc-id-a-0'" ) import_expected = dataclasses.asdict( - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=6, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -485,7 +485,7 @@ def test_import_jsonl_update_statustype_config_missing_zt_config(self): # we use `asdict` and replace the Exceptions with string representations # because for Exceptions raised from within dataclasses, equality ==/is identity import_result = dataclasses.asdict( - CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) ) @@ -494,7 +494,7 @@ def test_import_jsonl_update_statustype_config_missing_zt_config(self): "ZaakTypeConfig identificatie = 'bogus'" ) import_expected = dataclasses.asdict( - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=6, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -531,7 +531,7 @@ def test_import_jsonl_update_reports_duplicate_db_records(self): # we use `asdict` and replace the Exceptions with string representations # because for Exceptions raised from within dataclasses, equality ==/is identity import_result = dataclasses.asdict( - CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) ) @@ -540,7 +540,7 @@ def test_import_jsonl_update_reports_duplicate_db_records(self): "ZaakTypeConfig identificatie = 'ztc-id-a-0'" ) import_expected = dataclasses.asdict( - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=5, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -564,7 +564,7 @@ def test_import_jsonl_update_reports_duplicate_natural_keys_in_upload_file(self) # we use `asdict` and replace the Exceptions with string representations # because for Exceptions raised from within dataclasses, equality ==/is identity import_result = dataclasses.asdict( - CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + ZGWConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) ) @@ -573,7 +573,7 @@ def test_import_jsonl_update_reports_duplicate_natural_keys_in_upload_file(self) "natural keys: omschrijving = 'status omschrijving', ZaakTypeConfig identificatie = 'ztc-id-a-0'" ) import_expected = dataclasses.asdict( - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=6, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -606,9 +606,7 @@ def test_import_jsonl_fails_with_catalogus_domein_rsin_mismatch(self): with self.assertLogs( logger="open_inwoner.openzaak.import_export", level="ERROR" ) as cm: - import_result = CatalogusConfigImport.from_jsonl_stream_or_string( - import_line - ) + import_result = ZGWConfigImport.from_jsonl_stream_or_string(import_line) self.assertEqual( cm.output, [ @@ -635,7 +633,7 @@ def test_import_jsonl_fails_with_catalogus_domein_rsin_mismatch(self): def test_bad_import_types(self): for bad_type in (set(), list(), b""): with self.assertRaises(ValueError): - CatalogusConfigImport.from_jsonl_stream_or_string(bad_type) + ZGWConfigImport.from_jsonl_stream_or_string(bad_type) def test_valid_input_types_are_accepted(self): ZGWExportImportMockData() @@ -646,10 +644,10 @@ def test_valid_input_types_are_accepted(self): self.jsonl, ): with self.subTest(f"Input type {type(input)}"): - import_result = CatalogusConfigImport.from_jsonl_stream_or_string(input) + import_result = ZGWConfigImport.from_jsonl_stream_or_string(input) self.assertEqual( import_result, - CatalogusConfigImport( + ZGWConfigImport( total_rows_processed=5, catalogus_configs_imported=1, zaaktype_configs_imported=1, @@ -665,9 +663,7 @@ def test_import_is_atomic(self): bad_jsonl = self.jsonl + "\n" + bad_line with self.assertRaises(KeyError): - CatalogusConfigImport.from_jsonl_stream_or_string( - stream_or_string=bad_jsonl - ) + ZGWConfigImport.from_jsonl_stream_or_string(stream_or_string=bad_jsonl) counts = ( CatalogusConfig.objects.count(), @@ -691,8 +687,6 @@ def setUp(self): def test_exports_can_be_imported(self): export = ZGWConfigExport.from_catalogus_configs(CatalogusConfig.objects.all()) - import_result = CatalogusConfigImport.from_jsonl_stream_or_string( - export.as_jsonl() - ) + import_result = ZGWConfigImport.from_jsonl_stream_or_string(export.as_jsonl()) self.assertEqual(import_result.total_rows_processed, 5) diff --git a/src/open_inwoner/templates/admin/catalogusconfig_change_list.html b/src/open_inwoner/templates/admin/catalogusconfig_change_list.html index 41eb9eac60..38a2f2e19e 100644 --- a/src/open_inwoner/templates/admin/catalogusconfig_change_list.html +++ b/src/open_inwoner/templates/admin/catalogusconfig_change_list.html @@ -4,7 +4,7 @@ {% block object-tools-items %} {{ block.super }}
  • - + {% trans "Import from file" %}
  • diff --git a/src/open_inwoner/templates/admin/zaaktypeconfig_change_list.html b/src/open_inwoner/templates/admin/zaaktypeconfig_change_list.html new file mode 100644 index 0000000000..8ea4080c0b --- /dev/null +++ b/src/open_inwoner/templates/admin/zaaktypeconfig_change_list.html @@ -0,0 +1,12 @@ +{% extends "admin/change_list.html" %} +{% load i18n admin_urls %} + +{% block object-tools-items %} +{{ block.super }} +
  • + + {% trans "Import from file" %} + +
  • +{% endblock %} +