When changing document types, don't delete the old metadata that is

also found in the new document type. GitLab issue #421.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2017-08-24 19:44:08 -04:00
parent 61d064fb6d
commit 8b9e605e0b
6 changed files with 165 additions and 16 deletions

View File

@@ -32,7 +32,8 @@
- Use the literal 'System' instead of the target name when - Use the literal 'System' instead of the target name when
the action user in unknown. the action user in unknown.
- Remove the view to submit all document for OCR. - Remove the view to submit all document for OCR.
- When changing document types, don't delete the old metadata that is
also found in the new document type. GitLab issue #421.
2.6.4 (2017-07-26) 2.6.4 (2017-07-26)
================== ==================

View File

@@ -26,7 +26,7 @@ from .classes import DocumentMetadataHelper
from .handlers import ( from .handlers import (
handler_index_document, post_document_type_metadata_type_add, handler_index_document, post_document_type_metadata_type_add,
post_document_type_metadata_type_delete, post_document_type_metadata_type_delete,
post_post_document_type_change_metadata post_document_type_change_metadata
) )
from .links import ( from .links import (
link_metadata_add, link_metadata_edit, link_metadata_multiple_add, link_metadata_add, link_metadata_edit, link_metadata_multiple_add,
@@ -236,17 +236,17 @@ class MetadataApp(MayanAppConfig):
post_delete.connect( post_delete.connect(
post_document_type_metadata_type_delete, post_document_type_metadata_type_delete,
dispatch_uid='post_document_type_metadata_type_delete', dispatch_uid='metadata_post_document_type_metadata_type_delete',
sender=DocumentTypeMetadataType sender=DocumentTypeMetadataType
) )
post_document_type_change.connect( post_document_type_change.connect(
post_post_document_type_change_metadata, post_document_type_change_metadata,
dispatch_uid='post_post_document_type_change_metadata', dispatch_uid='metadata_post_document_type_change_metadata',
sender=Document sender=Document
) )
post_save.connect( post_save.connect(
post_document_type_metadata_type_add, post_document_type_metadata_type_add,
dispatch_uid='post_document_type_metadata_type_add', dispatch_uid='metadata_post_document_type_metadata_type_add',
sender=DocumentTypeMetadataType sender=DocumentTypeMetadataType
) )
@@ -254,11 +254,11 @@ class MetadataApp(MayanAppConfig):
post_delete.connect( post_delete.connect(
handler_index_document, handler_index_document,
dispatch_uid='handler_index_document_delete', dispatch_uid='metadata_handler_index_document_delete',
sender=DocumentMetadata sender=DocumentMetadata
) )
post_save.connect( post_save.connect(
handler_index_document, handler_index_document,
dispatch_uid='handler_index_document_save', dispatch_uid='metadata_handler_index_document_save',
sender=DocumentMetadata sender=DocumentMetadata
) )

View File

@@ -33,23 +33,43 @@ def post_document_type_metadata_type_delete(sender, instance, **kwargs):
) )
def post_post_document_type_change_metadata(sender, instance, **kwargs): def post_document_type_change_metadata(sender, instance, **kwargs):
logger.debug('received post_document_type_change') logger.debug('received post_document_type_change')
logger.debug('instance: %s', instance) logger.debug('instance: %s', instance)
# Delete existing document metadata
for metadata in instance.metadata.all(): # Delete existing document metadata types not found in the new document
# type
# First get the existing metadata types not found in the new document
# type
unneeded_metadata = instance.metadata.exclude(
metadata_type__in=instance.document_type.metadata.values(
'metadata_type'
)
)
# Remove the document metadata whose types are not found in the new
# document type
for metadata in unneeded_metadata:
metadata.delete(enforce_required=False) metadata.delete(enforce_required=False)
DocumentMetadata = apps.get_model( DocumentMetadata = apps.get_model(
app_label='metadata', model_name='DocumentMetadata' app_label='metadata', model_name='DocumentMetadata'
) )
# Add new document type metadata types to document # Add the metadata types of the new document type to the document
for document_type_metadata_type in instance.document_type.metadata.filter(required=True): # excluding exisitng document metadata
# get_or_create is not used to avoid a possible triggering of indexes
# or workflow on document change by metadata save signal
new_document_type_metadata_types = instance.document_type.metadata.filter(
required=True
).exclude(metadata_type__in=instance.metadata.values('metadata_type'))
for document_type_metadata_type in new_document_type_metadata_types:
DocumentMetadata.objects.create( DocumentMetadata.objects.create(
document=instance, document=instance,
metadata_type=document_type_metadata_type.metadata_type, metadata_type=document_type_metadata_type.metadata_type,
value=None value=document_type_metadata_type.metadata_type.default
) )

View File

@@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import apps
from django.db import models from django.db import models
@@ -20,3 +21,16 @@ class MetadataTypeManager(models.Manager):
'metadata_type', flat=True 'metadata_type', flat=True
) )
) )
class DocumentTypeMetadataTypeManager(models.Manager):
def get_metadata_types_for(self, document_type):
DocumentType = apps.get_model(
app_label='metadata', model_name='MetadataType'
)
return DocumentType.objects.filter(
pk__in=self.filter(
document_type=document_type
).values_list('metadata_type__pk', flat=True)
)

View File

@@ -12,7 +12,7 @@ from django.utils.translation import ugettext_lazy as _
from documents.models import Document, DocumentType from documents.models import Document, DocumentType
from .classes import MetadataLookup from .classes import MetadataLookup
from .managers import MetadataTypeManager from .managers import DocumentTypeMetadataTypeManager, MetadataTypeManager
from .settings import setting_available_parsers, setting_available_validators from .settings import setting_available_parsers, setting_available_validators
@@ -166,6 +166,11 @@ class DocumentMetadata(models.Model):
return force_text(self.metadata_type) return force_text(self.metadata_type)
def delete(self, enforce_required=True, *args, **kwargs): def delete(self, enforce_required=True, *args, **kwargs):
"""
enforce_required prevents deletion of required metadata at the
model level. It used set to False when deleting document metadata
on document type change.
"""
if enforce_required and self.metadata_type.pk in self.document.document_type.metadata.filter(required=True).values_list('metadata_type', flat=True): if enforce_required and self.metadata_type.pk in self.document.document_type.metadata.filter(required=True).values_list('metadata_type', flat=True):
raise ValidationError( raise ValidationError(
_('Metadata type is required for this document type.') _('Metadata type is required for this document type.')
@@ -212,6 +217,8 @@ class DocumentTypeMetadataType(models.Model):
) )
required = models.BooleanField(default=False, verbose_name=_('Required')) required = models.BooleanField(default=False, verbose_name=_('Required'))
objects = DocumentTypeMetadataTypeManager()
def __str__(self): def __str__(self):
return force_text(self.metadata_type) return force_text(self.metadata_type)

View File

@@ -6,7 +6,10 @@ from django.test import override_settings
from common.tests import BaseTestCase from common.tests import BaseTestCase
from documents.models import DocumentType from documents.models import DocumentType
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE_LABEL from documents.tests import (
TEST_DOCUMENT_TYPE_2_LABEL, TEST_SMALL_DOCUMENT_PATH,
TEST_DOCUMENT_TYPE_LABEL
)
from ..models import MetadataType, DocumentMetadata from ..models import MetadataType, DocumentMetadata
@@ -190,3 +193,107 @@ class MetadataTestCase(BaseTestCase):
self.metadata_type.lookup = 'test1,test2' self.metadata_type.lookup = 'test1,test2'
self.metadata_type.save() self.metadata_type.save()
self.metadata_type.validate_value(document_type=None, value='test1') self.metadata_type.validate_value(document_type=None, value='test1')
def test_add_new_metadata_type_on_document_type_change(self):
"""
When switching document types, add the required metadata of the new
document type, the value to the default of the metadata type.
"""
self.metadata_type.default = TEST_DEFAULT_VALUE
self.metadata_type.save()
self.document_type_2 = DocumentType.objects.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
self.document_type_2.metadata.create(
metadata_type=self.metadata_type, required=True
)
self.document.set_document_type(document_type=self.document_type_2)
self.assertEqual(self.document.metadata.count(), 1)
self.assertEqual(
self.document.metadata.first().value, TEST_DEFAULT_VALUE
)
def test_preserve_metadata_value_on_document_type_change(self):
"""
Preserve the document metadata that is present in the
old and new document types
"""
document_metadata = DocumentMetadata(
document=self.document, metadata_type=self.metadata_type,
value=TEST_DEFAULT_VALUE
)
document_metadata.full_clean()
document_metadata.save()
self.document_type_2 = DocumentType.objects.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
self.document_type_2.metadata.create(metadata_type=self.metadata_type)
self.document.set_document_type(document_type=self.document_type_2)
self.assertEqual(self.document.metadata.count(), 1)
self.assertEqual(
self.document.metadata.first().value, TEST_DEFAULT_VALUE
)
self.assertEqual(
self.document.metadata.first().metadata_type, self.metadata_type
)
def test_delete_metadata_value_on_document_type_change(self):
"""
Delete the old document metadata whose types are not present in the
new document type
"""
document_metadata = DocumentMetadata(
document=self.document, metadata_type=self.metadata_type,
value=TEST_DEFAULT_VALUE
)
document_metadata.full_clean()
document_metadata.save()
self.document_type_2 = DocumentType.objects.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
self.document.set_document_type(document_type=self.document_type_2)
self.assertEqual(self.document.metadata.count(), 0)
def test_duplicate_metadata_value_on_document_type_change(self):
"""
Delete the old document metadata whose types are not present in the
new document type
"""
document_metadata = DocumentMetadata(
document=self.document, metadata_type=self.metadata_type,
value=TEST_DEFAULT_VALUE
)
document_metadata.full_clean()
document_metadata.save()
self.document_type_2 = DocumentType.objects.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
self.document_type_2.metadata.create(
metadata_type=self.metadata_type, required=True
)
self.document.set_document_type(document_type=self.document_type_2)
self.assertEqual(self.document.metadata.count(), 1)
self.assertEqual(
self.document.metadata.first().value, TEST_DEFAULT_VALUE
)
self.assertEqual(
self.document.metadata.first().metadata_type, self.metadata_type
)