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:
@@ -233,6 +233,10 @@
|
||||
entire converter class is no longer cached and instead loaded
|
||||
on demand. This allows the garbage collector to clear the memory
|
||||
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)
|
||||
==================
|
||||
|
||||
@@ -27,23 +27,25 @@ from .handlers import (
|
||||
handler_index_document, handler_post_save_index_document,
|
||||
handler_remove_document
|
||||
)
|
||||
from .html_widgets import (
|
||||
IndexInstanceNodeWidget, IndexTemplateNodeIndentationWidget,
|
||||
get_instance_link
|
||||
)
|
||||
from .licenses import * # NOQA
|
||||
from .links import (
|
||||
link_document_index_list, link_index_main_menu, link_index_setup,
|
||||
link_index_setup_create, link_index_setup_delete,
|
||||
link_index_setup_document_types, link_index_setup_edit,
|
||||
link_index_setup_list, link_index_setup_view, link_rebuild_index_instances,
|
||||
link_template_node_create, link_template_node_delete,
|
||||
link_template_node_edit
|
||||
link_document_index_instance_list, link_index_instances_rebuild,
|
||||
link_index_main_menu, link_index_setup, link_index_template_create,
|
||||
link_index_template_delete, link_index_template_document_types,
|
||||
link_index_template_edit, link_index_template_list,
|
||||
link_index_template_view, link_index_template_node_create,
|
||||
link_index_template_node_delete, link_index_template_node_edit
|
||||
)
|
||||
from .permissions import (
|
||||
permission_document_indexing_create, permission_document_indexing_delete,
|
||||
permission_document_indexing_edit,
|
||||
permission_document_indexing_instance_view,
|
||||
permission_document_indexing_edit, permission_document_indexing_instance_view,
|
||||
permission_document_indexing_rebuild, permission_document_indexing_view
|
||||
)
|
||||
from .queues import * # NOQA
|
||||
from .widgets import get_instance_link, index_instance_item_link, node_level
|
||||
|
||||
|
||||
class DocumentIndexingApp(MayanAppConfig):
|
||||
@@ -86,53 +88,63 @@ class DocumentIndexingApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
SourceColumn(attribute='label', is_identifier=True, source=Index)
|
||||
SourceColumn(attribute='slug', source=Index)
|
||||
ModelPermission.register_inheritance(
|
||||
model=IndexTemplateNode, related='index'
|
||||
)
|
||||
|
||||
ModelPermission.register_inheritance(
|
||||
model=IndexInstanceNode, related='index_template_node__index'
|
||||
)
|
||||
|
||||
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(
|
||||
func=lambda context: context[
|
||||
'object'
|
||||
].instance_root.get_descendants_count(), label=_('Total levels'),
|
||||
source=IndexInstance,
|
||||
].instance_root.get_descendants_count(), include_label=True,
|
||||
label=_('Total levels'), source=IndexInstance,
|
||||
)
|
||||
SourceColumn(
|
||||
func=lambda context: context[
|
||||
'object'
|
||||
].instance_root.get_descendants_document_count(
|
||||
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(
|
||||
func=lambda context: node_level(context['object']),
|
||||
label=_('Level'), source=IndexTemplateNode
|
||||
)
|
||||
SourceColumn(
|
||||
attribute='enabled', source=IndexTemplateNode,
|
||||
widget=TwoStateWidget
|
||||
)
|
||||
SourceColumn(
|
||||
attribute='link_documents', source=IndexTemplateNode,
|
||||
widget=TwoStateWidget
|
||||
is_identifier=True, is_sortable=True, label=_('Level'),
|
||||
sort_field='value', source=IndexInstanceNode,
|
||||
widget=IndexInstanceNodeWidget
|
||||
)
|
||||
|
||||
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(
|
||||
func=lambda context: context[
|
||||
'object'
|
||||
].get_descendants_document_count(
|
||||
].get_item_count(
|
||||
user=context['request'].user
|
||||
), label=_('Documents'), source=IndexInstanceNode
|
||||
), include_label=True, label=_('Items'), source=IndexInstanceNode
|
||||
)
|
||||
|
||||
SourceColumn(
|
||||
@@ -174,35 +186,35 @@ class DocumentIndexingApp(MayanAppConfig):
|
||||
)
|
||||
|
||||
menu_facet.bind_links(
|
||||
links=(link_document_index_list,), sources=(Document,)
|
||||
links=(link_document_index_instance_list,), sources=(Document,)
|
||||
)
|
||||
menu_list_facet.bind_links(
|
||||
links=(
|
||||
link_acl_list, link_index_setup_document_types,
|
||||
link_index_setup_view,
|
||||
link_acl_list, link_index_template_document_types,
|
||||
link_index_template_view,
|
||||
), sources=(Index,)
|
||||
)
|
||||
menu_object.bind_links(
|
||||
links=(
|
||||
link_index_setup_edit, link_index_setup_delete
|
||||
link_index_template_edit, link_index_template_delete
|
||||
), sources=(Index,)
|
||||
)
|
||||
menu_object.bind_links(
|
||||
links=(
|
||||
link_template_node_create, link_template_node_edit,
|
||||
link_template_node_delete
|
||||
link_index_template_node_create, link_index_template_node_edit,
|
||||
link_index_template_node_delete
|
||||
), sources=(IndexTemplateNode,)
|
||||
)
|
||||
menu_main.bind_links(links=(link_index_main_menu,), position=98)
|
||||
menu_secondary.bind_links(
|
||||
links=(link_index_setup_list, link_index_setup_create),
|
||||
links=(link_index_template_list, link_index_template_create),
|
||||
sources=(
|
||||
Index, 'indexing:index_setup_list',
|
||||
'indexing:index_setup_create'
|
||||
Index, 'indexing:index_template_list',
|
||||
'indexing:index_template_create'
|
||||
)
|
||||
)
|
||||
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(
|
||||
dispatch_uid='document_indexing_handler_delete_empty',
|
||||
|
||||
@@ -4,30 +4,23 @@ from django import forms
|
||||
from django.utils.encoding import force_text
|
||||
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.forms import FilteredSelectionForm
|
||||
from mayan.apps.documents.models import Document
|
||||
|
||||
from .models import Index, IndexTemplateNode
|
||||
from .permissions import permission_document_indexing_rebuild
|
||||
|
||||
|
||||
class IndexListForm(forms.Form):
|
||||
indexes = forms.ModelMultipleChoiceField(
|
||||
help_text=_('Indexes to be queued for rebuilding.'),
|
||||
label=_('Indexes'), queryset=Index.objects.none(),
|
||||
required=False, widget=forms.widgets.CheckboxSelectMultiple()
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
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 IndexTemplateFilteredForm(FilteredSelectionForm):
|
||||
class Meta:
|
||||
allow_multiple = True
|
||||
field_name = 'index_templates'
|
||||
help_text = _('Index templates to be queued for rebuilding.')
|
||||
label = _('Index templates')
|
||||
queryset = Index.objects.filter(enabled=True)
|
||||
permission = permission_document_indexing_rebuild
|
||||
widget_attributes = {'class': 'select2'}
|
||||
|
||||
|
||||
class IndexTemplateNodeForm(forms.ModelForm):
|
||||
|
||||
@@ -1,10 +1,33 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.encoding import force_text
|
||||
from django.template.loader import render_to_string
|
||||
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):
|
||||
@@ -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 <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(
|
||||
[
|
||||
' ' * node.get_level(),
|
||||
'' if node.is_root_node() else icon_index_level_up.render(),
|
||||
force_text(node)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def node_tree(node, user):
|
||||
#TODO: Replace with a file template
|
||||
result = []
|
||||
@@ -71,7 +55,7 @@ def node_tree(node, user):
|
||||
else:
|
||||
element = ancestor
|
||||
if element.index_template_node.link_documents:
|
||||
icon = icon_node_with_documents
|
||||
icon = icon_index_instance_node_with_documents
|
||||
else:
|
||||
icon = icon_index_level_up
|
||||
|
||||
@@ -2,14 +2,37 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
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(
|
||||
driver_name='fontawesomecss', css_classes='fa-level-up-alt fa-rotate-90'
|
||||
)
|
||||
icon_index = Icon(driver_name='fontawesome', symbol='list-ul')
|
||||
icon_index_create = Icon(driver_name='fontawesome', symbol='plus')
|
||||
icon_index_setup_view = Icon(driver_name='fontawesome', symbol='folder-open')
|
||||
icon_node_with_documents = Icon(driver_name='fontawesome', symbol='folder')
|
||||
icon_rebuild_index_instances = Icon(
|
||||
icon_index_instance_node_with_documents = Icon(
|
||||
driver_name='fontawesome', symbol='folder'
|
||||
)
|
||||
icon_index_instances_rebuild = Icon(
|
||||
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')
|
||||
|
||||
@@ -6,8 +6,11 @@ from mayan.apps.documents.icons import icon_document_type
|
||||
from mayan.apps.navigation import Link, get_cascade_condition
|
||||
|
||||
from .icons import (
|
||||
icon_document_index_list, icon_index, icon_index_create,
|
||||
icon_index_setup_view, icon_rebuild_index_instances
|
||||
icon_document_index_instance_list, icon_index, icon_index_template_create,
|
||||
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 (
|
||||
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()
|
||||
|
||||
|
||||
link_document_index_list = Link(
|
||||
args='resolved_object.pk', icon_class=icon_document_index_list,
|
||||
text=_('Indexes'), view='indexing:document_index_list',
|
||||
link_document_index_instance_list = Link(
|
||||
icon_class=icon_document_index_instance_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(
|
||||
condition=get_cascade_condition(
|
||||
app_label='document_indexing', model_name='Index',
|
||||
object_permission=permission_document_indexing_instance_view,
|
||||
), icon_class=icon_index, text=_('Indexes'),
|
||||
view='indexing:index_list'
|
||||
view='indexing:index_instance_list'
|
||||
)
|
||||
link_index_setup = Link(
|
||||
condition=get_cascade_condition(
|
||||
@@ -38,55 +51,53 @@ link_index_setup = Link(
|
||||
object_permission=permission_document_indexing_view,
|
||||
view_permission=permission_document_indexing_create,
|
||||
), icon_class=icon_index, text=_('Indexes'),
|
||||
view='indexing:index_setup_list'
|
||||
view='indexing:index_template_list'
|
||||
)
|
||||
link_index_setup_list = Link(
|
||||
text=_('Indexes'), view='indexing:index_setup_list'
|
||||
link_index_template_list = Link(
|
||||
icon_class=icon_index_template_list,
|
||||
text=_('Indexes'), view='indexing:index_template_list'
|
||||
)
|
||||
link_index_setup_create = Link(
|
||||
icon_class=icon_index_create,
|
||||
link_index_template_create = Link(
|
||||
icon_class=icon_index_template_create,
|
||||
permission=permission_document_indexing_create, text=_('Create index'),
|
||||
view='indexing:index_setup_create'
|
||||
view='indexing:index_template_create'
|
||||
)
|
||||
link_index_setup_edit = Link(
|
||||
args='resolved_object.pk',
|
||||
permission=permission_document_indexing_edit, text=_('Edit'),
|
||||
view='indexing:index_setup_edit',
|
||||
)
|
||||
link_index_setup_delete = Link(
|
||||
args='resolved_object.pk',
|
||||
link_index_template_delete = Link(
|
||||
icon_class=icon_index_template_delete,
|
||||
kwargs={'index_template_id': 'resolved_object.pk'},
|
||||
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(
|
||||
args='resolved_object.pk', icon_class=icon_index_setup_view,
|
||||
link_index_template_edit = Link(
|
||||
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'),
|
||||
view='indexing:index_setup_view',
|
||||
view='indexing:index_template_view'
|
||||
)
|
||||
link_index_setup_document_types = Link(
|
||||
args='resolved_object.pk', icon_class=icon_document_type,
|
||||
link_index_template_document_types = Link(
|
||||
kwargs={'index_template_id': 'resolved_object.pk'}, icon_class=icon_document_type,
|
||||
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(
|
||||
condition=get_cascade_condition(
|
||||
app_label='document_indexing', model_name='Index',
|
||||
object_permission=permission_document_indexing_rebuild,
|
||||
), 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_index_template_node_create = Link(
|
||||
icon_class=icon_index_template_node_create,
|
||||
kwargs={'index_template_node_id': 'resolved_object.pk'},
|
||||
text=_('New node'), view='indexing:index_template_node_create'
|
||||
)
|
||||
link_template_node_create = Link(
|
||||
args='resolved_object.pk', text=_('New child node'),
|
||||
view='indexing:template_node_create',
|
||||
link_index_template_node_delete = Link(
|
||||
condition=condition_is_not_root_node,
|
||||
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(
|
||||
args='resolved_object.pk', condition=is_not_root_node, text=_('Edit'),
|
||||
view='indexing:template_node_edit',
|
||||
)
|
||||
link_template_node_delete = Link(
|
||||
args='resolved_object.pk', condition=is_not_root_node, tags='dangerous',
|
||||
text=_('Delete'), view='indexing:template_node_delete',
|
||||
link_index_template_node_edit = Link(
|
||||
condition=condition_is_not_root_node,
|
||||
icon_class=icon_index_template_node_edit,
|
||||
kwargs={'index_template_node_id': 'resolved_object.pk'}, text=_('Edit'),
|
||||
view='indexing:index_template_node_edit'
|
||||
)
|
||||
|
||||
@@ -66,7 +66,7 @@ class Index(models.Model):
|
||||
try:
|
||||
return reverse(
|
||||
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:
|
||||
return '#'
|
||||
@@ -183,7 +183,7 @@ class IndexTemplateNode(MPTTModel):
|
||||
)
|
||||
index = models.ForeignKey(
|
||||
on_delete=models.CASCADE, related_name='node_templates', to=Index,
|
||||
verbose_name=_('Index')
|
||||
verbose_name=_('Index template')
|
||||
)
|
||||
expression = models.TextField(
|
||||
help_text=_(
|
||||
@@ -356,7 +356,7 @@ class IndexInstanceNode(MPTTModel):
|
||||
def get_absolute_url(self):
|
||||
return reverse(
|
||||
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):
|
||||
|
||||
@@ -58,13 +58,13 @@ def task_index_document(self, document_id):
|
||||
|
||||
|
||||
@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(
|
||||
app_label='document_indexing', model_name='Index'
|
||||
)
|
||||
|
||||
try:
|
||||
index = Index.objects.get(pk=index_id)
|
||||
index = Index.objects.get(pk=index_template_id)
|
||||
index.rebuild()
|
||||
except LockError as exception:
|
||||
# This index is being rebuilt by another task, retry later
|
||||
|
||||
@@ -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>
|
||||
@@ -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 %}
|
||||
|
||||
{% endfor %}
|
||||
{% if not index_template_node.is_root_node %}
|
||||
{{ icon_index_level_up }}
|
||||
{% endif %}
|
||||
{{ index_template_node }}
|
||||
@@ -20,5 +20,4 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -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_DOCUMENT_LABEL_EXPRESSION = '{{ document.label }}'
|
||||
TEST_INDEX_TEMPLATE_DOCUMENT_DESCRIPTION_EXPRESSION = '{{ document.description }}'
|
||||
TEST_INDEX_TEMPLATE_DOCUMENT_UUID_EXPRESSION = '{{ document.uuid }}'
|
||||
TEST_INDEX_TEMPLATE_NODE_EXPRESSION_EDITED = 'expression edited'
|
||||
|
||||
@@ -2,13 +2,30 @@ from __future__ import unicode_literals
|
||||
|
||||
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):
|
||||
def _create_index(self):
|
||||
class IndexTemplateTestMixin(object):
|
||||
def _create_index_template(self, add_document_type=False):
|
||||
# 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
|
||||
self.index.document_types.add(self.document_type)
|
||||
if add_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()
|
||||
|
||||
@@ -3,9 +3,7 @@ from __future__ import unicode_literals
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from mayan.apps.common.tests import BaseTestCase
|
||||
from mayan.apps.documents.tests import (
|
||||
TEST_SMALL_DOCUMENT_PATH, DocumentTestMixin
|
||||
)
|
||||
from mayan.apps.documents.tests import DocumentTestMixin
|
||||
from mayan.apps.documents.tests.literals import (
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_DESCRIPTION_EDITED,
|
||||
TEST_DOCUMENT_LABEL_EDITED
|
||||
@@ -20,65 +18,54 @@ from .literals import (
|
||||
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, TEST_METADATA_TYPE_LABEL,
|
||||
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):
|
||||
self._create_index()
|
||||
|
||||
self.index.node_templates.create(
|
||||
parent=self.index.template_root,
|
||||
self.test_document.description = TEST_DOCUMENT_DESCRIPTION
|
||||
self.test_document.save()
|
||||
self._create_index_template_node(
|
||||
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(
|
||||
IndexInstanceNode.objects.last().value, self.document.description
|
||||
IndexInstanceNode.objects.last().value, self.test_document.description
|
||||
)
|
||||
self.document.description = TEST_DOCUMENT_DESCRIPTION_EDITED
|
||||
self.document.save()
|
||||
self.test_document.description = TEST_DOCUMENT_DESCRIPTION_EDITED
|
||||
self.test_document.save()
|
||||
|
||||
self.assertEqual(
|
||||
IndexInstanceNode.objects.last().value, self.document.description
|
||||
IndexInstanceNode.objects.last().value, self.test_document.description
|
||||
)
|
||||
|
||||
def test_document_label_index(self):
|
||||
self._create_index()
|
||||
|
||||
self.index.node_templates.create(
|
||||
parent=self.index.template_root,
|
||||
self._create_index_template_node(
|
||||
expression=TEST_INDEX_TEMPLATE_DOCUMENT_LABEL_EXPRESSION,
|
||||
link_documents=True
|
||||
rebuild=True
|
||||
)
|
||||
|
||||
self.index.rebuild()
|
||||
|
||||
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.document.save()
|
||||
self.test_document.label = TEST_DOCUMENT_LABEL_EDITED
|
||||
self.test_document.save()
|
||||
|
||||
self.assertEqual(
|
||||
IndexInstanceNode.objects.last().value, self.document.label
|
||||
IndexInstanceNode.objects.last().value, self.test_document.label
|
||||
)
|
||||
|
||||
def test_date_based_index(self):
|
||||
self._create_index()
|
||||
self._create_index_template(add_document_type=True)
|
||||
|
||||
level_year = self.index.node_templates.create(
|
||||
parent=self.index.template_root,
|
||||
level_year = self.test_index_template.node_templates.create(
|
||||
parent=self.test_index_template.template_root,
|
||||
expression='{{ document.date_added.year }}',
|
||||
link_documents=False
|
||||
)
|
||||
|
||||
self.index.node_templates.create(
|
||||
self.test_index_template.node_templates.create(
|
||||
parent=level_year,
|
||||
expression='{{ document.date_added.month }}',
|
||||
link_documents=True
|
||||
@@ -89,18 +76,18 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
|
||||
self.document.delete()
|
||||
|
||||
# Uploading a new should not trigger an error
|
||||
document = self.upload_document()
|
||||
self._create_document()
|
||||
|
||||
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(document.date_added.month).zfill(2)
|
||||
'', force_text(self.test_document.date_added.year),
|
||||
force_text(self.test_document.date_added.month)
|
||||
]
|
||||
)
|
||||
|
||||
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):
|
||||
@@ -109,33 +96,33 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
|
||||
values and two second levels with the same value but as separate
|
||||
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.document_type.new_document(
|
||||
file_object=file_object
|
||||
)
|
||||
self.document_2 = self.upload_document()
|
||||
|
||||
self._create_index()
|
||||
self._create_index_template(add_document_type=True)
|
||||
|
||||
# Create simple index template
|
||||
root = self.index.template_root
|
||||
level_1 = self.index.node_templates.create(
|
||||
root = self.test_index_template.template_root
|
||||
level_1 = self.test_index_template.node_templates.create(
|
||||
parent=root, expression='{{ document.uuid }}',
|
||||
link_documents=False
|
||||
)
|
||||
|
||||
self.index.node_templates.create(
|
||||
self.test_index_template.node_templates.create(
|
||||
parent=level_1, expression='{{ document.label }}',
|
||||
link_documents=True
|
||||
)
|
||||
|
||||
Index.objects.rebuild()
|
||||
|
||||
# Typecast to sets to make sorting irrelevant in the comparison.
|
||||
self.assertEqual(
|
||||
[instance.value for instance in IndexInstanceNode.objects.all().order_by('index_instance_node_pk')],
|
||||
[
|
||||
'', force_text(self.document_2.uuid), self.document_2.label,
|
||||
force_text(self.document.uuid), self.document.label
|
||||
]
|
||||
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
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def test_metadata_indexing(self):
|
||||
@@ -146,11 +133,11 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
|
||||
document_type=self.document_type, metadata_type=metadata_type
|
||||
)
|
||||
|
||||
self._create_index()
|
||||
self._create_index_template(add_document_type=True)
|
||||
|
||||
# Create simple index template
|
||||
root = self.index.template_root
|
||||
self.index.node_templates.create(
|
||||
root = self.test_index_template.template_root
|
||||
self.test_index_template.node_templates.create(
|
||||
parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION,
|
||||
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
|
||||
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(
|
||||
parent=self.index.template_root,
|
||||
level_1 = self.test_index_template.node_templates.create(
|
||||
parent=self.test_index_template.template_root,
|
||||
expression='',
|
||||
link_documents=True
|
||||
)
|
||||
|
||||
self.index.node_templates.create(
|
||||
self.test_index_template.node_templates.create(
|
||||
parent=level_1, expression='{{ document.label }}',
|
||||
link_documents=True
|
||||
)
|
||||
@@ -260,11 +247,11 @@ class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase):
|
||||
metadata_type=metadata_type, value=TEST_METADATA_VALUE
|
||||
)
|
||||
|
||||
self._create_index()
|
||||
self._create_index_template(add_document_type=True)
|
||||
|
||||
# Create simple index template
|
||||
root = self.index.template_root
|
||||
self.index.node_templates.create(
|
||||
root = self.test_index_template.template_root
|
||||
self.test_index_template.node_templates.create(
|
||||
parent=root, expression=TEST_INDEX_TEMPLATE_METADATA_EXPRESSION,
|
||||
link_documents=True
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,101 +2,109 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
"""
|
||||
from .api_views import (
|
||||
APIDocumentIndexListView, APIIndexListView,
|
||||
APIDocumentIndexTemplateListView, APIIndexTemplateListView,
|
||||
APIIndexNodeInstanceDocumentListView, APIIndexTemplateListView,
|
||||
APIIndexTemplateView, APIIndexView
|
||||
)
|
||||
"""
|
||||
from .views import (
|
||||
DocumentIndexNodeListView, IndexInstanceNodeView, IndexListView,
|
||||
RebuildIndexesView, SetupIndexCreateView, SetupIndexDeleteView,
|
||||
SetupIndexDocumentTypesView, SetupIndexEditView, SetupIndexListView,
|
||||
SetupIndexTreeTemplateListView, TemplateNodeCreateView,
|
||||
TemplateNodeDeleteView, TemplateNodeEditView
|
||||
DocumentIndexInstanceNodeListView, IndexInstanceNodeView, IndexInstancesRebuildView,
|
||||
IndexInstanceView, IndexTemplateCreateView, IndexTemplateDeleteView,
|
||||
IndexTemplateDocumentTypesView, IndexTemplateEditView,
|
||||
IndexTemplateListView, IndexTemplateNodeCreateView,
|
||||
IndexTemplateNodeDeleteView, IndexTemplateNodeEditView,
|
||||
IndexTemplateNodeListView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
regex=r'^indexes/$', name='index_setup_list',
|
||||
view=SetupIndexListView.as_view()
|
||||
regex=r'^documents/(?P<document_id>\d+)/indexes/$',
|
||||
name='document_index_instance_list',
|
||||
view=DocumentIndexInstanceNodeListView.as_view()
|
||||
),
|
||||
url(
|
||||
regex=r'^indexes/create/$', name='index_setup_create',
|
||||
view=SetupIndexCreateView.as_view()
|
||||
regex=r'^index_instances/$', name='index_instance_list',
|
||||
view=IndexInstanceView.as_view()
|
||||
),
|
||||
url(
|
||||
regex=r'^indexes/(?P<index_pk>\d+)/delete/$',
|
||||
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+)/$',
|
||||
regex=r'^index_instances/nodes/(?P<index_instance_node_id>\d+)/$',
|
||||
name='index_instance_node_view', view=IndexInstanceNodeView.as_view()
|
||||
),
|
||||
|
||||
url(
|
||||
regex=r'^indexes/rebuild/$', name='rebuild_index_instances',
|
||||
view=RebuildIndexesView.as_view()
|
||||
regex=r'^index_instances/rebuild/$', name='index_instances_rebuild',
|
||||
view=IndexInstancesRebuildView.as_view()
|
||||
),
|
||||
url(
|
||||
regex=r'^documents/(?P<document_pk>\d+)/indexes/$',
|
||||
name='document_index_list', view=DocumentIndexNodeListView.as_view()
|
||||
regex=r'^index_templates/$', name='index_template_list',
|
||||
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 = [
|
||||
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',
|
||||
view=APIIndexNodeInstanceDocumentListView.as_view(),
|
||||
),
|
||||
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()
|
||||
),
|
||||
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()
|
||||
),
|
||||
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()
|
||||
),
|
||||
url(
|
||||
regex=r'^indexes/$', name='index-list',
|
||||
view=APIIndexListView.as_view()
|
||||
regex=r'^index_templates/$', name='index-list',
|
||||
view=APIIndexTemplateListView.as_view()
|
||||
),
|
||||
url(
|
||||
regex=r'^documents/(?P<document_pk>\d+)/indexes/$',
|
||||
name='document-index-list',
|
||||
view=APIDocumentIndexListView.as_view()
|
||||
view=APIDocumentIndexTemplateListView.as_view()
|
||||
),
|
||||
]
|
||||
"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user