Skip to content

Commit

Permalink
[#2903] Support export of only select ZaakType configs
Browse files Browse the repository at this point in the history
  • Loading branch information
pi-sigma committed Dec 4, 2024
1 parent 064d3ce commit 814eb49
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 54 deletions.
23 changes: 16 additions & 7 deletions src/open_inwoner/openzaak/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand All @@ -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",
Expand Down Expand Up @@ -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

Expand Down
87 changes: 57 additions & 30 deletions src/open_inwoner/openzaak/import_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,17 @@ 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

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,
Expand All @@ -174,9 +172,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):
Expand All @@ -189,50 +210,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)
Expand Down
100 changes: 83 additions & 17 deletions src/open_inwoner/openzaak/tests/test_import_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
)

Expand All @@ -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",
Expand Down Expand Up @@ -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 = [
Expand All @@ -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()
Expand Down Expand Up @@ -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 = [
Expand Down Expand Up @@ -585,9 +653,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()
)
Expand Down

0 comments on commit 814eb49

Please sign in to comment.