Add document_indexing API endpoints
This commit is contained in:
@@ -9,6 +9,7 @@ from main.api import register_maintenance_links
|
|||||||
from metadata.models import DocumentMetadata
|
from metadata.models import DocumentMetadata
|
||||||
from navigation.api import register_links, register_top_menu
|
from navigation.api import register_links, register_top_menu
|
||||||
from project_setup.api import register_setup
|
from project_setup.api import register_setup
|
||||||
|
from rest_api.classes import APIEndPoint
|
||||||
|
|
||||||
from .api import update_indexes, delete_indexes
|
from .api import update_indexes, delete_indexes
|
||||||
from .links import (document_index_list, document_index_main_menu_link,
|
from .links import (document_index_list, document_index_main_menu_link,
|
||||||
@@ -18,6 +19,7 @@ from .links import (document_index_list, document_index_main_menu_link,
|
|||||||
rebuild_index_instances, template_node_create,
|
rebuild_index_instances, template_node_create,
|
||||||
template_node_delete, template_node_edit)
|
template_node_delete, template_node_edit)
|
||||||
from .models import Index, IndexTemplateNode, IndexInstanceNode
|
from .models import Index, IndexTemplateNode, IndexInstanceNode
|
||||||
|
from .urls import api_urls
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, dispatch_uid='document_index_delete', sender=Document)
|
@receiver(pre_delete, dispatch_uid='document_index_delete', sender=Document)
|
||||||
@@ -52,3 +54,7 @@ register_links(IndexTemplateNode, [template_node_create, template_node_edit, tem
|
|||||||
register_setup(index_setup)
|
register_setup(index_setup)
|
||||||
|
|
||||||
register_top_menu('indexes', document_index_main_menu_link)
|
register_top_menu('indexes', document_index_main_menu_link)
|
||||||
|
|
||||||
|
endpoint = APIEndPoint('indexes')
|
||||||
|
endpoint.register_urls(api_urls)
|
||||||
|
endpoint.add_endpoint('indexes-list', _(u'Returns a list of all the indexes.'))
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
@@ -15,6 +17,7 @@ from .models import Index, IndexInstanceNode, DocumentRenameCount
|
|||||||
from .settings import (AVAILABLE_INDEXING_FUNCTIONS, MAX_SUFFIX_COUNT,
|
from .settings import (AVAILABLE_INDEXING_FUNCTIONS, MAX_SUFFIX_COUNT,
|
||||||
SLUGIFY_PATHS)
|
SLUGIFY_PATHS)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
if SLUGIFY_PATHS:
|
if SLUGIFY_PATHS:
|
||||||
SLUGIFY_FUNCTION = slugify
|
SLUGIFY_FUNCTION = slugify
|
||||||
else:
|
else:
|
||||||
@@ -52,7 +55,7 @@ def delete_indexes(document):
|
|||||||
|
|
||||||
warnings = []
|
warnings = []
|
||||||
|
|
||||||
for index_instance in document.indexinstancenode_set.all():
|
for index_instance in document.index_instance_nodes.all():
|
||||||
index_warnings = cascade_document_remove(document, index_instance)
|
index_warnings = cascade_document_remove(document, index_instance)
|
||||||
warnings.extend(index_warnings)
|
warnings.extend(index_warnings)
|
||||||
|
|
||||||
@@ -85,8 +88,10 @@ def cascade_eval(eval_dict, document, template_node, parent_index_instance=None)
|
|||||||
try:
|
try:
|
||||||
result = eval(template_node.expression, eval_dict, AVAILABLE_INDEXING_FUNCTIONS)
|
result = eval(template_node.expression, eval_dict, AVAILABLE_INDEXING_FUNCTIONS)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
warnings.append(_(u'Error in document indexing update expression: %(expression)s; %(exception)s') % {
|
error_message = _(u'Error in document indexing update expression: %(expression)s; %(exception)s') % {
|
||||||
'expression': template_node.expression, 'exception': exception})
|
'expression': template_node.expression, 'exception': exception}
|
||||||
|
warnings.append(error_message)
|
||||||
|
logger.debug(error_message)
|
||||||
else:
|
else:
|
||||||
if result:
|
if result:
|
||||||
index_instance, created = IndexInstanceNode.objects.get_or_create(index_template_node=template_node, value=result, parent=parent_index_instance)
|
index_instance, created = IndexInstanceNode.objects.get_or_create(index_template_node=template_node, value=result, parent=parent_index_instance)
|
||||||
@@ -109,8 +114,10 @@ def cascade_eval(eval_dict, document, template_node, parent_index_instance=None)
|
|||||||
try:
|
try:
|
||||||
fs_create_document_link(index_instance, document, suffix)
|
fs_create_document_link(index_instance, document, suffix)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
warnings.append(_(u'Error updating document index, expression: %(expression)s; %(exception)s') % {
|
error_message = _(u'Error updating document index, expression: %(expression)s; %(exception)s') % {
|
||||||
'expression': template_node.expression, 'exception': exception})
|
'expression': template_node.expression, 'exception': exception}
|
||||||
|
warnings.append(error_message)
|
||||||
|
logger.debug(error_message)
|
||||||
|
|
||||||
index_instance.documents.add(document)
|
index_instance.documents.add(document)
|
||||||
|
|
||||||
|
|||||||
64
mayan/apps/document_indexing/api_views.py
Normal file
64
mayan/apps/document_indexing/api_views.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from rest_framework import generics
|
||||||
|
|
||||||
|
from acls.models import AccessEntry
|
||||||
|
from documents.models import Document
|
||||||
|
from documents.permissions import PERMISSION_DOCUMENT_VIEW
|
||||||
|
from permissions.models import Permission
|
||||||
|
from rest_api.filters import MayanObjectPermissionsFilter
|
||||||
|
from rest_api.permissions import MayanPermission
|
||||||
|
|
||||||
|
from .models import Index, IndexInstanceNode
|
||||||
|
from .permissions import (PERMISSION_DOCUMENT_INDEXING_CREATE,
|
||||||
|
PERMISSION_DOCUMENT_INDEXING_VIEW)
|
||||||
|
from .serializers import IndexSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class APIIndexView(generics.RetrieveAPIView):
|
||||||
|
"""
|
||||||
|
Details of the selected index.
|
||||||
|
"""
|
||||||
|
serializer_class = IndexSerializer
|
||||||
|
queryset = Index.objects.all()
|
||||||
|
|
||||||
|
permission_classes = (MayanPermission,)
|
||||||
|
mayan_object_permissions = {'GET': [PERMISSION_DOCUMENT_INDEXING_VIEW]}
|
||||||
|
|
||||||
|
|
||||||
|
class APIIndexListView(generics.ListCreateAPIView):
|
||||||
|
"""
|
||||||
|
Returns a list of all the defined indexes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer_class = IndexSerializer
|
||||||
|
queryset = Index.objects.all()
|
||||||
|
|
||||||
|
filter_backends = (MayanObjectPermissionsFilter,)
|
||||||
|
mayan_object_permissions = {'GET': [PERMISSION_DOCUMENT_INDEXING_VIEW]}
|
||||||
|
mayan_view_permissions = {'POST': [PERMISSION_DOCUMENT_INDEXING_CREATE]}
|
||||||
|
|
||||||
|
|
||||||
|
class APIIndexNodeInstanceDocumentListView(generics.ListAPIView):
|
||||||
|
"""
|
||||||
|
Returns a list of all the documents contained by a particular index node instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
filter_backends = (MayanObjectPermissionsFilter,)
|
||||||
|
mayan_object_permissions = {'GET': [PERMISSION_DOCUMENT_VIEW]}
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
from documents.serializers import DocumentSerializer
|
||||||
|
return DocumentSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
index_node_instance = get_object_or_404(IndexInstanceNode, pk=self.kwargs['pk'])
|
||||||
|
try:
|
||||||
|
Permission.objects.check_permissions(self.request.user, [PERMISSION_DOCUMENT_INDEXING_VIEW])
|
||||||
|
except PermissionDenied:
|
||||||
|
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_INDEXING_VIEW, self.request.user, index_node_instance.index)
|
||||||
|
|
||||||
|
return index_node_instance.documents.all()
|
||||||
@@ -24,11 +24,11 @@ class Index(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def template_root(self):
|
def template_root(self):
|
||||||
return self.indextemplatenode_set.get(parent=None)
|
return self.template_nodes.get(parent=None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def instance_root(self):
|
def instance_root(self):
|
||||||
return self.template_root.node_instance
|
return self.template_root.node_instance.get()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.title
|
return self.title
|
||||||
@@ -41,6 +41,7 @@ class Index(models.Model):
|
|||||||
return DocumentType.objects.exclude(pk__in=self.document_types.all())
|
return DocumentType.objects.exclude(pk__in=self.document_types.all())
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
"""Automatically create the root index template node"""
|
||||||
super(Index, self).save(*args, **kwargs)
|
super(Index, self).save(*args, **kwargs)
|
||||||
index_template_node_root, created = IndexTemplateNode.objects.get_or_create(parent=None, index=self)
|
index_template_node_root, created = IndexTemplateNode.objects.get_or_create(parent=None, index=self)
|
||||||
|
|
||||||
@@ -56,14 +57,18 @@ class Index(models.Model):
|
|||||||
except IndexInstanceNode.DoesNotExist:
|
except IndexInstanceNode.DoesNotExist:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node_instances(self):
|
||||||
|
return [template_node.node_instance.get() for template_node in self.template_nodes.all()]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _(u'Index')
|
verbose_name = _(u'Index')
|
||||||
verbose_name_plural = _(u'Indexes')
|
verbose_name_plural = _(u'Indexes')
|
||||||
|
|
||||||
|
|
||||||
class IndexTemplateNode(MPTTModel):
|
class IndexTemplateNode(MPTTModel):
|
||||||
parent = TreeForeignKey('self', null=True, blank=True, related_name='index_template_node')
|
parent = TreeForeignKey('self', null=True, blank=True)
|
||||||
index = models.ForeignKey(Index, verbose_name=_(u'Index'))
|
index = models.ForeignKey(Index, verbose_name=_(u'Index'), related_name='template_nodes')
|
||||||
expression = models.CharField(max_length=128, verbose_name=_(u'Indexing expression'), help_text=_(u'Enter a python string expression to be evaluated.'))
|
expression = models.CharField(max_length=128, verbose_name=_(u'Indexing expression'), help_text=_(u'Enter a python string expression to be evaluated.'))
|
||||||
# % available_indexing_functions_string)
|
# % available_indexing_functions_string)
|
||||||
enabled = models.BooleanField(default=True, verbose_name=_(u'Enabled'), help_text=_(u'Causes this node to be visible and updated when document data changes.'))
|
enabled = models.BooleanField(default=True, verbose_name=_(u'Enabled'), help_text=_(u'Causes this node to be visible and updated when document data changes.'))
|
||||||
@@ -72,24 +77,23 @@ class IndexTemplateNode(MPTTModel):
|
|||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.expression
|
return self.expression
|
||||||
|
|
||||||
@property
|
|
||||||
def node_instance(self):
|
|
||||||
return self.indexinstancenode_set.get()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _(u'Index template node')
|
verbose_name = _(u'Index template node')
|
||||||
verbose_name_plural = _(u'Indexes template nodes')
|
verbose_name_plural = _(u'Indexes template nodes')
|
||||||
|
|
||||||
|
|
||||||
class IndexInstanceNode(MPTTModel):
|
class IndexInstanceNode(MPTTModel):
|
||||||
parent = TreeForeignKey('self', null=True, blank=True, related_name='index_instance_node')
|
parent = TreeForeignKey('self', null=True, blank=True)
|
||||||
index_template_node = models.ForeignKey(IndexTemplateNode, verbose_name=_(u'Index template node'))
|
index_template_node = models.ForeignKey(IndexTemplateNode, related_name='node_instance', verbose_name=_(u'Index template node'))
|
||||||
value = models.CharField(max_length=128, blank=True, verbose_name=_(u'Value'))
|
value = models.CharField(max_length=128, blank=True, verbose_name=_(u'Value'))
|
||||||
documents = models.ManyToManyField(Document, verbose_name=_(u'Documents'))
|
documents = models.ManyToManyField(Document, related_name='index_instance_nodes', verbose_name=_(u'Documents'))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
return self.index_template_node.index
|
||||||
|
|
||||||
@models.permalink
|
@models.permalink
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return ('indexing:index_instance_node_view', [self.pk])
|
return ('indexing:index_instance_node_view', [self.pk])
|
||||||
|
|||||||
27
mayan/apps/document_indexing/serializers.py
Normal file
27
mayan/apps/document_indexing/serializers.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Index, IndexInstanceNode, IndexTemplateNode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IndexInstanceNodeSerializer(serializers.ModelSerializer):
|
||||||
|
documents = serializers.HyperlinkedIdentityField(view_name='index-node-documents')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = ('id', 'parent', 'index_template_node', 'value', 'level', 'documents')
|
||||||
|
model = IndexInstanceNode
|
||||||
|
|
||||||
|
|
||||||
|
class IndexTemplateNodeSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = IndexTemplateNode
|
||||||
|
|
||||||
|
|
||||||
|
class IndexSerializer(serializers.ModelSerializer):
|
||||||
|
template_nodes = IndexTemplateNodeSerializer(read_only=True, many=True)
|
||||||
|
node_instances = IndexInstanceNodeSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Index
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from .api_views import (APIIndexView, APIIndexListView,
|
||||||
|
APIIndexNodeInstanceDocumentListView)
|
||||||
|
|
||||||
urlpatterns = patterns('document_indexing.views',
|
urlpatterns = patterns('document_indexing.views',
|
||||||
url(r'^setup/index/list/$', 'index_setup_list', (), 'index_setup_list'),
|
url(r'^setup/index/list/$', 'index_setup_list', (), 'index_setup_list'),
|
||||||
url(r'^setup/index/create/$', 'index_setup_create', (), 'index_setup_create'),
|
url(r'^setup/index/create/$', 'index_setup_create', (), 'index_setup_create'),
|
||||||
@@ -18,3 +21,10 @@ urlpatterns = patterns('document_indexing.views',
|
|||||||
url(r'^rebuild/all/$', 'rebuild_index_instances', (), 'rebuild_index_instances'),
|
url(r'^rebuild/all/$', 'rebuild_index_instances', (), 'rebuild_index_instances'),
|
||||||
url(r'^list/for/document/(?P<document_id>\d+)/$', 'document_index_list', (), 'document_index_list'),
|
url(r'^list/for/document/(?P<document_id>\d+)/$', 'document_index_list', (), 'document_index_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
api_urls = patterns('',
|
||||||
|
url(r'^index/node/(?P<pk>[0-9]+)/documents/$', APIIndexNodeInstanceDocumentListView.as_view(), name='index-node-documents'),
|
||||||
|
url(r'^indexes/(?P<pk>[0-9]+)/$', APIIndexView.as_view(), name='index-detail'),
|
||||||
|
url(r'^indexes/$', APIIndexListView.as_view(), name='index-list'),
|
||||||
|
#url(r'^document/(?P<pk>[0-9]+)/folders/$', APIDocumentFolderListView.as_view(), name='document-folder-list'),
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user