Refactor the model accesors
Refactor the accesors to behave like methods instead of properties. This means all accesors will be prepended with the string "get_" and will include a set of parenthesis. Improve the ModeAttribute class to use the method's short_description. This commit also adds support for a new method .help_text attribute has been added. Move accessors to their own module, named "methods.py". Remove the PropertyHelper class as the accessors no longer need it. Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
12
HISTORY.rst
12
HISTORY.rst
@@ -1,4 +1,4 @@
|
|||||||
3.2 (2018-XX-XX)
|
4.0 (2019-XX-XX)
|
||||||
================
|
================
|
||||||
- Documents: Add a server side template for invalid documents.
|
- Documents: Add a server side template for invalid documents.
|
||||||
The new template can be accessed via the templates API.
|
The new template can be accessed via the templates API.
|
||||||
@@ -177,6 +177,16 @@
|
|||||||
BROKER_URL to CELERY_BROKER_URL.
|
BROKER_URL to CELERY_BROKER_URL.
|
||||||
- Internal change. Add support to the SourceColumn class to resolve
|
- Internal change. Add support to the SourceColumn class to resolve
|
||||||
related fields using the double underscore as separator.
|
related fields using the double underscore as separator.
|
||||||
|
- Refactored the model accesors to behave like methods instead of
|
||||||
|
properties. This means all accessors will be prepended with the
|
||||||
|
string "get_" and will include a set of parenthesis.
|
||||||
|
- The ModeAttribute class has been improved to use the method's
|
||||||
|
short_description. Support for a new method .help_text attribute
|
||||||
|
has been added.
|
||||||
|
- Add accessors have been moved to their own module, named
|
||||||
|
"methods.py".
|
||||||
|
- The PropertyHelper class has been removed as the accessors
|
||||||
|
no longer need it.
|
||||||
|
|
||||||
3.1.9 (2018-11-01)
|
3.1.9 (2018-11-01)
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -8,23 +8,9 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class ModelPermission(object):
|
class ModelPermission(object):
|
||||||
_registry = {}
|
|
||||||
_proxies = {}
|
|
||||||
_inheritances = {}
|
_inheritances = {}
|
||||||
|
_proxies = {}
|
||||||
@classmethod
|
_registry = {}
|
||||||
def register(cls, model, permissions):
|
|
||||||
from django.contrib.contenttypes.fields import GenericRelation
|
|
||||||
|
|
||||||
cls._registry.setdefault(model, [])
|
|
||||||
for permission in permissions:
|
|
||||||
cls._registry[model].append(permission)
|
|
||||||
|
|
||||||
AccessControlList = apps.get_model(
|
|
||||||
app_label='acls', model_name='AccessControlList'
|
|
||||||
)
|
|
||||||
|
|
||||||
model.add_to_class(name='acls', value=GenericRelation(AccessControlList))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_classes(cls, as_content_type=False):
|
def get_classes(cls, as_content_type=False):
|
||||||
@@ -72,13 +58,29 @@ class ModelPermission(object):
|
|||||||
return StoredPermission.objects.filter(pk__in=pks)
|
return StoredPermission.objects.filter(pk__in=pks)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_proxy(cls, source, model):
|
def get_inheritance(cls, model):
|
||||||
cls._proxies[model] = source
|
return cls._inheritances[model]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls, model, permissions):
|
||||||
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
|
|
||||||
|
cls._registry.setdefault(model, [])
|
||||||
|
for permission in permissions:
|
||||||
|
cls._registry[model].append(permission)
|
||||||
|
|
||||||
|
AccessControlList = apps.get_model(
|
||||||
|
app_label='acls', model_name='AccessControlList'
|
||||||
|
)
|
||||||
|
|
||||||
|
model.add_to_class(
|
||||||
|
name='acls', value=GenericRelation(to=AccessControlList)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_inheritance(cls, model, related):
|
def register_inheritance(cls, model, related):
|
||||||
cls._inheritances[model] = related
|
cls._inheritances[model] = related
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_inheritance(cls, model):
|
def register_proxy(cls, source, model):
|
||||||
return cls._inheritances[model]
|
cls._proxies[model] = source
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from .links import (
|
|||||||
link_document_cabinet_remove, link_multiple_document_cabinet_remove
|
link_document_cabinet_remove, link_multiple_document_cabinet_remove
|
||||||
)
|
)
|
||||||
from .menus import menu_cabinets
|
from .menus import menu_cabinets
|
||||||
|
from .methods import method_get_document_cabinets
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_cabinet_add_document, permission_cabinet_delete,
|
permission_cabinet_add_document, permission_cabinet_delete,
|
||||||
permission_cabinet_edit, permission_cabinet_remove_document,
|
permission_cabinet_edit, permission_cabinet_remove_document,
|
||||||
@@ -53,8 +54,7 @@ class CabinetsApp(MayanAppConfig):
|
|||||||
# Add explicit order_by as DocumentCabinet ordering Meta option has no
|
# Add explicit order_by as DocumentCabinet ordering Meta option has no
|
||||||
# effect.
|
# effect.
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='document_cabinets',
|
name='get_document_cabinets', value=method_get_document_cabinets
|
||||||
value=lambda document: DocumentCabinet.objects.filter(documents=document).order_by('parent__label', 'label')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
|
|||||||
13
mayan/apps/cabinets/methods.py
Normal file
13
mayan/apps/cabinets/methods.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_document_cabinets(self):
|
||||||
|
DocumentCabinet = apps.get_models(
|
||||||
|
app_label='cabinets', model_name='DocumentCabinet'
|
||||||
|
)
|
||||||
|
|
||||||
|
return DocumentCabinet.objects.filter(documents=self).order_by(
|
||||||
|
'parent__label', 'label'
|
||||||
|
)
|
||||||
@@ -25,14 +25,6 @@ class WizardStepCabinets(WizardStep):
|
|||||||
)
|
)
|
||||||
return Cabinet.objects.exists()
|
return Cabinet.objects.exists()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_form_kwargs(self, wizard):
|
|
||||||
return {
|
|
||||||
'help_text': _('Cabinets to which the document will be added.'),
|
|
||||||
'permission': permission_cabinet_add_document,
|
|
||||||
'user': wizard.request.user
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def done(cls, wizard):
|
def done(cls, wizard):
|
||||||
result = {}
|
result = {}
|
||||||
@@ -44,6 +36,14 @@ class WizardStepCabinets(WizardStep):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_form_kwargs(self, wizard):
|
||||||
|
return {
|
||||||
|
'help_text': _('Cabinets to which the document will be added.'),
|
||||||
|
'permission': permission_cabinet_add_document,
|
||||||
|
'user': wizard.request.user
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def step_post_upload_process(cls, document, querystring=None):
|
def step_post_upload_process(cls, document, querystring=None):
|
||||||
furl_instance = furl(querystring)
|
furl_instance = furl(querystring)
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ from .links import (
|
|||||||
link_checkout_list
|
link_checkout_list
|
||||||
)
|
)
|
||||||
from .literals import CHECK_EXPIRED_CHECK_OUTS_INTERVAL
|
from .literals import CHECK_EXPIRED_CHECK_OUTS_INTERVAL
|
||||||
|
from .methods import (
|
||||||
|
method_check_in, method_get_checkout_info, method_get_checkout_state,
|
||||||
|
method_is_checked_out
|
||||||
|
)
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_document_checkin, permission_document_checkin_override,
|
permission_document_checkin, permission_document_checkin_override,
|
||||||
permission_document_checkout, permission_document_checkout_detail_view
|
permission_document_checkout, permission_document_checkout_detail_view
|
||||||
@@ -54,29 +58,15 @@ class CheckoutsApp(MayanAppConfig):
|
|||||||
app_label='documents', model_name='DocumentVersion'
|
app_label='documents', model_name='DocumentVersion'
|
||||||
)
|
)
|
||||||
|
|
||||||
DocumentCheckout = self.get_model('DocumentCheckout')
|
Document.add_to_class(name='check_in', value=method_check_in)
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='check_in',
|
name='get_checkout_info', value=method_get_checkout_info
|
||||||
value=lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user)
|
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='checkout_info',
|
name='get_checkout_state', value=method_get_checkout_state
|
||||||
value=lambda document: DocumentCheckout.objects.document_checkout_info(
|
|
||||||
document
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='checkout_state',
|
name='is_checked_out', value=method_is_checked_out
|
||||||
value=lambda document: DocumentCheckout.objects.document_checkout_state(
|
|
||||||
document
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Document.add_to_class(
|
|
||||||
name='is_checked_out',
|
|
||||||
value=lambda document: DocumentCheckout.objects.is_document_checked_out(
|
|
||||||
document
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelEventType.register(
|
ModelEventType.register(
|
||||||
|
|||||||
@@ -62,27 +62,6 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def document_checkout_info(self, document):
|
|
||||||
try:
|
|
||||||
return self.model.objects.get(document=document)
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
raise DocumentNotCheckedOut
|
|
||||||
|
|
||||||
def document_checkout_state(self, document):
|
|
||||||
if self.is_document_checked_out(document):
|
|
||||||
return STATE_CHECKED_OUT
|
|
||||||
else:
|
|
||||||
return STATE_CHECKED_IN
|
|
||||||
|
|
||||||
def expired_check_outs(self):
|
|
||||||
expired_list = Document.objects.filter(
|
|
||||||
pk__in=self.model.objects.filter(
|
|
||||||
expiration_datetime__lte=now()
|
|
||||||
).values_list('document__pk', flat=True)
|
|
||||||
)
|
|
||||||
logger.debug('expired_list: %s', expired_list)
|
|
||||||
return expired_list
|
|
||||||
|
|
||||||
def get_by_natural_key(self, document_natural_key):
|
def get_by_natural_key(self, document_natural_key):
|
||||||
Document = apps.get_model(
|
Document = apps.get_model(
|
||||||
app_label='documents', model_name='Document'
|
app_label='documents', model_name='Document'
|
||||||
@@ -94,6 +73,27 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
|
|
||||||
return self.get(document__pk=document.pk)
|
return self.get(document__pk=document.pk)
|
||||||
|
|
||||||
|
def get_document_checkout_info(self, document):
|
||||||
|
try:
|
||||||
|
return self.model.objects.get(document=document)
|
||||||
|
except self.model.DoesNotExist:
|
||||||
|
raise DocumentNotCheckedOut
|
||||||
|
|
||||||
|
def get_document_checkout_state(self, document):
|
||||||
|
if self.is_document_checked_out(document):
|
||||||
|
return STATE_CHECKED_OUT
|
||||||
|
else:
|
||||||
|
return STATE_CHECKED_IN
|
||||||
|
|
||||||
|
def get_expired_check_outs(self):
|
||||||
|
expired_list = Document.objects.filter(
|
||||||
|
pk__in=self.model.objects.filter(
|
||||||
|
expiration_datetime__lte=now()
|
||||||
|
).values_list('document__pk', flat=True)
|
||||||
|
)
|
||||||
|
logger.debug('expired_list: %s', expired_list)
|
||||||
|
return expired_list
|
||||||
|
|
||||||
def is_document_checked_out(self, document):
|
def is_document_checked_out(self, document):
|
||||||
if self.model.objects.filter(document=document):
|
if self.model.objects.filter(document=document):
|
||||||
return True
|
return True
|
||||||
|
|||||||
33
mayan/apps/checkouts/methods.py
Normal file
33
mayan/apps/checkouts/methods.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
|
||||||
|
def method_check_in(self, user=None):
|
||||||
|
DocumentCheckout = apps.get_model(
|
||||||
|
app_label='checkouts', model_name='DocumentCheckout'
|
||||||
|
)
|
||||||
|
return DocumentCheckout.objects.check_in_document(
|
||||||
|
document=self, user=user
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_checkout_info(self):
|
||||||
|
DocumentCheckout = apps.get_model(
|
||||||
|
app_label='checkouts', model_name='DocumentCheckout'
|
||||||
|
)
|
||||||
|
return DocumentCheckout.objects.get_document_checkout_info(document=self)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_checkout_state(self):
|
||||||
|
DocumentCheckout = apps.get_model(
|
||||||
|
app_label='checkouts', model_name='DocumentCheckout'
|
||||||
|
)
|
||||||
|
return DocumentCheckout.objects.get_document_checkout_state(document=self)
|
||||||
|
|
||||||
|
|
||||||
|
def method_is_checked_out(self):
|
||||||
|
DocumentCheckout = apps.get_model(
|
||||||
|
app_label='checkouts', model_name='DocumentCheckout'
|
||||||
|
)
|
||||||
|
return DocumentCheckout.objects.is_document_checked_out(document=self)
|
||||||
@@ -86,19 +86,19 @@ class CheckoutListView(DocumentListView):
|
|||||||
{
|
{
|
||||||
'name': _('User'),
|
'name': _('User'),
|
||||||
'attribute': encapsulate(
|
'attribute': encapsulate(
|
||||||
lambda document: document.checkout_info().user.get_full_name() or document.checkout_info().user
|
lambda document: document.get_checkout_info().user.get_full_name() or document.get_checkout_info().user
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': _('Checkout time and date'),
|
'name': _('Checkout time and date'),
|
||||||
'attribute': encapsulate(
|
'attribute': encapsulate(
|
||||||
lambda document: document.checkout_info().checkout_datetime
|
lambda document: document.get_checkout_info().checkout_datetime
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': _('Checkout expiration'),
|
'name': _('Checkout expiration'),
|
||||||
'attribute': encapsulate(
|
'attribute': encapsulate(
|
||||||
lambda document: document.checkout_info().expiration_datetime
|
lambda document: document.get_checkout_info().expiration_datetime
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -140,7 +140,7 @@ class DocumentCheckinView(ConfirmView):
|
|||||||
'object': document,
|
'object': document,
|
||||||
}
|
}
|
||||||
|
|
||||||
if document.checkout_info().user != self.request.user:
|
if document.get_checkout_info().user != self.request.user:
|
||||||
context['title'] = _(
|
context['title'] = _(
|
||||||
'You didn\'t originally checked out this document. '
|
'You didn\'t originally checked out this document. '
|
||||||
'Forcefully check in the document: %s?'
|
'Forcefully check in the document: %s?'
|
||||||
@@ -159,7 +159,7 @@ class DocumentCheckinView(ConfirmView):
|
|||||||
def view_action(self):
|
def view_action(self):
|
||||||
document = self.get_object()
|
document = self.get_object()
|
||||||
|
|
||||||
if document.checkout_info().user == self.request.user:
|
if document.get_checkout_info().user == self.request.user:
|
||||||
AccessControlList.objects.check_access(
|
AccessControlList.objects.check_access(
|
||||||
permissions=permission_document_checkin,
|
permissions=permission_document_checkin,
|
||||||
user=self.request.user, obj=document
|
user=self.request.user, obj=document
|
||||||
|
|||||||
@@ -226,13 +226,27 @@ class ModelAttribute(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.get_display()
|
return self.get_display()
|
||||||
|
|
||||||
def get_display(self, show_name=False):
|
def get_label(self):
|
||||||
|
if self.label:
|
||||||
|
return self.label
|
||||||
|
else:
|
||||||
|
return getattr(
|
||||||
|
getattr(self.model, self.name), 'short_description', self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
if self.description:
|
if self.description:
|
||||||
|
return self.description
|
||||||
|
else:
|
||||||
|
return getattr(getattr(self.model, self.name), 'help_text', None)
|
||||||
|
|
||||||
|
def get_display(self, show_name=False):
|
||||||
|
if self.get_description():
|
||||||
return '{} - {}'.format(
|
return '{} - {}'.format(
|
||||||
self.name if show_name else self.label, self.description
|
self.name if show_name else self.get_label(), self.get_description()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return force_text(self.name if show_name else self.label)
|
return force_text(self.name if show_name else self.get_label())
|
||||||
|
|
||||||
|
|
||||||
class ModelField(ModelAttribute):
|
class ModelField(ModelAttribute):
|
||||||
@@ -344,31 +358,6 @@ class Package(object):
|
|||||||
self.__class__._registry.append(self)
|
self.__class__._registry.append(self)
|
||||||
|
|
||||||
|
|
||||||
class PropertyHelper(object):
|
|
||||||
"""
|
|
||||||
Makes adding fields using __class__.add_to_class easier.
|
|
||||||
Each subclass must implement the `constructor` and the `get_result`
|
|
||||||
method.
|
|
||||||
"""
|
|
||||||
@staticmethod
|
|
||||||
@property
|
|
||||||
def constructor(source_object):
|
|
||||||
return PropertyHelper(source_object)
|
|
||||||
|
|
||||||
def __init__(self, instance):
|
|
||||||
self.instance = instance
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return self.get_result(name=name)
|
|
||||||
|
|
||||||
def get_result(self, name):
|
|
||||||
"""
|
|
||||||
The method that produces the actual result. Must be implemented
|
|
||||||
by each subclass.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class Template(object):
|
class Template(object):
|
||||||
_registry = {}
|
_registry = {}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ErrorLogEntryManager(models.Manager):
|
|||||||
app_label='common', model_name='ErrorLogEntry'
|
app_label='common', model_name='ErrorLogEntry'
|
||||||
)
|
)
|
||||||
model.add_to_class(
|
model.add_to_class(
|
||||||
name='error_logs', value=GenericRelation(ErrorLogEntry)
|
name='error_logs', value=GenericRelation(to=ErrorLogEntry)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ TEST_INDEX_LABEL_EDITED = 'test edited label'
|
|||||||
TEST_INDEX_SLUG = 'test_slug'
|
TEST_INDEX_SLUG = 'test_slug'
|
||||||
TEST_METADATA_TYPE_LABEL = 'test metadata label'
|
TEST_METADATA_TYPE_LABEL = 'test metadata label'
|
||||||
TEST_METADATA_TYPE_NAME = 'test_metadata_name'
|
TEST_METADATA_TYPE_NAME = 'test_metadata_name'
|
||||||
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{ document.metadata_value_of.%s }}' % TEST_METADATA_TYPE_NAME
|
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{ document.get_metadata("%s").value }}' % 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 }}'
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.utils.timezone import now
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from kombu import Exchange, Queue
|
from kombu import Exchange, Queue
|
||||||
@@ -16,14 +14,12 @@ from mayan.apps.common import (
|
|||||||
menu_tools
|
menu_tools
|
||||||
)
|
)
|
||||||
from mayan.apps.common.classes import ModelAttribute, ModelField
|
from mayan.apps.common.classes import ModelAttribute, ModelField
|
||||||
from mayan.apps.common.settings import settings_db_sync_task_delay
|
|
||||||
from mayan.apps.documents.search import document_page_search, document_search
|
from mayan.apps.documents.search import document_page_search, document_search
|
||||||
from mayan.apps.documents.signals import post_version_upload
|
from mayan.apps.documents.signals import post_version_upload
|
||||||
from mayan.apps.documents.widgets import document_link
|
from mayan.apps.documents.widgets import document_link
|
||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
|
|
||||||
from .events import event_parsing_document_version_submit
|
|
||||||
from .handlers import (
|
from .handlers import (
|
||||||
handler_index_document, handler_initialize_new_parsing_settings,
|
handler_index_document, handler_initialize_new_parsing_settings,
|
||||||
handler_parse_document_version
|
handler_parse_document_version
|
||||||
@@ -35,36 +31,20 @@ from .links import (
|
|||||||
link_document_type_parsing_settings, link_document_type_submit,
|
link_document_type_parsing_settings, link_document_type_submit,
|
||||||
link_error_list
|
link_error_list
|
||||||
)
|
)
|
||||||
|
from .methods import (
|
||||||
|
method_document_submit_for_parsing,
|
||||||
|
method_document_version_submit_for_parsing,
|
||||||
|
method_get_document_content, method_get_document_version_content
|
||||||
|
)
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_content_view, permission_document_type_parsing_setup,
|
permission_content_view, permission_document_type_parsing_setup,
|
||||||
permission_parse_document
|
permission_parse_document
|
||||||
)
|
)
|
||||||
from .signals import post_document_version_parsing
|
from .signals import post_document_version_parsing
|
||||||
from .utils import document_property_content, get_document_content
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def document_parsing_submit(self):
|
|
||||||
latest_version = self.latest_version
|
|
||||||
# Don't error out if document has no version
|
|
||||||
if latest_version:
|
|
||||||
latest_version.submit_for_parsing()
|
|
||||||
|
|
||||||
|
|
||||||
def document_version_parsing_submit(self):
|
|
||||||
from .tasks import task_parse_document_version
|
|
||||||
|
|
||||||
event_parsing_document_version_submit.commit(
|
|
||||||
action_object=self.document, target=self
|
|
||||||
)
|
|
||||||
|
|
||||||
task_parse_document_version.apply_async(
|
|
||||||
eta=now() + timedelta(seconds=settings_db_sync_task_delay.value),
|
|
||||||
kwargs={'document_version_pk': self.pk},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentParsingApp(MayanAppConfig):
|
class DocumentParsingApp(MayanAppConfig):
|
||||||
app_namespace = 'document_parsing'
|
app_namespace = 'document_parsing'
|
||||||
app_url = 'parsing'
|
app_url = 'parsing'
|
||||||
@@ -96,26 +76,24 @@ class DocumentParsingApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='submit_for_parsing', value=document_parsing_submit
|
name='submit_for_parsing',
|
||||||
|
value=method_document_submit_for_parsing
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='content', value=document_property_content
|
name='get_content', value=method_get_document_content
|
||||||
)
|
)
|
||||||
DocumentVersion.add_to_class(
|
DocumentVersion.add_to_class(
|
||||||
name='content', value=get_document_content
|
name='get_content', value=method_get_document_version_content
|
||||||
)
|
)
|
||||||
DocumentVersion.add_to_class(
|
DocumentVersion.add_to_class(
|
||||||
name='submit_for_parsing', value=document_version_parsing_submit
|
name='submit_for_parsing',
|
||||||
|
value=method_document_version_submit_for_parsing
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelAttribute(
|
ModelAttribute(model=Document, name='get_content')
|
||||||
model=Document, name='content', description=_(
|
|
||||||
'The parsed content of the document.'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ModelField(
|
ModelField(
|
||||||
Document, name='versions__pages__content__content'
|
model=Document, name='versions__pages__content__content'
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
|
|||||||
56
mayan/apps/document_parsing/methods.py
Normal file
56
mayan/apps/document_parsing/methods.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.common.settings import settings_db_sync_task_delay
|
||||||
|
|
||||||
|
from .events import event_parsing_document_version_submit
|
||||||
|
from .tasks import task_parse_document_version
|
||||||
|
from .utils import get_document_version_content_iterator
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_submit_for_parsing(self):
|
||||||
|
latest_version = self.latest_version
|
||||||
|
# Don't error out if document has no version
|
||||||
|
if latest_version:
|
||||||
|
latest_version.submit_for_parsing()
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_version_submit_for_parsing(self):
|
||||||
|
event_parsing_document_version_submit.commit(
|
||||||
|
action_object=self.document, target=self
|
||||||
|
)
|
||||||
|
|
||||||
|
task_parse_document_version.apply_async(
|
||||||
|
eta=now() + timedelta(seconds=settings_db_sync_task_delay.value),
|
||||||
|
kwargs={'document_version_pk': self.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_document_content(self):
|
||||||
|
latest_version = self.latest_version
|
||||||
|
|
||||||
|
if latest_version:
|
||||||
|
return latest_version.get_content()
|
||||||
|
|
||||||
|
|
||||||
|
method_get_document_content.help_text = _(
|
||||||
|
'Return the parsed content of the document.'
|
||||||
|
)
|
||||||
|
method_get_document_content.short_description = _(
|
||||||
|
'get_content()'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_document_version_content(self):
|
||||||
|
return ' '.join(
|
||||||
|
get_document_version_content_iterator(document_version=self)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
method_get_document_version_content.help_text = _(
|
||||||
|
'Return the parsed content of the document version.'
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
TEST_DOCUMENT_CONTENT = 'Sample text'
|
TEST_DOCUMENT_CONTENT = 'Sample text'
|
||||||
TEST_PARSING_INDEX_NODE_TEMPLATE = '{% if "sample" in document.content.lower() %}sample{% endif %}'
|
TEST_PARSING_INDEX_NODE_TEMPLATE = '{% if "sample" in document.get_content().lower() %}sample{% endif %}'
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ class DocumentAutoParsingTestCase(GenericDocumentTestCase):
|
|||||||
self._create_document_type()
|
self._create_document_type()
|
||||||
self.document = self.upload_document()
|
self.document = self.upload_document()
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT in self.document.content
|
TEST_DOCUMENT_CONTENT in self.document.get_content()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from ..permissions import (
|
|||||||
permission_content_view, permission_document_type_parsing_setup,
|
permission_content_view, permission_document_type_parsing_setup,
|
||||||
permission_parse_document
|
permission_parse_document
|
||||||
)
|
)
|
||||||
from ..utils import get_document_content
|
from ..utils import get_document_content_iterator
|
||||||
|
|
||||||
from .literals import TEST_DOCUMENT_CONTENT
|
from .literals import TEST_DOCUMENT_CONTENT
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.assert_download_response(
|
self.assert_download_response(
|
||||||
response=response, content=(
|
response=response, content=(
|
||||||
''.join(get_document_content(document=self.document))
|
''.join(get_document_content_iterator(document=self.document))
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
response = self._request_document_type_submit_view()
|
response = self._request_document_type_submit_view()
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT not in self.document.content
|
TEST_DOCUMENT_CONTENT not in self.document.get_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_type_submit_view_with_access(self):
|
def test_document_type_submit_view_with_access(self):
|
||||||
@@ -142,5 +142,5 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
response = self._request_document_type_submit_view()
|
response = self._request_document_type_submit_view()
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT in self.document.content
|
TEST_DOCUMENT_CONTENT in self.document.get_content()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,20 +5,24 @@ from django.utils.encoding import force_text
|
|||||||
from django.utils.html import conditional_escape
|
from django.utils.html import conditional_escape
|
||||||
|
|
||||||
|
|
||||||
def get_document_content(document):
|
def get_document_content_iterator(document):
|
||||||
|
latest_version = document.latest_version
|
||||||
|
|
||||||
|
if latest_version:
|
||||||
|
return get_document_version_content_iterator(
|
||||||
|
document_version=latest_version
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_document_version_content_iterator(document_version):
|
||||||
DocumentPageContent = apps.get_model(
|
DocumentPageContent = apps.get_model(
|
||||||
app_label='document_parsing', model_name='DocumentPageContent'
|
app_label='document_parsing', model_name='DocumentPageContent'
|
||||||
)
|
)
|
||||||
|
|
||||||
for page in document.pages.all():
|
for page in document_version.pages.all():
|
||||||
try:
|
try:
|
||||||
page_content = page.content.content
|
page_content = page.content.content
|
||||||
except DocumentPageContent.DoesNotExist:
|
except DocumentPageContent.DoesNotExist:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
yield conditional_escape(force_text(page_content))
|
yield conditional_escape(force_text(page_content))
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def document_property_content(self):
|
|
||||||
return ' '.join(get_document_content(self))
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@@ -20,7 +21,7 @@ from .permissions import (
|
|||||||
permission_content_view, permission_document_type_parsing_setup,
|
permission_content_view, permission_document_type_parsing_setup,
|
||||||
permission_parse_document
|
permission_parse_document
|
||||||
)
|
)
|
||||||
from .utils import get_document_content
|
from .utils import get_document_content_iterator
|
||||||
|
|
||||||
|
|
||||||
class DocumentContentView(SingleObjectDetailView):
|
class DocumentContentView(SingleObjectDetailView):
|
||||||
@@ -50,7 +51,7 @@ class DocumentContentDownloadView(SingleObjectDownloadView):
|
|||||||
|
|
||||||
def get_file(self):
|
def get_file(self):
|
||||||
file_object = DocumentContentDownloadView.TextIteratorIO(
|
file_object = DocumentContentDownloadView.TextIteratorIO(
|
||||||
iterator=get_document_content(document=self.get_object())
|
iterator=get_document_content_iterator(document=self.get_object())
|
||||||
)
|
)
|
||||||
return DocumentContentDownloadView.VirtualFile(
|
return DocumentContentDownloadView.VirtualFile(
|
||||||
file=file_object, name='{}-content'.format(self.get_object())
|
file=file_object, name='{}-content'.format(self.get_object())
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ from mayan.apps.common.widgets import TwoStateWidget
|
|||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
|
|
||||||
from .classes import DocumentStateHelper, WorkflowAction
|
from .classes import WorkflowAction
|
||||||
from .handlers import (
|
from .handlers import (
|
||||||
handler_index_document, handler_trigger_transition, launch_workflow
|
handler_index_document, handler_trigger_transition, launch_workflow
|
||||||
)
|
)
|
||||||
|
from .methods import method_get_workflow
|
||||||
from .links import (
|
from .links import (
|
||||||
link_document_workflow_instance_list, link_setup_workflow_create,
|
link_document_workflow_instance_list, link_setup_workflow_create,
|
||||||
link_setup_workflow_delete, link_setup_workflow_document_types,
|
link_setup_workflow_delete, link_setup_workflow_document_types,
|
||||||
@@ -84,28 +85,14 @@ class DocumentStatesApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='workflow', value=DocumentStateHelper.constructor
|
name='get_workflow', value=method_get_workflow
|
||||||
)
|
)
|
||||||
|
|
||||||
ErrorLogEntry.objects.register(model=WorkflowStateAction)
|
ErrorLogEntry.objects.register(model=WorkflowStateAction)
|
||||||
|
|
||||||
WorkflowAction.initialize()
|
WorkflowAction.initialize()
|
||||||
|
|
||||||
ModelAttribute(
|
ModelAttribute(model=Document, name='get_workflow')
|
||||||
model=Document,
|
|
||||||
name='workflow.< workflow internal name >.get_current_state()',
|
|
||||||
label=_('Current state of a workflow'), description=_(
|
|
||||||
'Return the current state of the selected workflow'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
ModelAttribute(
|
|
||||||
model=Document,
|
|
||||||
name='workflow.< workflow internal name >.get_current_state().completion',
|
|
||||||
label=_('Current state of a workflow'), description=_(
|
|
||||||
'Return the completion value of the current state of the '
|
|
||||||
'selected workflow'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
model=Document, permissions=(permission_workflow_view,)
|
model=Document, permissions=(permission_workflow_view,)
|
||||||
|
|||||||
@@ -7,22 +7,10 @@ from django.apps import apps
|
|||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from mayan.apps.common.classes import PropertyHelper
|
|
||||||
|
|
||||||
__all__ = ('WorkflowAction',)
|
__all__ = ('WorkflowAction',)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DocumentStateHelper(PropertyHelper):
|
|
||||||
@staticmethod
|
|
||||||
@property
|
|
||||||
def constructor(*args, **kwargs):
|
|
||||||
return DocumentStateHelper(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_result(self, name):
|
|
||||||
return self.instance.workflows.get(workflow__internal_name=name)
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowActionMetaclass(type):
|
class WorkflowActionMetaclass(type):
|
||||||
_registry = {}
|
_registry = {}
|
||||||
|
|
||||||
|
|||||||
15
mayan/apps/document_states/methods.py
Normal file
15
mayan/apps/document_states/methods.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_workflow(self, name):
|
||||||
|
return self.workflows.get(workflow__internal_name=name)
|
||||||
|
|
||||||
|
|
||||||
|
method_get_workflow.short_description = _(
|
||||||
|
'get_workflow(< workflow internal name >)'
|
||||||
|
)
|
||||||
|
method_get_workflow.help_text = _(
|
||||||
|
'Return the current state of the selected workflow.'
|
||||||
|
)
|
||||||
@@ -15,6 +15,6 @@ TEST_WORKFLOW_TRANSITION_LABEL = 'test transition label'
|
|||||||
TEST_WORKFLOW_TRANSITION_LABEL_2 = 'test transition label 2'
|
TEST_WORKFLOW_TRANSITION_LABEL_2 = 'test transition label 2'
|
||||||
TEST_WORKFLOW_TRANSITION_LABEL_EDITED = 'test transition label edited'
|
TEST_WORKFLOW_TRANSITION_LABEL_EDITED = 'test transition label edited'
|
||||||
|
|
||||||
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{{{ document.workflow.{}.get_current_state() }}}}'.format(
|
TEST_INDEX_TEMPLATE_METADATA_EXPRESSION = '{{{{ document.get_workflow.("{}").get_current_state() }}}}'.format(
|
||||||
TEST_WORKFLOW_INTERNAL_NAME
|
TEST_WORKFLOW_INTERNAL_NAME
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from mayan.apps.acls import ModelPermission
|
|||||||
from mayan.apps.common import (
|
from mayan.apps.common import (
|
||||||
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_tools
|
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_tools
|
||||||
)
|
)
|
||||||
|
from mayan.apps.common.classes import ModelAttribute, ModelField
|
||||||
from mayan.apps.document_indexing.handlers import handler_index_document
|
from mayan.apps.document_indexing.handlers import handler_index_document
|
||||||
from mayan.apps.documents.search import document_page_search, document_search
|
from mayan.apps.documents.search import document_page_search, document_search
|
||||||
from mayan.apps.documents.signals import post_version_upload
|
from mayan.apps.documents.signals import post_version_upload
|
||||||
@@ -31,16 +32,16 @@ from .links import (
|
|||||||
link_document_submit, link_document_submit_multiple,
|
link_document_submit, link_document_submit_multiple,
|
||||||
link_document_type_file_metadata_settings, link_document_type_submit
|
link_document_type_file_metadata_settings, link_document_type_submit
|
||||||
)
|
)
|
||||||
|
from .methods import (
|
||||||
|
method_document_submit, method_document_version_submit,
|
||||||
|
method_get_document_file_metadata,
|
||||||
|
method_get_document_version_file_metadata
|
||||||
|
)
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_document_type_file_metadata_setup,
|
permission_document_type_file_metadata_setup,
|
||||||
permission_file_metadata_submit, permission_file_metadata_view
|
permission_file_metadata_submit, permission_file_metadata_view
|
||||||
)
|
)
|
||||||
from .signals import post_document_version_file_metadata_processing
|
from .signals import post_document_version_file_metadata_processing
|
||||||
from .utils import (
|
|
||||||
method_document_submit, method_document_version_submit,
|
|
||||||
method_get_document_file_metadata,
|
|
||||||
method_get_document_version_file_metadata
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FileMetadataApp(MayanAppConfig):
|
class FileMetadataApp(MayanAppConfig):
|
||||||
@@ -78,14 +79,16 @@ class FileMetadataApp(MayanAppConfig):
|
|||||||
name='get_file_metadata',
|
name='get_file_metadata',
|
||||||
value=method_get_document_file_metadata
|
value=method_get_document_file_metadata
|
||||||
)
|
)
|
||||||
DocumentVersion.add_to_class(
|
|
||||||
name='submit_for_file_metadata_processing',
|
|
||||||
value=method_document_version_submit
|
|
||||||
)
|
|
||||||
DocumentVersion.add_to_class(
|
DocumentVersion.add_to_class(
|
||||||
name='get_file_metadata',
|
name='get_file_metadata',
|
||||||
value=method_get_document_version_file_metadata
|
value=method_get_document_version_file_metadata
|
||||||
)
|
)
|
||||||
|
DocumentVersion.add_to_class(
|
||||||
|
name='submit_for_file_metadata_processing',
|
||||||
|
value=method_document_version_submit
|
||||||
|
)
|
||||||
|
|
||||||
|
ModelAttribute(model=Document, name='get_file_metadata')
|
||||||
|
|
||||||
ModelEventType.register(
|
ModelEventType.register(
|
||||||
model=Document, event_types=(
|
model=Document, event_types=(
|
||||||
@@ -94,6 +97,15 @@ class FileMetadataApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ModelField(
|
||||||
|
label=_('File metadata key'), model=Document,
|
||||||
|
name='versions__file_metadata_drivers__entries__key',
|
||||||
|
)
|
||||||
|
ModelField(
|
||||||
|
label=_('File metadata key'), model=Document,
|
||||||
|
name='versions__file_metadata_drivers__entries__value',
|
||||||
|
)
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
model=Document, permissions=(
|
model=Document, permissions=(
|
||||||
permission_file_metadata_submit, permission_file_metadata_view,
|
permission_file_metadata_submit, permission_file_metadata_view,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .events import event_file_metadata_document_version_submit
|
from .events import event_file_metadata_document_version_submit
|
||||||
from .tasks import task_process_document_version
|
from .tasks import task_process_document_version
|
||||||
|
|
||||||
@@ -32,6 +34,14 @@ def method_get_document_file_metadata(self, dotted_name):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
method_get_document_file_metadata.short_description=_(
|
||||||
|
'get_file_metadata(< file metadata dotted path >)'
|
||||||
|
)
|
||||||
|
method_get_document_file_metadata.help_text = _(
|
||||||
|
'Return the specified document file metadata entry.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def method_get_document_version_file_metadata(self, dotted_name):
|
def method_get_document_version_file_metadata(self, dotted_name):
|
||||||
driver_internal_name, key = dotted_name.split('.')
|
driver_internal_name, key = dotted_name.split('.')
|
||||||
|
|
||||||
@@ -46,3 +56,8 @@ def method_get_document_version_file_metadata(self, dotted_name):
|
|||||||
return document_driver.entries.get(key=key).value
|
return document_driver.entries.get(key=key).value
|
||||||
except document_driver.entries.model.DoesNotExist:
|
except document_driver.entries.model.DoesNotExist:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
method_get_document_version_file_metadata.help_text = _(
|
||||||
|
'Return the specified document version file metadata entry.'
|
||||||
|
)
|
||||||
@@ -100,10 +100,12 @@ class FileMetadataEntry(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
key = models.CharField(
|
key = models.CharField(
|
||||||
db_index=True, max_length=255, verbose_name=_('Key')
|
db_index=True, help_text=_('Name of the file metadata entry.'),
|
||||||
|
max_length=255, verbose_name=_('Key')
|
||||||
)
|
)
|
||||||
value = models.CharField(
|
value = models.CharField(
|
||||||
db_index=True, max_length=255, verbose_name=_('Value')
|
db_index=True, help_text=_('Value of the file metadata entry.'),
|
||||||
|
max_length=255, verbose_name=_('Value')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ from mayan.apps.events.permissions import permission_events_view
|
|||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
|
|
||||||
from .classes import DocumentMetadataHelper
|
|
||||||
from .events import (
|
from .events import (
|
||||||
event_document_metadata_added, event_document_metadata_edited,
|
event_document_metadata_added, event_document_metadata_edited,
|
||||||
event_document_metadata_removed, event_metadata_type_edited,
|
event_document_metadata_removed, event_metadata_type_edited,
|
||||||
@@ -46,13 +45,13 @@ from .links import (
|
|||||||
link_setup_metadata_type_delete, link_setup_metadata_type_document_types,
|
link_setup_metadata_type_delete, link_setup_metadata_type_document_types,
|
||||||
link_setup_metadata_type_edit, link_setup_metadata_type_list,
|
link_setup_metadata_type_edit, link_setup_metadata_type_list,
|
||||||
)
|
)
|
||||||
|
from .methods import method_get_metadata
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_metadata_document_add, permission_metadata_document_edit,
|
permission_metadata_document_add, permission_metadata_document_edit,
|
||||||
permission_metadata_document_remove, permission_metadata_document_view,
|
permission_metadata_document_remove, permission_metadata_document_view,
|
||||||
permission_metadata_type_delete, permission_metadata_type_edit,
|
permission_metadata_type_delete, permission_metadata_type_edit,
|
||||||
permission_metadata_type_view
|
permission_metadata_type_view
|
||||||
)
|
)
|
||||||
|
|
||||||
from .queues import * # NOQA
|
from .queues import * # NOQA
|
||||||
from .search import metadata_type_search # NOQA
|
from .search import metadata_type_search # NOQA
|
||||||
from .widgets import get_metadata_string
|
from .widgets import get_metadata_string
|
||||||
@@ -90,23 +89,18 @@ class MetadataApp(MayanAppConfig):
|
|||||||
MetadataType = self.get_model('MetadataType')
|
MetadataType = self.get_model('MetadataType')
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='metadata_value_of',
|
name='get_metadata', value=method_get_metadata
|
||||||
value=DocumentMetadataHelper.constructor
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelAttribute(
|
ModelAttribute(model=Document, name='get_metadata')
|
||||||
Document, 'metadata_value_of',
|
|
||||||
description=_(
|
|
||||||
'Return the value of a specific document metadata'
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
ModelField(
|
ModelField(
|
||||||
Document, 'metadata__metadata_type__name',
|
label=_('Metadata type name'), model=Document,
|
||||||
label=_('Metadata type name')
|
name='metadata__metadata_type__name'
|
||||||
)
|
)
|
||||||
ModelField(
|
ModelField(
|
||||||
Document, 'metadata__value', label=_('Metadata type value'),
|
label=_('Metadata type value'), model=Document,
|
||||||
|
name='metadata__value',
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelEventType.register(
|
ModelEventType.register(
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from mayan.apps.common.classes import PropertyHelper
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentMetadataHelper(PropertyHelper):
|
|
||||||
@staticmethod
|
|
||||||
@property
|
|
||||||
def constructor(*args, **kwargs):
|
|
||||||
return DocumentMetadataHelper(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_result(self, name):
|
|
||||||
return self.instance.metadata.get(metadata_type__name=name).value
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataLookup(object):
|
class MetadataLookup(object):
|
||||||
_registry = []
|
_registry = []
|
||||||
|
|||||||
15
mayan/apps/metadata/methods.py
Normal file
15
mayan/apps/metadata/methods.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_metadata(self, internal_name):
|
||||||
|
return self.metadata.get(metadata_type__name=internal_name)
|
||||||
|
|
||||||
|
|
||||||
|
method_get_metadata.short_description = _(
|
||||||
|
'get_metadata(< metadata type internal name >)'
|
||||||
|
)
|
||||||
|
method_get_metadata.help_text = _(
|
||||||
|
'Return the specified document metadata.'
|
||||||
|
)
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from kombu import Exchange, Queue
|
from kombu import Exchange, Queue
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.utils.timezone import now
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mayan.apps.acls import ModelPermission
|
from mayan.apps.acls import ModelPermission
|
||||||
@@ -16,14 +14,12 @@ from mayan.apps.common import (
|
|||||||
menu_tools
|
menu_tools
|
||||||
)
|
)
|
||||||
from mayan.apps.common.classes import ModelAttribute, ModelField
|
from mayan.apps.common.classes import ModelAttribute, ModelField
|
||||||
from mayan.apps.common.settings import settings_db_sync_task_delay
|
|
||||||
from mayan.apps.documents.search import document_search, document_page_search
|
from mayan.apps.documents.search import document_search, document_page_search
|
||||||
from mayan.apps.documents.signals import post_version_upload
|
from mayan.apps.documents.signals import post_version_upload
|
||||||
from mayan.apps.documents.widgets import document_link
|
from mayan.apps.documents.widgets import document_link
|
||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
|
|
||||||
from .events import event_ocr_document_version_submit
|
|
||||||
from .handlers import (
|
from .handlers import (
|
||||||
handler_index_document, handler_initialize_new_ocr_settings,
|
handler_index_document, handler_initialize_new_ocr_settings,
|
||||||
handler_ocr_document_version,
|
handler_ocr_document_version,
|
||||||
@@ -35,37 +31,20 @@ from .links import (
|
|||||||
link_document_type_ocr_settings, link_document_type_submit,
|
link_document_type_ocr_settings, link_document_type_submit,
|
||||||
link_entry_list
|
link_entry_list
|
||||||
)
|
)
|
||||||
|
from .methods import (
|
||||||
|
method_document_ocr_submit, method_document_version_ocr_submit,
|
||||||
|
method_get_document_ocr_content, method_get_document_version_ocr_content
|
||||||
|
)
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_document_type_ocr_setup, permission_ocr_document,
|
permission_document_type_ocr_setup, permission_ocr_document,
|
||||||
permission_ocr_content_view
|
permission_ocr_content_view
|
||||||
)
|
)
|
||||||
from .queues import * # NOQA
|
from .queues import * # NOQA
|
||||||
from .signals import post_document_version_ocr
|
from .signals import post_document_version_ocr
|
||||||
from .utils import document_property_ocr_content
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def document_ocr_submit(self):
|
|
||||||
latest_version = self.latest_version
|
|
||||||
# Don't error out if document has no version
|
|
||||||
if latest_version:
|
|
||||||
latest_version.submit_for_ocr()
|
|
||||||
|
|
||||||
|
|
||||||
def document_version_ocr_submit(self):
|
|
||||||
from .tasks import task_do_ocr
|
|
||||||
|
|
||||||
event_ocr_document_version_submit.commit(
|
|
||||||
action_object=self.document, target=self
|
|
||||||
)
|
|
||||||
|
|
||||||
task_do_ocr.apply_async(
|
|
||||||
eta=now() + timedelta(seconds=settings_db_sync_task_delay.value),
|
|
||||||
kwargs={'document_version_pk': self.pk},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OCRApp(MayanAppConfig):
|
class OCRApp(MayanAppConfig):
|
||||||
app_namespace = 'ocr'
|
app_namespace = 'ocr'
|
||||||
app_url = 'ocr'
|
app_url = 'ocr'
|
||||||
@@ -96,22 +75,24 @@ class OCRApp(MayanAppConfig):
|
|||||||
DocumentVersionOCRError = self.get_model('DocumentVersionOCRError')
|
DocumentVersionOCRError = self.get_model('DocumentVersionOCRError')
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='submit_for_ocr', value=document_ocr_submit)
|
name='get_ocr_content',
|
||||||
|
value=method_get_document_ocr_content
|
||||||
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
name='ocr_content', value=document_property_ocr_content
|
name='submit_for_ocr', value=method_document_ocr_submit
|
||||||
)
|
)
|
||||||
DocumentVersion.add_to_class(
|
DocumentVersion.add_to_class(
|
||||||
name='submit_for_ocr', value=document_version_ocr_submit
|
name='get_ocr_content',
|
||||||
|
value=method_get_document_version_ocr_content
|
||||||
|
)
|
||||||
|
DocumentVersion.add_to_class(
|
||||||
|
name='submit_for_ocr', value=method_document_version_ocr_submit
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelAttribute(
|
ModelAttribute(model=Document, name='get_ocr_content')
|
||||||
model=Document, name='ocr_content', description=_(
|
|
||||||
'The OCR content of the document.'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ModelField(
|
ModelField(
|
||||||
Document, name='versions__pages__ocr_content__content'
|
model=Document, name='versions__pages__ocr_content__content'
|
||||||
)
|
)
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
|
|||||||
51
mayan/apps/ocr/methods.py
Normal file
51
mayan/apps/ocr/methods.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.common.settings import settings_db_sync_task_delay
|
||||||
|
|
||||||
|
from .events import event_ocr_document_version_submit
|
||||||
|
from .tasks import task_do_ocr
|
||||||
|
from .utils import get_document_version_content_iterator
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_ocr_submit(self):
|
||||||
|
latest_version = self.latest_version
|
||||||
|
# Don't error out if document has no version
|
||||||
|
if latest_version:
|
||||||
|
latest_version.submit_for_ocr()
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_version_ocr_submit(self):
|
||||||
|
event_ocr_document_version_submit.commit(
|
||||||
|
action_object=self.document, target=self
|
||||||
|
)
|
||||||
|
|
||||||
|
task_do_ocr.apply_async(
|
||||||
|
eta=now() + timedelta(seconds=settings_db_sync_task_delay.value),
|
||||||
|
kwargs={'document_version_pk': self.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_document_ocr_content(self):
|
||||||
|
latest_version = self.latest_version
|
||||||
|
# Don't error out if document has no version
|
||||||
|
if latest_version:
|
||||||
|
latest_version.get_ocr_content()
|
||||||
|
|
||||||
|
|
||||||
|
method_get_document_ocr_content.short_description = _(
|
||||||
|
'get_ocr_content()'
|
||||||
|
)
|
||||||
|
method_get_document_ocr_content.help_text = _(
|
||||||
|
'Return the OCR content of the document.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_document_version_ocr_content(self):
|
||||||
|
return ' '.join(
|
||||||
|
get_document_version_content_iterator(document_version=self)
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
TEST_OCR_INDEX_NODE_TEMPLATE = '{% if "mayan" in document.ocr_content.lower() %}mayan{% endif %}'
|
TEST_OCR_INDEX_NODE_TEMPLATE = '{% if "mayan" in document.get_ocr_content().lower() %}mayan{% endif %}'
|
||||||
TEST_OCR_INDEX_NODE_TEMPLATE_LEVEL = 'mayan'
|
TEST_OCR_INDEX_NODE_TEMPLATE_LEVEL = 'mayan'
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
self._request_document_submit_view()
|
self._request_document_submit_view()
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT in self.document.ocr_content
|
TEST_DOCUMENT_CONTENT in self.document.get_ocr_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _request_multiple_document_submit_view(self):
|
def _request_multiple_document_submit_view(self):
|
||||||
@@ -95,7 +95,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
def test_multiple_document_submit_view_no_permission(self):
|
def test_multiple_document_submit_view_no_permission(self):
|
||||||
self._request_multiple_document_submit_view()
|
self._request_multiple_document_submit_view()
|
||||||
self.assertEqual(self.document.ocr_content, '')
|
self.assertEqual(self.document.get_ocr_content(), '')
|
||||||
|
|
||||||
def test_multiple_document_submit_view_with_access(self):
|
def test_multiple_document_submit_view_with_access(self):
|
||||||
self.grant_access(
|
self.grant_access(
|
||||||
@@ -103,7 +103,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
self._request_multiple_document_submit_view()
|
self._request_multiple_document_submit_view()
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT in self.document.ocr_content
|
TEST_DOCUMENT_CONTENT in self.document.get_ocr_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _request_document_ocr_download_view(self):
|
def _request_document_ocr_download_view(self):
|
||||||
@@ -126,7 +126,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
self.assert_download_response(
|
self.assert_download_response(
|
||||||
response=response, content=self.document.ocr_content
|
response=response, content=self.document.get_ocr_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
response = self._request_document_type_submit_view()
|
response = self._request_document_type_submit_view()
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT not in self.document.ocr_content
|
TEST_DOCUMENT_CONTENT not in self.document.get_ocr_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_type_submit_view_with_access(self):
|
def test_document_type_submit_view_with_access(self):
|
||||||
@@ -179,5 +179,5 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
response = self._request_document_type_submit_view()
|
response = self._request_document_type_submit_view()
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
TEST_DOCUMENT_CONTENT in self.document.ocr_content
|
TEST_DOCUMENT_CONTENT in self.document.get_ocr_content()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,20 +4,24 @@ from django.apps import apps
|
|||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
|
||||||
def get_document_ocr_content(document):
|
def get_document_content_iterator(document):
|
||||||
|
latest_version = document.latest_version
|
||||||
|
|
||||||
|
if latest_version:
|
||||||
|
return get_document_version_content_iterator(
|
||||||
|
document_version=latest_version
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_document_version_content_iterator(document_version):
|
||||||
DocumentPageOCRContent = apps.get_model(
|
DocumentPageOCRContent = apps.get_model(
|
||||||
app_label='ocr', model_name='DocumentPageOCRContent'
|
app_label='ocr', model_name='DocumentPageOCRContent'
|
||||||
)
|
)
|
||||||
|
|
||||||
for page in document.pages.all():
|
for page in document_version.pages.all():
|
||||||
try:
|
try:
|
||||||
page_content = page.ocr_content.content
|
page_content = page.ocr_content.content
|
||||||
except DocumentPageOCRContent.DoesNotExist:
|
except DocumentPageOCRContent.DoesNotExist:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
yield force_text(page_content)
|
yield force_text(page_content)
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def document_property_ocr_content(self):
|
|
||||||
return ' '.join(get_document_ocr_content(self))
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from .permissions import (
|
|||||||
permission_ocr_content_view, permission_ocr_document,
|
permission_ocr_content_view, permission_ocr_document,
|
||||||
permission_document_type_ocr_setup
|
permission_document_type_ocr_setup
|
||||||
)
|
)
|
||||||
from .utils import get_document_ocr_content
|
from .utils import get_document_content_iterator
|
||||||
|
|
||||||
|
|
||||||
class DocumentOCRContentView(SingleObjectDetailView):
|
class DocumentOCRContentView(SingleObjectDetailView):
|
||||||
@@ -49,7 +49,7 @@ class DocumentOCRDownloadView(SingleObjectDownloadView):
|
|||||||
|
|
||||||
def get_file(self):
|
def get_file(self):
|
||||||
file_object = DocumentOCRDownloadView.TextIteratorIO(
|
file_object = DocumentOCRDownloadView.TextIteratorIO(
|
||||||
iterator=get_document_ocr_content(document=self.get_object())
|
iterator=get_document_content_iterator(document=self.get_object())
|
||||||
)
|
)
|
||||||
return DocumentOCRDownloadView.VirtualFile(
|
return DocumentOCRDownloadView.VirtualFile(
|
||||||
file=file_object, name='{}-OCR'.format(self.get_object())
|
file=file_object, name='{}-OCR'.format(self.get_object())
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ class APIDocumentTagListView(generics.ListCreateAPIView):
|
|||||||
obj=document
|
obj=document
|
||||||
)
|
)
|
||||||
|
|
||||||
return document.attached_tags().all()
|
return document.get_tags().all()
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
if not self.request:
|
if not self.request:
|
||||||
@@ -171,7 +171,7 @@ class APIDocumentTagView(generics.RetrieveDestroyAPIView):
|
|||||||
return document
|
return document
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.get_document().attached_tags().all()
|
return self.get_document().get_tags().all()
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
if not self.request:
|
if not self.request:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from mayan.apps.common import (
|
|||||||
MayanAppConfig, menu_facet, menu_list_facet, menu_object, menu_main,
|
MayanAppConfig, menu_facet, menu_list_facet, menu_object, menu_main,
|
||||||
menu_multi_item, menu_sidebar
|
menu_multi_item, menu_sidebar
|
||||||
)
|
)
|
||||||
from mayan.apps.common.classes import ModelField
|
from mayan.apps.common.classes import ModelAttribute, ModelField
|
||||||
from mayan.apps.documents.search import document_page_search, document_search
|
from mayan.apps.documents.search import document_page_search, document_search
|
||||||
from mayan.apps.events import ModelEventType
|
from mayan.apps.events import ModelEventType
|
||||||
from mayan.apps.events.links import (
|
from mayan.apps.events.links import (
|
||||||
@@ -32,6 +32,7 @@ from .links import (
|
|||||||
link_tag_multiple_delete, link_tag_tagged_item_list
|
link_tag_multiple_delete, link_tag_tagged_item_list
|
||||||
)
|
)
|
||||||
from .menus import menu_tags
|
from .menus import menu_tags
|
||||||
|
from .methods import method_get_tags
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_tag_attach, permission_tag_delete, permission_tag_edit,
|
permission_tag_attach, permission_tag_delete, permission_tag_edit,
|
||||||
permission_tag_remove, permission_tag_view
|
permission_tag_remove, permission_tag_view
|
||||||
@@ -65,10 +66,9 @@ class TagsApp(MayanAppConfig):
|
|||||||
DocumentTag = self.get_model('DocumentTag')
|
DocumentTag = self.get_model('DocumentTag')
|
||||||
Tag = self.get_model('Tag')
|
Tag = self.get_model('Tag')
|
||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(name='get_tags', value=method_get_tags)
|
||||||
name='attached_tags',
|
|
||||||
value=lambda document: DocumentTag.objects.filter(documents=document)
|
ModelAttribute(model=Document, name='get_tags')
|
||||||
)
|
|
||||||
|
|
||||||
ModelEventType.register(
|
ModelEventType.register(
|
||||||
model=Tag, event_types=(
|
model=Tag, event_types=(
|
||||||
|
|||||||
8
mayan/apps/tags/managers.py
Normal file
8
mayan/apps/tags/managers.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentTagManager(models.Manager):
|
||||||
|
def get_for(self, document):
|
||||||
|
return self.filter(document=document)
|
||||||
13
mayan/apps/tags/methods.py
Normal file
13
mayan/apps/tags/methods.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
def method_get_tags(self):
|
||||||
|
DocumentTag = apps.get_model(app_label='tags', model_name='DocumentTag')
|
||||||
|
return DocumentTag.objects.filter(documents=self)
|
||||||
|
|
||||||
|
|
||||||
|
method_get_tags.help_text = _('Return a the tags attached to the document.')
|
||||||
|
method_get_tags.short_description = _('get_tags()')
|
||||||
@@ -14,6 +14,7 @@ from mayan.apps.documents.permissions import permission_document_view
|
|||||||
from .events import (
|
from .events import (
|
||||||
event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove
|
event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove
|
||||||
)
|
)
|
||||||
|
from .managers import DocumentTagManager
|
||||||
from .widgets import widget_single_tag
|
from .widgets import widget_single_tag
|
||||||
|
|
||||||
|
|
||||||
@@ -95,6 +96,8 @@ class Tag(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class DocumentTag(Tag):
|
class DocumentTag(Tag):
|
||||||
|
objects = DocumentTagManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
verbose_name = _('Document tag')
|
verbose_name = _('Document tag')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ TEST_TAG_COLOR_EDITED = '#221100'
|
|||||||
TEST_TAG_INDEX_HAS_TAG = 'HAS_TAG'
|
TEST_TAG_INDEX_HAS_TAG = 'HAS_TAG'
|
||||||
TEST_TAG_INDEX_NO_TAG = 'NO_TAG'
|
TEST_TAG_INDEX_NO_TAG = 'NO_TAG'
|
||||||
TEST_TAG_INDEX_NODE_TEMPLATE = '''
|
TEST_TAG_INDEX_NODE_TEMPLATE = '''
|
||||||
{{% for tag in document.tags.all() %}}
|
{{% for tag in document.get_tags().all() %}}
|
||||||
{{% if tag.label == "{}" %}}
|
{{% if tag.label == "{}" %}}
|
||||||
{}
|
{}
|
||||||
{{% else %}}
|
{{% else %}}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class TagAttachActionView(MultipleObjectFormActionView):
|
|||||||
return super(TagAttachActionView, self).get_post_action_redirect()
|
return super(TagAttachActionView, self).get_post_action_redirect()
|
||||||
|
|
||||||
def object_action(self, form, instance):
|
def object_action(self, form, instance):
|
||||||
attached_tags = instance.attached_tags()
|
attached_tags = instance.get_tags()
|
||||||
|
|
||||||
for tag in form.cleaned_data['tags']:
|
for tag in form.cleaned_data['tags']:
|
||||||
AccessControlList.objects.check_access(
|
AccessControlList.objects.check_access(
|
||||||
@@ -269,7 +269,7 @@ class DocumentTagListView(TagListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_tag_queryset(self):
|
def get_tag_queryset(self):
|
||||||
return self.document.attached_tags().all()
|
return self.document.get_tags().all()
|
||||||
|
|
||||||
|
|
||||||
class TagRemoveActionView(MultipleObjectFormActionView):
|
class TagRemoveActionView(MultipleObjectFormActionView):
|
||||||
@@ -331,7 +331,7 @@ class TagRemoveActionView(MultipleObjectFormActionView):
|
|||||||
return super(TagRemoveActionView, self).get_post_action_redirect()
|
return super(TagRemoveActionView, self).get_post_action_redirect()
|
||||||
|
|
||||||
def object_action(self, form, instance):
|
def object_action(self, form, instance):
|
||||||
attached_tags = instance.attached_tags()
|
attached_tags = instance.get_tags()
|
||||||
|
|
||||||
for tag in form.cleaned_data['tags']:
|
for tag in form.cleaned_data['tags']:
|
||||||
AccessControlList.objects.check_access(
|
AccessControlList.objects.check_access(
|
||||||
|
|||||||
Reference in New Issue
Block a user