summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2025-01-30 13:08:34 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-19 14:45:56 +0100
commit042b8f956369c115536dfe7e33d9c0461dafe539 (patch)
tree90f8577c5e52047be00f5aeb2f31d010f0d65fb2
parentbfae6a086ff5d35c794a5c5c0380bf2332661452 (diff)
downloadIshtar-042b8f956369c115536dfe7e33d9c0461dafe539.tar.bz2
Ishtar-042b8f956369c115536dfe7e33d9c0461dafe539.zip
✨ admin: better management of Import - Item keys, import/export, links all user/imports
-rw-r--r--archaeological_operations/tests.py4
-rw-r--r--ishtar_common/admin.py64
-rw-r--r--ishtar_common/data_importer.py8
-rw-r--r--ishtar_common/forms_common.py16
-rw-r--r--ishtar_common/migrations/0260_itemkey_importer_type.py62
-rw-r--r--ishtar_common/models_common.py33
-rw-r--r--ishtar_common/models_imports.py138
-rw-r--r--ishtar_common/serializers.py19
-rw-r--r--ishtar_common/serializers_utils.py97
9 files changed, 336 insertions, 105 deletions
diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py
index 18e17cca5..26217d7c3 100644
--- a/archaeological_operations/tests.py
+++ b/archaeological_operations/tests.py
@@ -202,7 +202,7 @@ class ImportTest(BaseImportTest):
def init_ope_targetkey(self, imp):
# doing manually connections
- q = Q(importer=imp) | Q(user=imp.user)
+ q = Q(ishtar_import=imp) | Q(user=imp.user) | Q(importer_type=imp.importer_type)
if imp.associated_group:
q |= Q(group=imp.associated_group)
for ik in ItemKey.objects.filter(q).all():
@@ -565,7 +565,7 @@ class ImportOperationTest(ImportTest, TestCase):
other_imp = form.save(self.ishtar_user)
# re-associate with another import
- q = Q(importer=impt) | Q(user=impt.user)
+ q = Q(ishtar_import=impt) | Q(user=impt.user) | Q(importer_type=impt.importer_type)
if impt.associated_group:
q |= Q(group=impt.associated_group)
for ik in ItemKey.objects.filter(q).all():
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py
index 7b1aa48d4..a2dfc63aa 100644
--- a/ishtar_common/admin.py
+++ b/ishtar_common/admin.py
@@ -281,23 +281,25 @@ def serialize_action(dir_name, model_list):
else:
modellist = [modeladmin.model]
opts = modeladmin.model._meta
+ if getattr(modeladmin, "serialize_filter_queryset", None):
+ queryset = queryset.filter(**modeladmin.serialize_filter_queryset)
result = generic_get_results(
modellist, dir_name, result_queryset={opts.object_name: queryset}
)
basename = str(opts).replace(".", "_")
in_memory = BytesIO()
- zip = zipfile.ZipFile(in_memory, "a")
+ current_zip = zipfile.ZipFile(in_memory, "a")
for key in result.keys():
__, model_name = key
- zip.writestr(dir_name + os.sep + model_name + ".json", result[key])
+ current_zip.writestr(dir_name + os.sep + model_name + ".json", result[key])
# info
- zip.writestr("info.json", json.dumps(serialization_info(), indent=2))
+ current_zip.writestr("info.json", json.dumps(serialization_info(), indent=2))
# fix for Linux zip files read in Windows
- for file in zip.filelist:
- file.create_system = 0
- zip.close()
+ for cfile in current_zip.filelist:
+ cfile.create_system = 0
+ current_zip.close()
response = HttpResponse(content_type="application/zip")
response["Content-Disposition"] = "attachment; filename={}.zip".format(basename)
in_memory.seek(0)
@@ -2124,15 +2126,11 @@ class ImporterGroupImporterInline(admin.TabularInline):
extra = 3
-serialize_importer_group_action = serialize_action("common_imports", IMPORT_MODEL_LIST)
-serialize_importer_group_action.short_description = SERIALIZE_DESC
-
-
@admin.register(models.ImporterGroup, site=admin_site)
class ImporterGroupAdmin(ImportJSONActionAdmin):
list_display = ("name", "importer_types_label", "available")
actions = [
- serialize_importer_group_action,
+ serialize_importer_action,
change_value("available", True, _("Make available")),
change_value("available", False, _("Make unavailable")),
]
@@ -2359,6 +2357,7 @@ class ImportAdmin(admin.ModelAdmin):
"creation_date",
)
autocomplete_fields = ["user"]
+ search_fields = ("name", "importer_type__name")
admin_site.register(models.Import, ImportAdmin)
@@ -2377,23 +2376,6 @@ class ImportGroupAdmin(admin.ModelAdmin):
autocomplete_fields = ["user"]
-class TargetKeyGroupAdmin(admin.ModelAdmin):
- list_display = ("name", "all_user_can_use", "all_user_can_modify", "available")
- search_fields = ("name",)
-
-
-admin_site.register(models.TargetKeyGroup, TargetKeyGroupAdmin)
-
-
-class TargetKeyAdmin(admin.ModelAdmin):
- list_display = ("target", "importer_type", "column_nb", "key", "value", "is_set")
- list_filter = ("is_set", "target__column__importer_type")
- search_fields = ("target__target", "value", "key")
-
-
-admin_site.register(models.TargetKey, TargetKeyAdmin)
-
-
class OperationTypeAdmin(GeneralTypeAdmin):
extra_list_display = ["order", "preventive"]
model = models.OperationType
@@ -2410,12 +2392,30 @@ class SpatialReferenceSystemAdmin(GeneralTypeAdmin):
admin_site.register(models.SpatialReferenceSystem, SpatialReferenceSystemAdmin)
-class ItemKeyAdmin(admin.ModelAdmin):
- list_display = ("content_type", "key", "content_object", "importer")
- search_fields = ("key",)
+@admin.register(models.ItemKey, site=admin_site)
+class ItemKeyAdmin(ImportJSONActionAdmin):
+ list_display = (
+ "content_type", "content_type_model", "key", "content_object", "linked_to_all",
+ "linked_to_importer_type", "linked_to_importer", "linked_to_user"
+ )
+ search_fields = ("key", "content_type__model")
+ list_filter = (("content_type", admin.RelatedOnlyFieldListFilter),)
+ autocomplete_fields = ["user", "ishtar_import"]
+ actions = [
+ serialize_type_action
+ ]
+ serialize_filter_queryset = {
+ "user__isnull": True,
+ "group__isnull": True,
+ "ishtar_import__isnull": True
+ }
-admin_site.register(models.ItemKey, ItemKeyAdmin)
+@admin.register(models.TargetKey, site=admin_site)
+class TargetKeyAdmin(admin.ModelAdmin):
+ list_display = ("target", "importer_type", "column_nb", "key", "value", "is_set")
+ list_filter = ("is_set", "target__column__importer_type")
+ search_fields = ("target__target", "value", "key")
@admin.register(models.ImportColumnValue, site=admin_site)
diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py
index defea4da8..0c58e04f7 100644
--- a/ishtar_common/data_importer.py
+++ b/ishtar_common/data_importer.py
@@ -536,7 +536,7 @@ class StrChoiceFormater(Formater, ChoiceChecker):
def new(self, value):
return
- def add_key(self, obj, value, importer=None):
+ def add_key(self, obj, value, ishtar_import=None):
return
def format(self, value):
@@ -575,14 +575,14 @@ class TypeFormater(StrChoiceFormater):
if self.import_instance:
for item in model.objects.all():
self.choices.append((item.pk, str(item)))
- for key in item.get_keys(importer=import_instance):
+ for key in item.get_keys(current_import=import_instance):
self.equiv_dict[key] = item
def prepare(self, value):
return slugify(str(value).strip())
- def add_key(self, obj, value, importer=None):
- obj.add_key(slugify(value), force=True, importer=importer)
+ def add_key(self, obj, value, ishtar_import=None):
+ obj.add_key(slugify(value), force=True, ishtar_import=ishtar_import)
def new(self, value):
values = copy.copy(self.defaults)
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py
index 3fa819a8f..187941bb6 100644
--- a/ishtar_common/forms_common.py
+++ b/ishtar_common/forms_common.py
@@ -605,6 +605,7 @@ class TargetKeyForm(forms.ModelForm):
]
if self.user.is_superuser:
choices += [("all", _("all users"))]
+ choices += [("all-importers", _("all users and importers"))]
self.fields["remember"].choices = choices
self.fields["remember"].widget.attrs["class"] = "auto"
self.remember_choices = choices
@@ -625,7 +626,7 @@ class TargetKeyForm(forms.ModelForm):
def save(self, commit=True):
try:
- super(TargetKeyForm, self).save(commit)
+ super().save(commit)
except ImporterError:
return
if not self.cleaned_data.get("value") or not self.user:
@@ -644,23 +645,28 @@ class TargetKeyForm(forms.ModelForm):
)
remember = self.cleaned_data.get("remember")
+ self.instance.associated_import = self.associated_import
+ force_importer_type, force_all = False, False
if remember == "import" and self.associated_import:
- self.instance.associated_import = self.associated_import
self.instance.associated_user = None
self.instance.associated_group = None
elif remember == "group" and can_edit_group:
- self.instance.associated_import = None
self.instance.associated_user = None
self.instance.associated_group = self.associated_import.associated_group
elif remember == "all" and self.user.is_superuser:
- self.instance.associated_import = None
self.instance.associated_user = None
self.instance.associated_group = None
+ force_importer_type = True
+ elif remember == "all-importers" and self.user.is_superuser:
+ self.instance.associated_user = None
+ self.instance.associated_group = None
+ force_all = True
else:
# for me!
- self.instance.associated_import = None
self.instance.associated_user = self.user.ishtaruser
self.instance.associated_group = None
+ self.instance.create_itemkey(force_importer_type=force_importer_type,
+ force_all=force_all)
self.instance.save()
diff --git a/ishtar_common/migrations/0260_itemkey_importer_type.py b/ishtar_common/migrations/0260_itemkey_importer_type.py
new file mode 100644
index 000000000..789f22162
--- /dev/null
+++ b/ishtar_common/migrations/0260_itemkey_importer_type.py
@@ -0,0 +1,62 @@
+# Generated by Django 2.2.24 on 2025-01-30 15:55
+
+from django.db import migrations, models, connection
+import django.db.models.deletion
+
+
+def migrate_import_type(apps, __):
+ ItemKey = apps.get_model("ishtar_common", "ItemKey")
+ Import = apps.get_model("ishtar_common", "Import")
+
+ with connection.cursor() as cursor:
+ cursor.execute("SELECT id, importer_id FROM ishtar_common_itemkey WHERE importer_id IS NOT NULL;")
+ for ik_id, import_id in cursor.fetchall():
+ importer_type_id = Import.objects.get(pk=import_id).importer_type_id
+ ik = ItemKey.objects.get(pk=ik_id)
+ ik.importer_type_id = importer_type_id
+ ik.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0259_ishtarsiteprofile_default_location_for_treatment'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='targetkey',
+ options={'ordering': ('target', 'key'), 'verbose_name': 'Import - Target key', 'verbose_name_plural': 'Import - Targets keys'},
+ ),
+ migrations.AddField(
+ model_name='itemkey',
+ name='importer_type',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.ImporterType'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='default_location_for_treatment',
+ field=models.ForeignKey(blank=True, help_text='If provided, treatment forms have by default this location set. Furthermore if this location has an organization attached, this organization is set by default.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_warehouse.Warehouse', verbose_name='Default location for treatments'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='find_external_id',
+ field=models.TextField(default='{{get_first_base_find__context_record__external_id}}-{{label}}{% if upstream_count %}-{{upstream_count}}{% endif %}', help_text='Formula to manage find external ID. Change this with care. With incorrect formula, the application might be unusable and import of external data can be destructive.', verbose_name='Find external id'),
+ ),
+ migrations.AlterField(
+ model_name='ishtarsiteprofile',
+ name='no_context_button',
+ field=models.ForeignKey(blank=True, help_text='If provided, a button is displayed on find add page to create a "No context" find', null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_context_records.ContextRecord', verbose_name='Context record for no context button'),
+ ),
+ migrations.RunPython(migrate_import_type),
+ migrations.RenameField(
+ model_name='itemkey',
+ old_name='importer',
+ new_name='ishtar_import'
+ ),
+ migrations.AlterField(
+ model_name='itemkey',
+ name='ishtar_import',
+ field=models.ForeignKey(blank=True, help_text='Specific key to an import', null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Import'),
+ ),
+ ]
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index ed7cae234..4bea24ab5 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -675,22 +675,28 @@ class GeneralType(Cached, models.Model):
self.generate_key(force=True)
return obj
- def add_key(self, key, force=False, importer=None, group=None, user=None):
+ def add_key(self, key, force=False, importer_type=None, ishtar_import=None,
+ group=None, user=None):
ItemKey = apps.get_model("ishtar_common", "ItemKey")
content_type = ContentType.objects.get_for_model(self.__class__)
if (
- not importer
+ not ishtar_import
and not force
and ItemKey.objects.filter(key=key, content_type=content_type).count()
):
return
filtr = {"key": key, "content_type": content_type}
- if group:
+ if importer_type:
+ filtr["importer_type"] = importer_type
+ elif group:
filtr["group"] = group
+ filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None
elif user:
filtr["user"] = user
+ filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None
else:
- filtr["importer"] = importer
+ filtr["ishtar_import"] = ishtar_import
+ filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None
if force:
ItemKey.objects.filter(**filtr).exclude(object_id=self.pk).delete()
filtr["object_id"] = self.pk
@@ -700,18 +706,23 @@ class GeneralType(Cached, models.Model):
for key in (slugify(self.label), self.txt_idx):
self.add_key(key)
- def get_keys(self, importer):
+ def get_keys(self, current_import):
+ importer_type = current_import.importer_type
ItemKey = apps.get_model("ishtar_common", "ItemKey")
keys = [self.txt_idx]
content_type = ContentType.objects.get_for_model(self.__class__)
base_q = Q(content_type=content_type, object_id=self.pk)
- subquery = Q(importer__isnull=True, user__isnull=True, group__isnull=True)
- subquery |= Q(user__isnull=True, group__isnull=True, importer=importer)
- if importer.user:
- subquery |= Q(user=importer.user, group__isnull=True, importer=importer)
- if importer.associated_group:
+ subquery = Q(importer_type__isnull=True, user__isnull=True,
+ group__isnull=True)
+ subquery |= Q(user__isnull=True, group__isnull=True,
+ importer_type=importer_type)
+ if current_import.user:
+ subquery |= Q(user=current_import.user, group__isnull=True,
+ importer_type=importer_type)
+ if current_import.associated_group:
subquery |= Q(
- user__isnull=True, group=importer.associated_group, importer=importer
+ user__isnull=True, group=current_import.associated_group,
+ importer_type=importer_type
)
q = ItemKey.objects.filter(base_q & subquery)
for ik in q.exclude(key=self.txt_idx).all():
diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py
index a33b599c7..5bd230d6b 100644
--- a/ishtar_common/models_imports.py
+++ b/ishtar_common/models_imports.py
@@ -964,7 +964,7 @@ class TargetKeyGroup(models.Model):
class TargetKey(models.Model):
"""
User's link between import source and ishtar database.
- Also temporary used for GeneralType to point missing link before adding
+ Used for GeneralType to point missing link before adding
them in ItemKey table.
A targetkey connection can be create to be applied to one particular
import (associated_import), one particular user (associated_user),
@@ -975,17 +975,17 @@ class TargetKey(models.Model):
target = models.ForeignKey(
ImportTarget, related_name="keys", on_delete=models.CASCADE
)
- key = models.TextField(_("Key"))
+ key = models.TextField(_("Key"), blank=True, default="")
value = models.TextField(_("Value"), blank=True, null=True)
is_set = models.BooleanField(_("Is set"), default=False)
associated_import = models.ForeignKey(
- "Import", blank=True, null=True, on_delete=models.SET_NULL
+ "Import", blank=True, null=True, on_delete=models.CASCADE
)
associated_user = models.ForeignKey(
- "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL
+ "IshtarUser", blank=True, null=True, on_delete=models.CASCADE
)
associated_group = models.ForeignKey(
- TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL
+ TargetKeyGroup, blank=True, null=True, on_delete=models.CASCADE
)
class Meta:
@@ -995,8 +995,8 @@ class TargetKey(models.Model):
"associated_user",
"associated_import",
)
- verbose_name = _("Importer - Target key")
- verbose_name_plural = _("Importer - Targets keys")
+ verbose_name = _("Import - Target key")
+ verbose_name_plural = _("Import - Targets keys")
ordering = ("target", "key")
ADMIN_SECTION = _("Imports")
@@ -1022,34 +1022,36 @@ class TargetKey(models.Model):
return
return self.value
- def save(self, *args, **kwargs):
- obj = super(TargetKey, self).save(*args, **kwargs)
+ def create_itemkey(self, force_importer_type=False, force_all=False):
if not self.value:
- return obj
+ return
v = None
associated_model = self.target.associated_model
- if associated_model and hasattr(self.target.associated_model, "add_key"):
- # pk is given
+ if not associated_model or not hasattr(self.target.associated_model, "add_key"):
+ return
+ # pk is given
+ try:
+ v = self.target.associated_model.objects.get(pk=str(int(self.value)))
+ except (ValueError, self.target.associated_model.DoesNotExist):
+ # try with txt_idx
try:
- v = self.target.associated_model.objects.get(pk=str(int(self.value)))
- except (ValueError, self.target.associated_model.DoesNotExist):
- # try with txt_idx
- try:
- v = self.target.associated_model.objects.get(
- txt_idx=str(self.value)
- )
- except self.target.associated_model.DoesNotExist:
- pass
- if v:
- keys = {}
- if self.associated_group:
- keys["group"] = self.associated_group
- if self.associated_user:
- keys["user"] = self.associated_user
- else:
- keys["importer"] = self.associated_import
- v.add_key(self.key, **keys)
- return obj
+ v = self.target.associated_model.objects.get(
+ txt_idx=str(self.value)
+ )
+ except self.target.associated_model.DoesNotExist:
+ return
+ if force_all:
+ keys = {}
+ elif force_importer_type:
+ keys = {"importer_type": self.associated_import.importer_type}
+ else:
+ keys = {"ishtar_import": self.associated_import}
+ if self.associated_user:
+ keys["user"] = self.associated_user
+ elif self.associated_group:
+ keys["group"] = self.associated_group
+ v.add_key(self.key, **keys)
+ return
TARGET_MODELS = [
@@ -2919,28 +2921,86 @@ class ImportColumnValue(models.Model):
class ItemKey(models.Model):
+ """
+ Importers: links Key -> Item
+ """
key = models.TextField(_("Key"))
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
- object_id = models.PositiveIntegerField()
+ object_id = models.PositiveIntegerField(verbose_name=_("Value ID"))
content_object = GenericForeignKey("content_type", "object_id")
- importer = models.ForeignKey(
+ importer_type = models.ForeignKey(
+ ImporterType,
+ null=True,
+ blank=True,
+ verbose_name=_("Importer type"),
+ on_delete=models.CASCADE,
+ )
+ ishtar_import = models.ForeignKey(
Import,
null=True,
blank=True,
- help_text=_("Specific key to an import"),
- on_delete=models.SET_NULL,
+ verbose_name=_("Import"),
+ on_delete=models.CASCADE,
)
user = models.ForeignKey(
- "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL
+ "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("User")
)
group = models.ForeignKey(
- TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL
+ TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("Group")
)
class Meta:
- verbose_name = _("Import - Item key")
- verbose_name_plural = _("Import - Item keys")
+ verbose_name = _("Importer - Item key")
+ verbose_name_plural = _("Importer - Item keys")
ADMIN_SECTION = _("Imports")
def __str__(self):
return self.key
+
+ @property
+ def content_slug(self):
+ item = self.content_object
+ if hasattr(item, "txt_idx"):
+ key = "txt_idx"
+ elif hasattr(item, "slug"):
+ key = "slug"
+ else:
+ key = "pk"
+ return getattr(item, key)
+
+ def content_type_model(self):
+ return self.content_type.model
+ content_type_model.short_description = _("Content type model")
+
+ def linked_to_all(self):
+ return not self.importer_type and not self.user and not self.group \
+ and not self.ishtar_import
+
+ linked_to_all.boolean = True
+ linked_to_all.short_description = _("Linked to all")
+
+ def linked_to_importer_type(self):
+ return bool(self.importer_type)
+
+ linked_to_importer_type.boolean = True
+ linked_to_importer_type.short_description = _("Linked to importer")
+
+ def linked_to_importer(self):
+ return bool(self.ishtar_import)
+
+ linked_to_importer.boolean = True
+ linked_to_importer.short_description = _("Linked to import")
+
+ def linked_to_user(self):
+ return bool(self.user)
+
+ linked_to_user.boolean = True
+ linked_to_user.short_description = _("Linked to user")
+
+ def linked_to_group(self):
+ return bool(self.group)
+
+ linked_to_group.boolean = True
+ linked_to_group.short_description = _("Linked to group")
diff --git a/ishtar_common/serializers.py b/ishtar_common/serializers.py
index 1469e13a1..3cbe20907 100644
--- a/ishtar_common/serializers.py
+++ b/ishtar_common/serializers.py
@@ -17,11 +17,12 @@ from .models_common import State, Department
from archaeological_operations.models import ActType
from ishtar_common.serializers_utils import (
- generic_get_results,
archive_serialization,
+ CUSTOM_SERIALIZERS,
+ generic_get_results,
generic_archive_files,
- SERIALIZATION_VERSION,
get_model_from_filename,
+ SERIALIZATION_VERSION,
)
from archaeological_operations.serializers import (
@@ -466,7 +467,15 @@ def restore_serialized(
or releasing_locks
or (user and historized)
)
- idx = -1
+ idx = None
+ model_name = f"{model._meta.app_label}.{model._meta.model_name}"
+ if model_name in CUSTOM_SERIALIZERS:
+ current_serializer = CUSTOM_SERIALIZERS[model_name]()
+ for idx, values in enumerate(json.loads(data)):
+ current_serializer.save(values["fields"])
+ if idx is not None:
+ result.append((model._meta.verbose_name, idx + 1))
+ continue
for idx, obj in enumerate(deserialize("json", data)):
extra_attrs = {}
if historized or hasattr(model, "locked"):
@@ -514,6 +523,6 @@ def restore_serialized(
obj.lock_user = None
obj._no_move = True
obj.save()
- if idx >= 0:
- result.append((model.__name__, idx + 1))
+ if idx is not None:
+ result.append((model._meta.verbose_name, idx + 1))
return result
diff --git a/ishtar_common/serializers_utils.py b/ishtar_common/serializers_utils.py
index ada0a62cf..a4494d9c9 100644
--- a/ishtar_common/serializers_utils.py
+++ b/ishtar_common/serializers_utils.py
@@ -4,9 +4,12 @@ import datetime
import json
import importlib
import os
+from rest_framework.renderers import JSONRenderer
+from rest_framework import serializers
import tempfile
from zipfile import ZipFile
+from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.serializers import serialize
from django.db.models import Q
@@ -113,6 +116,70 @@ def archive_serialization(
return archive_name
+class ItemKeySerializer(serializers.ModelSerializer):
+ app = serializers.SerializerMethodField()
+ model = serializers.SerializerMethodField()
+ content_slug = serializers.SerializerMethodField()
+ importer_type = serializers.SerializerMethodField()
+
+ class Meta:
+ model = models.ItemKey
+ fields = ['key', 'app', 'model', 'content_slug', 'importer_type']
+
+ def get_content_slug(self, obj):
+ return obj.content_slug
+
+ def get_app(self, obj):
+ return obj.content_type.app_label
+
+ def get_model(self, obj):
+ return obj.content_type.model
+
+ def get_importer_type(self, obj):
+ return obj.importer_type.slug if obj.importer_type else ""
+
+ def save(self, data):
+ try:
+ ct = ContentType.objects.get(
+ app_label=data["app"],
+ model=data["model"],
+ )
+ except ContentType.DoesNotExist:
+ return
+ model = ct.model_class()
+ if hasattr(model, "txt_idx"):
+ slug = "txt_idx"
+ elif hasattr(model, "slug"):
+ slug = "slug"
+ else:
+ slug = "pk"
+ try:
+ value = model.objects.get(**{slug: data["content_slug"]})
+ except model.DoesNotExist:
+ return
+ importer_type = None
+ if data["importer_type"]:
+ try:
+ importer_type = models.ImporterType.objects.get(
+ slug=data["importer_type"]
+ )
+ except models.ImporterType.DoesNotExist:
+ return
+ obj, created = models.ItemKey.objects.get_or_create(
+ key=data["key"],
+ content_type=ct,
+ importer_type=importer_type,
+ ishtar_import=None,
+ user=None,
+ group=None,
+ defaults={"object_id": value.pk}
+ )
+ if not created:
+ obj.object_id = value.pk
+ obj.save()
+ return obj
+
+
GENERIC_QUERYSET_FILTER = {
"JsonDataSection": {"JsonDataField": "json_data_field__pk__in"},
"Regexp": {
@@ -170,6 +237,8 @@ GENERIC_QUERYSET_FILTER = {
}
}
+CUSTOM_SERIALIZERS = {"ishtar_common.itemkey": ItemKeySerializer}
+
def generic_get_results(
model_list, dirname, no_geo=True, result_queryset=None, serialization_include=None
@@ -212,13 +281,26 @@ def generic_get_results(
q = q.filter(**{recursion + "__isnull": True})
key = (dirname, model_name)
- result[key] = serialize(
- "json",
- q.distinct().all(),
- indent=2,
- use_natural_foreign_keys=True,
- use_natural_primary_keys=True,
- )
+ model_name = f"{model._meta.app_label}.{model._meta.model_name}"
+ is_custom = model_name in CUSTOM_SERIALIZERS
+ if is_custom:
+ current_serializer = CUSTOM_SERIALIZERS[model_name]
+ result[key] = JSONRenderer().render(
+ [{"model": model_name,
+ "fields": current_serializer(item).data}
+ for item in q.distinct().all()]
+ )
+ else:
+ result[key] = serialize(
+ "json",
+ q.distinct().all(),
+ indent=2,
+ use_natural_foreign_keys=True,
+ use_natural_primary_keys=True,
+ )
+ if recursion and is_custom:
+ # TODO
+ raise NotImplementedError("Recursion not managed for this custom serializer")
if recursion:
serialized = [item["id"] for item in q.values("id").all()]
@@ -278,6 +360,7 @@ def generic_get_results(
for k in serialization_include[model.__name__]:
if k in excluded_fields:
excluded_fields.pop(excluded_fields.index(k))
+
if excluded_fields:
new_result = json.loads(result[key])
for idx in range(len(new_result)):