diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py index b01ffb8334..71db526f95 100644 --- a/mayan/apps/document_indexing/apps.py +++ b/mayan/apps/document_indexing/apps.py @@ -206,18 +206,22 @@ class DocumentIndexingApp(MayanAppConfig): menu_tools.bind_links(links=(link_rebuild_index_instances,)) post_delete.connect( - handler_delete_empty, dispatch_uid='handler_delete_empty', + dispatch_uid='document_indexing_handler_delete_empty', + receiver=handler_delete_empty, sender=Document ) pre_delete.connect( - handler_remove_document, dispatch_uid='handler_remove_document', + dispatch_uid='document_indexing_handler_remove_document', + receiver=handler_remove_document, sender=Document ) post_document_created.connect( - handler_index_document, - dispatch_uid='handler_index_document', sender=Document + dispatch_uid='document_indexing_handler_index_document', + receiver=handler_index_document, + sender=Document ) post_initial_document_type.connect( - create_default_document_index, - dispatch_uid='create_default_document_index', sender=DocumentType + dispatch_uid='document_indexing_create_default_document_index', + receiver=create_default_document_index, + sender=DocumentType ) diff --git a/mayan/apps/document_indexing/models.py b/mayan/apps/document_indexing/models.py index 38a6ce4536..cc8fc5aaff 100644 --- a/mayan/apps/document_indexing/models.py +++ b/mayan/apps/document_indexing/models.py @@ -190,15 +190,16 @@ class IndexTemplateNode(MPTTModel): else: return self.expression + def get_lock_string(self): + return 'indexing:indexing_template_node_{}'.format(self.pk) + def index_document(self, document, acquire_lock=True, index_instance_node_parent=None): # Avoid another process indexing this same document for the same # template node. This prevents this template node's index instance # nodes from being deleted while the template is evaluated and # documents added to it. if acquire_lock: - lock = locking_backend.acquire_lock( - 'indexing:indexing_template_node_{}'.format(self.pk) - ) + lock = locking_backend.acquire_lock(self.get_lock_string()) # Start transaction after the lock in case the locking backend uses # the database. @@ -212,11 +213,12 @@ class IndexTemplateNode(MPTTModel): 'Removing document "%s" from all index instance nodes', document ) - for index_template_node in self.index_instance_nodes.all(): - index_template_node.remove_document( + for index_instance_node in self.index_instance_nodes.all(): + index_instance_node.remove_document( document=document, acquire_lock=False ) + with transaction.atomic(): if not self.parent: logger.debug( 'IndexTemplateNode; parent: creating empty root index ' @@ -263,6 +265,7 @@ class IndexTemplateNode(MPTTModel): parent=index_instance_node_parent, value=result ) + if self.link_documents: index_instance_node.documents.add(document) @@ -302,11 +305,6 @@ class IndexInstanceNode(MPTTModel): def __str__(self): return self.value - @property - def children(self): - # Convenience method for serializer - return self.get_children() - def delete_empty(self, acquire_lock=True): """ The argument `acquire_lock` controls whether or not this method @@ -318,17 +316,18 @@ class IndexInstanceNode(MPTTModel): # parent template node for the lock if acquire_lock: lock = locking_backend.acquire_lock( - 'indexing:indexing_template_node_{}'.format( - self.index_template_node.pk - ) + self.index_template_node.get_lock_string() ) # Start transaction after the lock in case the locking backend uses # the database. with transaction.atomic(): if self.documents.count() == 0 and self.get_children().count() == 0: if self.parent: - self.delete() self.parent.delete_empty(acquire_lock=False) + # Delete ourselves after the parent is deleted + # Sound counterintuitive but otherwise the tree becomes + # corrupted. Reference: test_models.test_date_based_index + self.delete() if acquire_lock: lock.release() @@ -385,10 +384,9 @@ class IndexInstanceNode(MPTTModel): # parent template node for the lock if acquire_lock: lock = locking_backend.acquire_lock( - 'indexing:indexing_template_node_{}'.format( - self.index_template_node.pk - ) + self.index_template_node.get_lock_string() ) + self.documents.remove(document) self.delete_empty(acquire_lock=False) diff --git a/mayan/apps/document_indexing/tests/test_models.py b/mayan/apps/document_indexing/tests/test_models.py index 1e4976cd41..c93ba0e933 100644 --- a/mayan/apps/document_indexing/tests/test_models.py +++ b/mayan/apps/document_indexing/tests/test_models.py @@ -217,3 +217,38 @@ class IndexTestCase(DocumentTestMixin, BaseTestCase): ) Index.objects.rebuild() + + def test_date_based_index(self): + index = Index.objects.create(label=TEST_INDEX_LABEL) + index.document_types.add(self.document_type) + + level_year = index.node_templates.create( + parent=index.template_root, + expression='{{ document.date_added|date:"Y" }}', + link_documents=False + ) + + index.node_templates.create( + parent=level_year, + expression='{{ document.date_added|date:"m" }}', + link_documents=True + ) + # Index the document created by default + Index.objects.rebuild() + + self.document.delete() + + # Uploading a new should not trigger an error + document = self.upload_document() + + self.assertEqual( + [instance.value for instance in IndexInstanceNode.objects.all().order_by('pk')], + [ + '', force_text(document.date_added.year), + force_text(document.date_added.month).zfill(2) + ] + ) + + self.assertTrue( + document in list(IndexInstanceNode.objects.order_by('pk').last().documents.all()) + )