Refactor document indexing app

Convert half the widget to HTML widgets.

Rename links and views to use the nomeclature _template_
and _instance_ to differenciate between index instances
and index templates.

Update URL parameters to use the "_id" form.

Add more tests.

Add model permission inheritance to the IndexTemplateNode,
and IndexInstanceNode models.

Remove the level and document count display from the
instance node. Display instead the total items.

Use a FilteredSelectionForm subclass to display the list
of index templates to rebuild.

Add missing icons.

Add keyword arguments to links.

Modernize tests to use the document test mixin.

Update the permission requirements for the index template
document type selection screen. The document type view
permission is now required in addition to the index
template edit permission.

Use ExternalObjectMixin to reduce the code in all views.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-02-05 05:40:01 -04:00
parent 4ab2b4fee0
commit bd12d587ee
17 changed files with 947 additions and 680 deletions

View File

@@ -233,6 +233,10 @@
entire converter class is no longer cached and instead loaded entire converter class is no longer cached and instead loaded
on demand. This allows the garbage collector to clear the memory on demand. This allows the garbage collector to clear the memory
used. used.
- Update the permission requirements for the index template
document type selection screen. The document type view
permission is now required in addition to the index
template edit permission.
3.1.9 (2018-11-01) 3.1.9 (2018-11-01)
================== ==================

View File

@@ -27,23 +27,25 @@ from .handlers import (
handler_index_document, handler_post_save_index_document, handler_index_document, handler_post_save_index_document,
handler_remove_document handler_remove_document
) )
from .html_widgets import (
IndexInstanceNodeWidget, IndexTemplateNodeIndentationWidget,
get_instance_link
)
from .licenses import * # NOQA from .licenses import * # NOQA
from .links import ( from .links import (
link_document_index_list, link_index_main_menu, link_index_setup, link_document_index_instance_list, link_index_instances_rebuild,
link_index_setup_create, link_index_setup_delete, link_index_main_menu, link_index_setup, link_index_template_create,
link_index_setup_document_types, link_index_setup_edit, link_index_template_delete, link_index_template_document_types,
link_index_setup_list, link_index_setup_view, link_rebuild_index_instances, link_index_template_edit, link_index_template_list,
link_template_node_create, link_template_node_delete, link_index_template_view, link_index_template_node_create,
link_template_node_edit link_index_template_node_delete, link_index_template_node_edit
) )
from .permissions import ( from .permissions import (
permission_document_indexing_create, permission_document_indexing_delete, permission_document_indexing_create, permission_document_indexing_delete,
permission_document_indexing_edit, permission_document_indexing_edit, permission_document_indexing_instance_view,
permission_document_indexing_instance_view,
permission_document_indexing_rebuild, permission_document_indexing_view permission_document_indexing_rebuild, permission_document_indexing_view
) )
from .queues import * # NOQA from .queues import * # NOQA
from .widgets import get_instance_link, index_instance_item_link, node_level
class DocumentIndexingApp(MayanAppConfig): class DocumentIndexingApp(MayanAppConfig):
@@ -86,53 +88,63 @@ class DocumentIndexingApp(MayanAppConfig):
) )
) )
SourceColumn(attribute='label', is_identifier=True, source=Index) ModelPermission.register_inheritance(
SourceColumn(attribute='slug', source=Index) model=IndexTemplateNode, related='index'
)
ModelPermission.register_inheritance(
model=IndexInstanceNode, related='index_template_node__index'
)
SourceColumn( SourceColumn(
attribute='enabled', source=Index, widget=TwoStateWidget attribute='label', is_identifier=True, is_sortable=True, source=Index
)
SourceColumn(
attribute='slug', include_label=True, is_sortable=True, source=Index
)
SourceColumn(
attribute='enabled', include_label=True, is_sortable=True,
source=Index, widget=TwoStateWidget
) )
SourceColumn( SourceColumn(
func=lambda context: context[ func=lambda context: context[
'object' 'object'
].instance_root.get_descendants_count(), label=_('Total levels'), ].instance_root.get_descendants_count(), include_label=True,
source=IndexInstance, label=_('Total levels'), source=IndexInstance,
) )
SourceColumn( SourceColumn(
func=lambda context: context[ func=lambda context: context[
'object' 'object'
].instance_root.get_descendants_document_count( ].instance_root.get_descendants_document_count(
user=context['request'].user user=context['request'].user
), label=_('Total documents'), source=IndexInstance ), include_label=True, label=_('Total documents'), source=IndexInstance
)
SourceColumn(
label=_('Level'), is_identifier=True, source=IndexTemplateNode,
widget=IndexTemplateNodeIndentationWidget
)
SourceColumn(
attribute='enabled', include_label=True, is_sortable=True,
source=IndexTemplateNode, widget=TwoStateWidget
)
SourceColumn(
attribute='link_documents', include_label=True,
is_sortable=True, source=IndexTemplateNode, widget=TwoStateWidget
) )
SourceColumn( SourceColumn(
func=lambda context: node_level(context['object']), is_identifier=True, is_sortable=True, label=_('Level'),
label=_('Level'), source=IndexTemplateNode sort_field='value', source=IndexInstanceNode,
) widget=IndexInstanceNodeWidget
SourceColumn(
attribute='enabled', source=IndexTemplateNode,
widget=TwoStateWidget
)
SourceColumn(
attribute='link_documents', source=IndexTemplateNode,
widget=TwoStateWidget
) )
SourceColumn(
func=lambda context: index_instance_item_link(context['object']),
label=_('Level'), source=IndexInstanceNode
)
SourceColumn(
func=lambda context: context['object'].get_descendants_count(),
label=_('Levels'), source=IndexInstanceNode
)
SourceColumn( SourceColumn(
func=lambda context: context[ func=lambda context: context[
'object' 'object'
].get_descendants_document_count( ].get_item_count(
user=context['request'].user user=context['request'].user
), label=_('Documents'), source=IndexInstanceNode ), include_label=True, label=_('Items'), source=IndexInstanceNode
) )
SourceColumn( SourceColumn(
@@ -174,35 +186,35 @@ class DocumentIndexingApp(MayanAppConfig):
) )
menu_facet.bind_links( menu_facet.bind_links(
links=(link_document_index_list,), sources=(Document,) links=(link_document_index_instance_list,), sources=(Document,)
) )
menu_list_facet.bind_links( menu_list_facet.bind_links(
links=( links=(
link_acl_list, link_index_setup_document_types, link_acl_list, link_index_template_document_types,
link_index_setup_view, link_index_template_view,
), sources=(Index,) ), sources=(Index,)
) )
menu_object.bind_links( menu_object.bind_links(
links=( links=(
link_index_setup_edit, link_index_setup_delete link_index_template_edit, link_index_template_delete
), sources=(Index,) ), sources=(Index,)
) )
menu_object.bind_links( menu_object.bind_links(
links=( links=(
link_template_node_create, link_template_node_edit, link_index_template_node_create, link_index_template_node_edit,
link_template_node_delete link_index_template_node_delete
), sources=(IndexTemplateNode,) ), sources=(IndexTemplateNode,)
) )
menu_main.bind_links(links=(link_index_main_menu,), position=98) menu_main.bind_links(links=(link_index_main_menu,), position=98)
menu_secondary.bind_links( menu_secondary.bind_links(
links=(link_index_setup_list, link_index_setup_create), links=(link_index_template_list, link_index_template_create),
sources=( sources=(
Index, 'indexing:index_setup_list', Index, 'indexing:index_template_list',
'indexing:index_setup_create' 'indexing:index_template_create'
) )
) )
menu_setup.bind_links(links=(link_index_setup,)) menu_setup.bind_links(links=(link_index_setup,))
menu_tools.bind_links(links=(link_rebuild_index_instances,)) menu_tools.bind_links(links=(link_index_instances_rebuild,))
post_delete.connect( post_delete.connect(
dispatch_uid='document_indexing_handler_delete_empty', dispatch_uid='document_indexing_handler_delete_empty',

View File

@@ -4,30 +4,23 @@ from django import forms
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.classes import ModelProperty from mayan.apps.common.classes import ModelProperty
from mayan.apps.common.forms import FilteredSelectionForm
from mayan.apps.documents.models import Document from mayan.apps.documents.models import Document
from .models import Index, IndexTemplateNode from .models import Index, IndexTemplateNode
from .permissions import permission_document_indexing_rebuild from .permissions import permission_document_indexing_rebuild
class IndexListForm(forms.Form): class IndexTemplateFilteredForm(FilteredSelectionForm):
indexes = forms.ModelMultipleChoiceField( class Meta:
help_text=_('Indexes to be queued for rebuilding.'), allow_multiple = True
label=_('Indexes'), queryset=Index.objects.none(), field_name = 'index_templates'
required=False, widget=forms.widgets.CheckboxSelectMultiple() help_text = _('Index templates to be queued for rebuilding.')
) label = _('Index templates')
queryset = Index.objects.filter(enabled=True)
def __init__(self, *args, **kwargs): permission = permission_document_indexing_rebuild
user = kwargs.pop('user') widget_attributes = {'class': 'select2'}
super(IndexListForm, self).__init__(*args, **kwargs)
queryset = AccessControlList.objects.restrict_queryset(
permission=permission_document_indexing_rebuild,
queryset=Index.objects.filter(enabled=True),
user=user
)
self.fields['indexes'].queryset = queryset
class IndexTemplateNodeForm(forms.ModelForm): class IndexTemplateNodeForm(forms.ModelForm):

View File

@@ -1,10 +1,33 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import apps from django.template.loader import render_to_string
from django.utils.encoding import force_text
from django.utils.html import escape, mark_safe from django.utils.html import escape, mark_safe
from .icons import icon_index, icon_index_level_up, icon_node_with_documents from .icons import (
icon_index, icon_index_level_up,
icon_index_instance_node_with_documents
)
class IndexInstanceNodeWidget(object):
def render(self, name, value):
return render_to_string(
template_name='document_indexing/index_instance_node.html',
context={
'index_instance_node': value,
}
)
class IndexTemplateNodeIndentationWidget(object):
def render(self, name, value):
return render_to_string(
template_name='document_indexing/index_template_node_indentation.html',
context={
'index_template_node': value,
'index_template_node_level': range(value.get_level()),
}
)
def get_instance_link(index_instance_node): def get_instance_link(index_instance_node):
@@ -19,45 +42,6 @@ def get_instance_link(index_instance_node):
) )
def index_instance_item_link(index_instance_item):
#TODO: Replace with a file template
IndexInstanceNode = apps.get_model(
app_label='document_indexing', model_name='IndexInstanceNode'
)
if isinstance(index_instance_item, IndexInstanceNode):
if index_instance_item.index_template_node.link_documents:
icon = icon_node_with_documents.render()
else:
icon = icon_index_level_up.render()
else:
icon = ''
return mark_safe(
s='%(icon)s&nbsp;<a href="%(url)s">%(text)s</a>' % {
'url': index_instance_item.get_absolute_url(),
'icon': icon,
'text': index_instance_item
}
)
def node_level(node):
"""
Render an indented tree like output for a specific node
"""
#TODO: Replace with a file template
return mark_safe(
s=''.join(
[
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' * node.get_level(),
'' if node.is_root_node() else icon_index_level_up.render(),
force_text(node)
]
)
)
def node_tree(node, user): def node_tree(node, user):
#TODO: Replace with a file template #TODO: Replace with a file template
result = [] result = []
@@ -71,7 +55,7 @@ def node_tree(node, user):
else: else:
element = ancestor element = ancestor
if element.index_template_node.link_documents: if element.index_template_node.link_documents:
icon = icon_node_with_documents icon = icon_index_instance_node_with_documents
else: else:
icon = icon_index_level_up icon = icon_index_level_up

View File

@@ -2,14 +2,37 @@ from __future__ import absolute_import, unicode_literals
from mayan.apps.appearance.classes import Icon from mayan.apps.appearance.classes import Icon
icon_document_index_list = Icon(driver_name='fontawesome', symbol='list-ul') icon_document_index_instance_list = Icon(
driver_name='fontawesome', symbol='list-ul'
)
icon_index = Icon(driver_name='fontawesome', symbol='list-ul')
icon_index_level_up = Icon( icon_index_level_up = Icon(
driver_name='fontawesomecss', css_classes='fa-level-up-alt fa-rotate-90' driver_name='fontawesomecss', css_classes='fa-level-up-alt fa-rotate-90'
) )
icon_index = Icon(driver_name='fontawesome', symbol='list-ul') icon_index_instance_node_with_documents = Icon(
icon_index_create = Icon(driver_name='fontawesome', symbol='plus') driver_name='fontawesome', symbol='folder'
icon_index_setup_view = Icon(driver_name='fontawesome', symbol='folder-open') )
icon_node_with_documents = Icon(driver_name='fontawesome', symbol='folder') icon_index_instances_rebuild = Icon(
icon_rebuild_index_instances = Icon(
driver_name='fontawesome', symbol='list-ul' driver_name='fontawesome', symbol='list-ul'
) )
icon_index_template_create = Icon(
driver_name='fontawesome-dual', primary_symbol='list-ul',
secondary_symbol='plus'
)
icon_index_template_delete = Icon(driver_name='fontawesome', symbol='times')
icon_index_template_edit = Icon(driver_name='fontawesome', symbol='pencil-alt')
icon_index_template_list = Icon(driver_name='fontawesome', symbol='list-ul')
icon_index_template_node_create = Icon(
driver_name='fontawesomecss', css_classes='fa-level-up-alt fa-rotate-90'
)
icon_index_template_node_delete = Icon(
driver_name='fontawesome', symbol='times'
)
icon_index_template_node_edit = Icon(
driver_name='fontawesome', symbol='pencil-alt'
)
icon_index_template_view = Icon(driver_name='fontawesome', symbol='folder-open')

View File

@@ -6,8 +6,11 @@ from mayan.apps.documents.icons import icon_document_type
from mayan.apps.navigation import Link, get_cascade_condition from mayan.apps.navigation import Link, get_cascade_condition
from .icons import ( from .icons import (
icon_document_index_list, icon_index, icon_index_create, icon_document_index_instance_list, icon_index, icon_index_template_create,
icon_index_setup_view, icon_rebuild_index_instances icon_index_template_delete, icon_index_template_edit,
icon_index_template_list, icon_index_template_node_create,
icon_index_template_node_delete, icon_index_template_node_edit,
icon_index_template_view, icon_index_instances_rebuild
) )
from .permissions import ( from .permissions import (
permission_document_indexing_create, permission_document_indexing_delete, permission_document_indexing_create, permission_document_indexing_delete,
@@ -17,20 +20,30 @@ from .permissions import (
) )
def is_not_root_node(context): def condition_is_not_root_node(context):
return not context['resolved_object'].is_root_node() return not context['resolved_object'].is_root_node()
link_document_index_list = Link( link_document_index_instance_list = Link(
args='resolved_object.pk', icon_class=icon_document_index_list, icon_class=icon_document_index_instance_list,
text=_('Indexes'), view='indexing:document_index_list', kwargs={'document_id': 'resolved_object.pk'}, text=_('Indexes'),
view='indexing:document_index_instace_list',
)
link_index_instances_rebuild = Link(
condition=get_cascade_condition(
app_label='document_indexing', model_name='Index',
object_permission=permission_document_indexing_rebuild,
), icon_class=icon_index_instances_rebuild,
description=_(
'Deletes and creates from scratch all the document indexes.'
), text=_('Rebuild indexes'), view='indexing:index_instances_rebuild'
) )
link_index_main_menu = Link( link_index_main_menu = Link(
condition=get_cascade_condition( condition=get_cascade_condition(
app_label='document_indexing', model_name='Index', app_label='document_indexing', model_name='Index',
object_permission=permission_document_indexing_instance_view, object_permission=permission_document_indexing_instance_view,
), icon_class=icon_index, text=_('Indexes'), ), icon_class=icon_index, text=_('Indexes'),
view='indexing:index_list' view='indexing:index_instance_list'
) )
link_index_setup = Link( link_index_setup = Link(
condition=get_cascade_condition( condition=get_cascade_condition(
@@ -38,55 +51,53 @@ link_index_setup = Link(
object_permission=permission_document_indexing_view, object_permission=permission_document_indexing_view,
view_permission=permission_document_indexing_create, view_permission=permission_document_indexing_create,
), icon_class=icon_index, text=_('Indexes'), ), icon_class=icon_index, text=_('Indexes'),
view='indexing:index_setup_list' view='indexing:index_template_list'
) )
link_index_setup_list = Link( link_index_template_list = Link(
text=_('Indexes'), view='indexing:index_setup_list' icon_class=icon_index_template_list,
text=_('Indexes'), view='indexing:index_template_list'
) )
link_index_setup_create = Link( link_index_template_create = Link(
icon_class=icon_index_create, icon_class=icon_index_template_create,
permission=permission_document_indexing_create, text=_('Create index'), permission=permission_document_indexing_create, text=_('Create index'),
view='indexing:index_setup_create' view='indexing:index_template_create'
) )
link_index_setup_edit = Link( link_index_template_delete = Link(
args='resolved_object.pk', icon_class=icon_index_template_delete,
permission=permission_document_indexing_edit, text=_('Edit'), kwargs={'index_template_id': 'resolved_object.pk'},
view='indexing:index_setup_edit',
)
link_index_setup_delete = Link(
args='resolved_object.pk',
permission=permission_document_indexing_delete, tags='dangerous', permission=permission_document_indexing_delete, tags='dangerous',
text=_('Delete'), view='indexing:index_setup_delete', text=_('Delete'), view='indexing:index_template_delete'
) )
link_index_setup_view = Link( link_index_template_edit = Link(
args='resolved_object.pk', icon_class=icon_index_setup_view, icon_class=icon_index_template_edit,
kwargs={'index_template_id': 'resolved_object.pk'},
permission=permission_document_indexing_edit, text=_('Edit'),
view='indexing:index_template_edit'
)
link_index_template_view = Link(
kwargs={'index_template_id': 'resolved_object.pk'}, icon_class=icon_index_template_view,
permission=permission_document_indexing_edit, text=_('Tree template'), permission=permission_document_indexing_edit, text=_('Tree template'),
view='indexing:index_setup_view', view='indexing:index_template_view'
) )
link_index_setup_document_types = Link( link_index_template_document_types = Link(
args='resolved_object.pk', icon_class=icon_document_type, kwargs={'index_template_id': 'resolved_object.pk'}, icon_class=icon_document_type,
permission=permission_document_indexing_edit, text=_('Document types'), permission=permission_document_indexing_edit, text=_('Document types'),
view='indexing:index_setup_document_types', view='indexing:index_template_document_types'
) )
link_rebuild_index_instances = Link( link_index_template_node_create = Link(
condition=get_cascade_condition( icon_class=icon_index_template_node_create,
app_label='document_indexing', model_name='Index', kwargs={'index_template_node_id': 'resolved_object.pk'},
object_permission=permission_document_indexing_rebuild, text=_('New node'), view='indexing:index_template_node_create'
), icon_class=icon_rebuild_index_instances,
description=_(
'Deletes and creates from scratch all the document indexes.'
),
text=_('Rebuild indexes'), view='indexing:rebuild_index_instances'
) )
link_template_node_create = Link( link_index_template_node_delete = Link(
args='resolved_object.pk', text=_('New child node'), condition=condition_is_not_root_node,
view='indexing:template_node_create', icon_class=icon_index_template_node_delete,
kwargs={'index_template_node_id': 'resolved_object.pk'}, tags='dangerous',
text=_('Delete'), view='indexing:index_template_node_delete'
) )
link_template_node_edit = Link( link_index_template_node_edit = Link(
args='resolved_object.pk', condition=is_not_root_node, text=_('Edit'), condition=condition_is_not_root_node,
view='indexing:template_node_edit', icon_class=icon_index_template_node_edit,
) kwargs={'index_template_node_id': 'resolved_object.pk'}, text=_('Edit'),
link_template_node_delete = Link( view='indexing:index_template_node_edit'
args='resolved_object.pk', condition=is_not_root_node, tags='dangerous',
text=_('Delete'), view='indexing:template_node_delete',
) )

View File

@@ -66,7 +66,7 @@ class Index(models.Model):
try: try:
return reverse( return reverse(
viewname='indexing:index_instance_node_view', viewname='indexing:index_instance_node_view',
kwargs={'index_instance_node_pk': self.instance_root.pk} kwargs={'index_instance_node_id': self.instance_root.pk}
) )
except IndexInstanceNode.DoesNotExist: except IndexInstanceNode.DoesNotExist:
return '#' return '#'
@@ -183,7 +183,7 @@ class IndexTemplateNode(MPTTModel):
) )
index = models.ForeignKey( index = models.ForeignKey(
on_delete=models.CASCADE, related_name='node_templates', to=Index, on_delete=models.CASCADE, related_name='node_templates', to=Index,
verbose_name=_('Index') verbose_name=_('Index template')
) )
expression = models.TextField( expression = models.TextField(
help_text=_( help_text=_(
@@ -356,7 +356,7 @@ class IndexInstanceNode(MPTTModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse( return reverse(
viewname='indexing:index_instance_node_view', viewname='indexing:index_instance_node_view',
kwargs={'index_instance_node_pk': self.pk} kwargs={'index_instance_node_id': self.pk}
) )
def get_children_count(self): def get_children_count(self):

View File

@@ -58,13 +58,13 @@ def task_index_document(self, document_id):
@app.task(bind=True, default_retry_delay=RETRY_DELAY, ignore_result=True) @app.task(bind=True, default_retry_delay=RETRY_DELAY, ignore_result=True)
def task_rebuild_index(self, index_id): def task_rebuild_index(self, index_template_id):
Index = apps.get_model( Index = apps.get_model(
app_label='document_indexing', model_name='Index' app_label='document_indexing', model_name='Index'
) )
try: try:
index = Index.objects.get(pk=index_id) index = Index.objects.get(pk=index_template_id)
index.rebuild() index.rebuild()
except LockError as exception: except LockError as exception:
# This index is being rebuilt by another task, retry later # This index is being rebuilt by another task, retry later

View File

@@ -0,0 +1,13 @@
{% load appearance_tags %}
{% get_icon 'mayan.apps.document_indexing.icons.icon_index_instance_node_with_documents' as icon_index_instance_node_with_documents %}
{% get_icon 'mayan.apps.document_indexing.icons.icon_index_level_up' as icon_index_level_up %}
<a href="{{ index_instance_node.get_absolute_url }}">
{% if index_instance_node.index_template_node.link_documents %}
{{ icon_index_instance_node_with_documents }}
{% else %}
{{ icon_index_level_up }}
{% endif %}
{{ index_instance_node }}
</a>

View File

@@ -0,0 +1,11 @@
{% load appearance_tags %}
{% get_icon 'mayan.apps.document_indexing.icons.icon_index_level_up' as icon_index_level_up %}
{% for level in index_template_node_level %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{% endfor %}
{% if not index_template_node.is_root_node %}
{{ icon_index_level_up }}
{% endif %}
{{ index_template_node }}

View File

@@ -20,5 +20,4 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -9,3 +9,5 @@ TEST_METADATA_VALUE = '0001'
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{{{ document.get_metadata("{}").value }}}}'.format(TEST_METADATA_TYPE_NAME) TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{{{ document.get_metadata("{}").value }}}}'.format(TEST_METADATA_TYPE_NAME)
TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION = '{{ document.label }}' TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION = '{{ document.label }}'
TEST_INDEX_TEMPLATE_DOCUMENT_DESCRIPTION_EXPRESSION = '{{ document.description }}' TEST_INDEX_TEMPLATE_DOCUMENT_DESCRIPTION_EXPRESSION = '{{ document.description }}'
TEST_INDEX_TEMPLATE_DOCUMENT_UUID_EXPRESSION = '{{ document.uuid }}'
TEST_INDEX_TEMPLATE_NODE_EXPRESSION_EDITED = 'expression edited'

View File

@@ -2,13 +2,30 @@ from __future__ import unicode_literals
from ..models import Index from ..models import Index
from .literals import TEST_INDEX_LABEL from .literals import (
TEST_INDEX_LABEL, TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION
)
class DocumentIndexingTestMixin(object): class IndexTemplateTestMixin(object):
def _create_index(self): def _create_index_template(self, add_document_type=False):
# Create empty index # Create empty index
self.index = Index.objects.create(label=TEST_INDEX_LABEL) self.test_index_template = Index.objects.create(label=TEST_INDEX_LABEL)
# Add our document type to the new index if add_document_type:
self.index.document_types.add(self.document_type) # Add our document type to the new index
self.test_index_template.document_types.add(self.test_document_type)
def _create_index_template_node(self, expression=None, rebuild=False):
self._create_index_template(add_document_type=True)
expression = expression or TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION
self.test_index_template_node = self.test_index_template.node_templates.create(
parent=self.test_index_template.template_root,
expression=expression, link_documents=True
)
# Rebuild indexes
if rebuild:
Index.objects.rebuild()

View File

@@ -3,9 +3,7 @@ from __future__ import unicode_literals
from django.utils.encoding import force_text from django.utils.encoding import force_text
from mayan.apps.common.tests import BaseTestCase from mayan.apps.common.tests import BaseTestCase
from mayan.apps.documents.tests import ( from mayan.apps.documents.tests import DocumentTestMixin
TEST_SMALL_DOCUMENT_PATH, DocumentTestMixin
)
from mayan.apps.documents.tests.literals import ( from mayan.apps.documents.tests.literals import (
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_DESCRIPTION_EDITED, TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_DESCRIPTION_EDITED,
TEST_DOCUMENT_LABEL_EDITED TEST_DOCUMENT_LABEL_EDITED
@@ -20,65 +18,54 @@ from .literals import (
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, TEST_METADATA_TYPE_LABEL, TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, TEST_METADATA_TYPE_LABEL,
TEST_METADATA_TYPE_NAME, TEST_METADATA_VALUE TEST_METADATA_TYPE_NAME, TEST_METADATA_VALUE
) )
from .mixins import DocumentIndexingTestMixin from .mixins import IndexTemplateTestMixin
class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase): class IndexTestCase(IndexTemplateTestMixin, DocumentTestMixin, BaseTestCase):
def test_document_description_index(self): def test_document_description_index(self):
self._create_index() self.test_document.description = TEST_DOCUMENT_DESCRIPTION
self.test_document.save()
self.index.node_templates.create( self._create_index_template_node(
parent=self.index.template_root,
expression=TEST_INDEX_TEMPLATE_DOCUMENT_DESCRIPTION_EXPRESSION, expression=TEST_INDEX_TEMPLATE_DOCUMENT_DESCRIPTION_EXPRESSION,
link_documents=True rebuild=True
) )
self.document.description = TEST_DOCUMENT_DESCRIPTION
self.document.save()
self.index.rebuild()
self.assertEqual( self.assertEqual(
IndexInstanceNode.objects.last().value, self.document.description IndexInstanceNode.objects.last().value, self.test_document.description
) )
self.document.description = TEST_DOCUMENT_DESCRIPTION_EDITED self.test_document.description = TEST_DOCUMENT_DESCRIPTION_EDITED
self.document.save() self.test_document.save()
self.assertEqual( self.assertEqual(
IndexInstanceNode.objects.last().value, self.document.description IndexInstanceNode.objects.last().value, self.test_document.description
) )
def test_document_label_index(self): def test_document_label_index(self):
self._create_index() self._create_index_template_node(
self.index.node_templates.create(
parent=self.index.template_root,
expression=TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION, expression=TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION,
link_documents=True rebuild=True
) )
self.index.rebuild()
self.assertEqual( self.assertEqual(
IndexInstanceNode.objects.last().value, self.document.label IndexInstanceNode.objects.last().value, self.test_document.label
) )
self.document.label = TEST_DOCUMENT_LABEL_EDITED self.test_document.label = TEST_DOCUMENT_LABEL_EDITED
self.document.save() self.test_document.save()
self.assertEqual( self.assertEqual(
IndexInstanceNode.objects.last().value, self.document.label IndexInstanceNode.objects.last().value, self.test_document.label
) )
def test_date_based_index(self): def test_date_based_index(self):
self._create_index() self._create_index_template(add_document_type=True)
level_year = self.index.node_templates.create( level_year = self.test_index_template.node_templates.create(
parent=self.index.template_root, parent=self.test_index_template.template_root,
expression='{{ document.date_added.year }}', expression='{{ document.date_added.year }}',
link_documents=False link_documents=False
) )
self.index.node_templates.create( self.test_index_template.node_templates.create(
parent=level_year, parent=level_year,
expression='{{ document.date_added.month }}', expression='{{ document.date_added.month }}',
link_documents=True link_documents=True
@@ -89,18 +76,18 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
self.document.delete() self.document.delete()
# Uploading a new should not trigger an error # Uploading a new should not trigger an error
document = self.upload_document() self._create_document()
self.assertEqual( self.assertEqual(
[instance.value for instance in IndexInstanceNode.objects.all().order_by('index_instance_node_pk')], [instance.value for instance in IndexInstanceNode.objects.all()],
[ [
'', force_text(document.date_added.year), '', force_text(self.test_document.date_added.year),
force_text(document.date_added.month).zfill(2) force_text(self.test_document.date_added.month)
] ]
) )
self.assertTrue( self.assertTrue(
document in list(IndexInstanceNode.objects.order_by('index_instance_node_pk').last().documents.all()) self.test_document in list(IndexInstanceNode.objects.last().documents.all())
) )
def test_dual_level_dual_document_index(self): def test_dual_level_dual_document_index(self):
@@ -109,33 +96,33 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
values and two second levels with the same value but as separate values and two second levels with the same value but as separate
children of each of the first levels. GitLab issue #391 children of each of the first levels. GitLab issue #391
""" """
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: self.document_2 = self.upload_document()
self.document_2 = self.document_type.new_document(
file_object=file_object
)
self._create_index() self._create_index_template(add_document_type=True)
# Create simple index template # Create simple index template
root = self.index.template_root root = self.test_index_template.template_root
level_1 = self.index.node_templates.create( level_1 = self.test_index_template.node_templates.create(
parent=root, expression='{{ document.uuid }}', parent=root, expression='{{ document.uuid }}',
link_documents=False link_documents=False
) )
self.index.node_templates.create( self.test_index_template.node_templates.create(
parent=level_1, expression='{{ document.label }}', parent=level_1, expression='{{ document.label }}',
link_documents=True link_documents=True
) )
Index.objects.rebuild() Index.objects.rebuild()
# Typecast to sets to make sorting irrelevant in the comparison.
self.assertEqual( self.assertEqual(
[instance.value for instance in IndexInstanceNode.objects.all().order_by('index_instance_node_pk')], set(IndexInstanceNode.objects.values_list('value', flat=True)),
[ set(
'', force_text(self.document_2.uuid), self.document_2.label, [
force_text(self.document.uuid), self.document.label '', force_text(self.document_2.uuid), self.document_2.label,
] force_text(self.document.uuid), self.document.label
]
)
) )
def test_metadata_indexing(self): def test_metadata_indexing(self):
@@ -146,11 +133,11 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
document_type=self.document_type, metadata_type=metadata_type document_type=self.document_type, metadata_type=metadata_type
) )
self._create_index() self._create_index_template(add_document_type=True)
# Create simple index template # Create simple index template
root = self.index.template_root root = self.test_index_template.template_root
self.index.node_templates.create( self.test_index_template.node_templates.create(
parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION,
link_documents=True link_documents=True
) )
@@ -231,15 +218,15 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
On a two level template if the first level doesn't return a result On a two level template if the first level doesn't return a result
the indexing should stop. GitLab issue #391. the indexing should stop. GitLab issue #391.
""" """
self._create_index() self._create_index_template(add_document_type=True)
level_1 = self.index.node_templates.create( level_1 = self.test_index_template.node_templates.create(
parent=self.index.template_root, parent=self.test_index_template.template_root,
expression='', expression='',
link_documents=True link_documents=True
) )
self.index.node_templates.create( self.test_index_template.node_templates.create(
parent=level_1, expression='{{ document.label }}', parent=level_1, expression='{{ document.label }}',
link_documents=True link_documents=True
) )
@@ -260,11 +247,11 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
metadata_type=metadata_type, value=TEST_METADATA_VALUE metadata_type=metadata_type, value=TEST_METADATA_VALUE
) )
self._create_index() self._create_index_template(add_document_type=True)
# Create simple index template # Create simple index template
root = self.index.template_root root = self.test_index_template.template_root
self.index.node_templates.create( self.test_index_template.node_templates.create(
parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION,
link_documents=True link_documents=True
) )

File diff suppressed because it is too large Load Diff

View File

@@ -2,101 +2,109 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
"""
from .api_views import ( from .api_views import (
APIDocumentIndexListView, APIIndexListView, APIDocumentIndexTemplateListView, APIIndexTemplateListView,
APIIndexNodeInstanceDocumentListView, APIIndexTemplateListView, APIIndexNodeInstanceDocumentListView, APIIndexTemplateListView,
APIIndexTemplateView, APIIndexView APIIndexTemplateView, APIIndexView
) )
"""
from .views import ( from .views import (
DocumentIndexNodeListView, IndexInstanceNodeView, IndexListView, DocumentIndexInstanceNodeListView, IndexInstanceNodeView, IndexInstancesRebuildView,
RebuildIndexesView, SetupIndexCreateView, SetupIndexDeleteView, IndexInstanceView, IndexTemplateCreateView, IndexTemplateDeleteView,
SetupIndexDocumentTypesView, SetupIndexEditView, SetupIndexListView, IndexTemplateDocumentTypesView, IndexTemplateEditView,
SetupIndexTreeTemplateListView, TemplateNodeCreateView, IndexTemplateListView, IndexTemplateNodeCreateView,
TemplateNodeDeleteView, TemplateNodeEditView IndexTemplateNodeDeleteView, IndexTemplateNodeEditView,
IndexTemplateNodeListView
) )
urlpatterns = [ urlpatterns = [
url( url(
regex=r'^indexes/$', name='index_setup_list', regex=r'^documents/(?P<document_id>\d+)/indexes/$',
view=SetupIndexListView.as_view() name='document_index_instance_list',
view=DocumentIndexInstanceNodeListView.as_view()
), ),
url( url(
regex=r'^indexes/create/$', name='index_setup_create', regex=r'^index_instances/$', name='index_instance_list',
view=SetupIndexCreateView.as_view() view=IndexInstanceView.as_view()
), ),
url( url(
regex=r'^indexes/(?P<index_pk>\d+)/delete/$', regex=r'^index_instances/nodes/(?P<index_instance_node_id>\d+)/$',
name='index_setup_delete', view=SetupIndexDeleteView.as_view()
),
url(
regex=r'^indexes/(?P<index_pk>\d+)/edit/$',
name='index_setup_edit', view=SetupIndexEditView.as_view()
),
url(
regex=r'^indexes/(?P<index_pk>\d+)/templates/$',
name='index_setup_view', view=SetupIndexTreeTemplateListView.as_view()
),
url(
regex=r'^indexes/(?P<index_pk>\d+)/document_types/$',
name='index_setup_document_types',
view=SetupIndexDocumentTypesView.as_view()
),
url(
regex=r'^indexes/templates/nodes/(?P<index_template_node_pk>\d+)/create/child/$',
name='template_node_create', view=TemplateNodeCreateView.as_view()
),
url(
regex=r'^indexes/templates/nodes/(?P<index_template_node_pk>\d+)/edit/$',
name='template_node_edit', view=TemplateNodeEditView.as_view()
),
url(
regex=r'^indexes/templates/nodes/(?P<index_template_node_pk>\d+)/delete/$',
name='template_node_delete', view=TemplateNodeDeleteView.as_view()
),
url(
regex=r'^indexes/instances/list/$', name='index_list',
view=IndexListView.as_view()
),
url(
regex=r'^indexes/instances/node/(?P<index_instance_node_pk>\d+)/$',
name='index_instance_node_view', view=IndexInstanceNodeView.as_view() name='index_instance_node_view', view=IndexInstanceNodeView.as_view()
), ),
url( url(
regex=r'^indexes/rebuild/$', name='rebuild_index_instances', regex=r'^index_instances/rebuild/$', name='index_instances_rebuild',
view=RebuildIndexesView.as_view() view=IndexInstancesRebuildView.as_view()
), ),
url( url(
regex=r'^documents/(?P<document_pk>\d+)/indexes/$', regex=r'^index_templates/$', name='index_template_list',
name='document_index_list', view=DocumentIndexNodeListView.as_view() view=IndexTemplateListView.as_view()
), ),
url(
regex=r'^index_templates/create/$', name='index_template_create',
view=IndexTemplateCreateView.as_view()
),
url(
regex=r'^index_templates/(?P<index_template_id>\d+)/delete/$',
name='index_template_delete', view=IndexTemplateDeleteView.as_view()
),
url(
regex=r'^index_templates/(?P<index_template_id>\d+)/document_types/$',
name='index_template_document_types',
view=IndexTemplateDocumentTypesView.as_view()
),
url(
regex=r'^index_templates/(?P<index_template_id>\d+)/edit/$',
name='index_template_edit', view=IndexTemplateEditView.as_view()
),
url(
regex=r'^index_templates/(?P<index_template_id>\d+)/nodes/$',
name='index_template_view', view=IndexTemplateNodeListView.as_view()
),
url(
regex=r'^index_templates/nodes/(?P<index_template_node_id>\d+)/create/$',
name='index_template_node_create',
view=IndexTemplateNodeCreateView.as_view()
),
url(
regex=r'^index_templates/nodes/(?P<index_template_node_id>\d+)/delete/$',
name='index_template_node_delete',
view=IndexTemplateNodeDeleteView.as_view()
),
url(
regex=r'^index_templates/nodes/(?P<index_template_node_id>\d+)/edit/$',
name='index_template_node_edit',
view=IndexTemplateNodeEditView.as_view()
)
] ]
"""
api_urls = [ api_urls = [
url( url(
regex=r'^indexes/nodes/(?P<index_instance_node_pk>[0-9]+)/documents/$', regex=r'^index_templates/nodes/(?P<index_instance_node_pk>\d+)/documents/$',
name='index-node-documents', name='index-node-documents',
view=APIIndexNodeInstanceDocumentListView.as_view(), view=APIIndexNodeInstanceDocumentListView.as_view(),
), ),
url( url(
regex=r'^indexes/templates/(?P<index_template_node_pk>[0-9]+)/$', regex=r'^index_templates/templates/(?P<index_template_node_pk>\d+)/$',
name='index-template-detail', view=APIIndexTemplateView.as_view() name='index-template-detail', view=APIIndexTemplateView.as_view()
), ),
url( url(
regex=r'^indexes/(?P<index_pk>\d+)/$', name='index-detail', regex=r'^index_templates/(?P<index_pk>\d+)/$', name='index-detail',
view=APIIndexView.as_view() view=APIIndexView.as_view()
), ),
url( url(
regex=r'^indexes/(?P<index_pk>\d+)/templates/$', regex=r'^index_templates/(?P<index_pk>\d+)/templates/$',
name='index-template-detail', view=APIIndexTemplateListView.as_view() name='index-template-detail', view=APIIndexTemplateListView.as_view()
), ),
url( url(
regex=r'^indexes/$', name='index-list', regex=r'^index_templates/$', name='index-list',
view=APIIndexListView.as_view() view=APIIndexTemplateListView.as_view()
), ),
url( url(
regex=r'^documents/(?P<document_pk>\d+)/indexes/$', regex=r'^documents/(?P<document_pk>\d+)/indexes/$',
name='document-index-list', name='document-index-list',
view=APIDocumentIndexListView.as_view() view=APIDocumentIndexTemplateListView.as_view()
), ),
] ]
"""

File diff suppressed because it is too large Load Diff