diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-22 17:45:05 +0100 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-28 12:15:24 +0100 | 
| commit | d713093cfabbfb8fe68caa4180de999ab70a3f71 (patch) | |
| tree | 21d4c294c6f63e3d85ba8673d83e670adc62df30 | |
| parent | 9d0bb012a463c7ce7805efd25888f938bb7d8867 (diff) | |
| download | Ishtar-d713093cfabbfb8fe68caa4180de999ab70a3f71.tar.bz2 Ishtar-d713093cfabbfb8fe68caa4180de999ab70a3f71.zip  | |
Option to auto-clean associated documents
| -rw-r--r-- | ishtar_common/migrations/0212_auto_20210222_1718.py (renamed from ishtar_common/migrations/0212_auto_20210222_1334.py) | 7 | ||||
| -rw-r--r-- | ishtar_common/models.py | 9 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 36 | ||||
| -rw-r--r-- | ishtar_common/tests.py | 82 | 
4 files changed, 110 insertions, 24 deletions
diff --git a/ishtar_common/migrations/0212_auto_20210222_1334.py b/ishtar_common/migrations/0212_auto_20210222_1718.py index 703b58f2b..5cd506a10 100644 --- a/ishtar_common/migrations/0212_auto_20210222_1334.py +++ b/ishtar_common/migrations/0212_auto_20210222_1718.py @@ -1,5 +1,5 @@  # -*- coding: utf-8 -*- -# Generated by Django 1.11.27 on 2021-02-22 13:34 +# Generated by Django 1.11.27 on 2021-02-22 17:18  from __future__ import unicode_literals  from django.db import migrations, models @@ -20,6 +20,11 @@ class Migration(migrations.Migration):          ),          migrations.AddField(              model_name='ishtarsiteprofile', +            name='clean_redundant_document_association', +            field=models.BooleanField(default=False, help_text='For instance, remove operation association of a document also associated to a find of this operation. Only manage association of operations, context records and finds.', verbose_name='Document - Remove redundant association'), +        ), +        migrations.AddField( +            model_name='ishtarsiteprofile',              name='delete_image_zip_on_archive',              field=models.BooleanField(default=False, verbose_name='Import - Delete image/document zip on archive'),          ), diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 0a2f4bb6f..55fb1566c 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -60,7 +60,7 @@ from django.core.files.base import ContentFile  from django.core.files.uploadedfile import SimpleUploadedFile  from django.core.urlresolvers import reverse  from django.db.models import Q, Max, Count -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import post_save, post_delete, m2m_changed  from django.db.utils import DatabaseError  from django.template import Context, Template  from django.template.defaultfilters import slugify @@ -740,6 +740,13 @@ class IshtarSiteProfile(models.Model, Cached):      delete_image_zip_on_archive = models.BooleanField(          _("Import - Delete image/document zip on archive"), default=False      ) +    clean_redundant_document_association = models.BooleanField( +        _("Document - Remove redundant association"), default=False, +        help_text=_("For instance, remove operation association of a " +                    "document also associated to a find of this operation. " +                    "Only manage association of operations, context records " +                    "and finds.") +    )      config = models.CharField(          _("Alternate configuration"), max_length=200,          choices=ALTERNATE_CONFIGS_CHOICES, diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 2d8966066..17e643631 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -2369,6 +2369,40 @@ class DocumentItem:          return actions +def clean_duplicate_association(document, related_item, action): +    profile = get_current_profile() +    if not profile.clean_redundant_document_association or action != "post_add": +        return +    class_name = related_item.__class__.__name__ +    if class_name not in ("Find", "ContextRecord", "Operation"): +        return +    if class_name == "Find": +        for cr in document.context_records.filter( +                base_finds__find__pk=related_item.pk).all(): +            document.context_records.remove(cr) +        for ope in document.operations.filter( +                context_record__base_finds__find__pk=related_item.pk).all(): +            document.operations.remove(ope) +        return +    if class_name == "ContextRecord": +        for ope in document.operations.filter( +                context_record__pk=related_item.pk).all(): +            document.operations.remove(ope) +        if document.finds.filter( +                base_finds__context_record=related_item.pk).count(): +            document.context_records.remove(related_item) +        return +    if class_name == "Operation": +        if document.context_records.filter( +                operation=related_item.pk).count(): +            document.operations.remove(related_item) +            return +        if document.finds.filter( +                base_finds__context_record__operation=related_item.pk).count(): +            document.operations.remove(related_item) +        return + +  def document_attached_changed(sender, **kwargs):      # associate a default main image      instance = kwargs.get("instance", None) @@ -2388,6 +2422,8 @@ def document_attached_changed(sender, **kwargs):              return      for item in items: +        clean_duplicate_association(instance, item, +                                    kwargs.get("action", None))          for doc in item.documents.all():              doc.regenerate_all_ids()          q = item.documents.filter( diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index 71e599d59..72836fa45 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -2757,7 +2757,7 @@ class PersonQATest(TestCase):  class DocumentTest(TestCase): -    def test_custom_index(self): +    def setUp(self):          Operation = apps.get_model("archaeological_operations", "Operation")          ContextRecord = apps.get_model("archaeological_context_records",                                         "ContextRecord") @@ -2766,25 +2766,27 @@ class DocumentTest(TestCase):          Find = apps.get_model("archaeological_finds", "Find")          operation_type, __ = models.OperationType.objects.get_or_create(              txt_idx="arch_diagnostic", label="Diagnostic") -        ope1 = Operation.objects.create( +        self.ope1 = Operation.objects.create(              code_patriarche="001",              operation_type_id=operation_type.pk) -        ope2 = Operation.objects.create( +        self.ope2 = Operation.objects.create(              code_patriarche="002",              operation_type_id=operation_type.pk)          su, __ = Unit.objects.get_or_create(              txt_idx='stratigraphic-unit', label="Stratigraphic unit", order=1) -        cr1 = ContextRecord.objects.create(operation=ope1, unit=su) -        cr2 = ContextRecord.objects.create(operation=ope2, unit=su) -        bf1 = BaseFind.objects.create(context_record=cr1) -        bf2 = BaseFind.objects.create(context_record=cr2) -        find1 = Find.objects.create() -        find1.base_finds.add(bf1) -        find2 = Find.objects.create() -        find2.base_finds.add(bf2) -        st1 = models.SourceType.objects.create(label="Report", code="REP") -        st2 = models.SourceType.objects.create(label="Illustration", code="ILL") +        self.cr1 = ContextRecord.objects.create(operation=self.ope1, unit=su) +        self.cr2 = ContextRecord.objects.create(operation=self.ope2, unit=su) +        bf1 = BaseFind.objects.create(context_record=self.cr1) +        bf2 = BaseFind.objects.create(context_record=self.cr2) +        self.find1 = Find.objects.create() +        self.find1.base_finds.add(bf1) +        self.find2 = Find.objects.create() +        self.find2.base_finds.add(bf2) +        self.st1 = models.SourceType.objects.create(label="Report", code="REP") +        self.st2 = models.SourceType.objects.create(label="Illustration", +                                                    code="ILL") +    def test_custom_index(self):          profile, created = models.IshtarSiteProfile.objects.get_or_create(              slug='default', active=True)          profile.document_complete_identifier = \ @@ -2792,25 +2794,25 @@ class DocumentTest(TestCase):          profile.document_custom_index = "operation"          profile.save() -        doc = models.Document.objects.create(source_type=st1, +        doc = models.Document.objects.create(source_type=self.st1,                                               title="Operation report") -        doc.operations.add(ope1) +        doc.operations.add(self.ope1)          doc = models.Document.objects.get(pk=doc.pk)          self.assertEqual(doc.complete_identifier, "001-REP-1") -        doc2 = models.Document.objects.create(source_type=st2, +        doc2 = models.Document.objects.create(source_type=self.st2,                                                title="Illustration CR") -        doc2.context_records.add(cr1) +        doc2.context_records.add(self.cr1)          doc2 = models.Document.objects.get(pk=doc2.pk)          self.assertEqual(doc2.complete_identifier, "001-ILL-2") -        doc3 = models.Document.objects.create(source_type=st1, +        doc3 = models.Document.objects.create(source_type=self.st1,                                                title="Operation report 2") -        doc3.operations.add(ope2) +        doc3.operations.add(self.ope2)          doc3 = models.Document.objects.get(pk=doc3.pk)          self.assertEqual(doc3.complete_identifier, "002-REP-1") -        doc3.operations.add(ope1) +        doc3.operations.add(self.ope1)          doc3.custom_index = None          doc3.save()          doc3 = models.Document.objects.get(pk=doc3.pk) @@ -2823,20 +2825,56 @@ class DocumentTest(TestCase):              "{{ \"%03d\" % (custom_index|int)}}{% else %}no-code{% endif %}"          profile.save() -        doc3.operations.remove(ope1) +        doc3.operations.remove(self.ope1)          doc3.custom_index = None          doc3.complete_identifier = ""          doc3.save()          doc3 = models.Document.objects.get(pk=doc3.pk)          self.assertEqual(doc3.complete_identifier, '002-REP-001') -        doc3.operations.remove(ope2) +        doc3.operations.remove(self.ope2)          doc3.custom_index = None          doc3.complete_identifier = ""          doc3.save()          doc3 = models.Document.objects.get(pk=doc3.pk)          self.assertEqual(doc3.complete_identifier, 'no-code') +    def test_clean_duplicate_association(self): +        doc = models.Document.objects.create(source_type=self.st1, +                                             title="Operation report") +        doc.operations.add(self.ope1) +        doc.context_records.add(self.cr1) +        doc = models.Document.objects.get(pk=doc.pk) +        self.assertEqual(doc.operations.count(), 1) + +        doc.context_records.remove(self.cr1) +        profile = models.get_current_profile() +        profile.clean_redundant_document_association = True +        profile.save() + +        doc = models.Document.objects.get(pk=doc.pk) +        doc.context_records.add(self.cr1) + +        doc = models.Document.objects.get(pk=doc.pk) +        self.assertEqual(doc.context_records.count(), 1) +        self.assertEqual(doc.operations.count(), 0) + +        doc.context_records.remove(self.cr1) +        doc.operations.add(self.ope1) +        doc.context_records.add(self.cr2) +        self.assertEqual(doc.operations.count(), 1)  # different ope keep it +        self.assertEqual(doc.context_records.count(), 1) + +        # adding a find - remove the operation +        doc.finds.add(self.find1) +        self.assertEqual(doc.finds.count(), 1) +        self.assertEqual(doc.context_records.count(), 1) +        self.assertEqual(doc.operations.count(), 0) + +        # adding an operation is undo +        doc.operations.add(self.ope1) +        self.assertEqual(doc.operations.count(), 0) +  class JinjaFilterTest(TestCase):      def test_splitpart(self):  | 
