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.
|
||||
The new template can be accessed via the templates API.
|
||||
@@ -177,6 +177,16 @@
|
||||
BROKER_URL to CELERY_BROKER_URL.
|
||||
- Internal change. Add support to the SourceColumn class to resolve
|
||||
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)
|
||||
==================
|
||||
|
||||
@@ -8,23 +8,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ModelPermission(object):
|
||||
_registry = {}
|
||||
_proxies = {}
|
||||
_inheritances = {}
|
||||
|
||||
@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(AccessControlList))
|
||||
_proxies = {}
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def get_classes(cls, as_content_type=False):
|
||||
@@ -72,13 +58,29 @@ class ModelPermission(object):
|
||||
return StoredPermission.objects.filter(pk__in=pks)
|
||||
|
||||
@classmethod
|
||||
def register_proxy(cls, source, model):
|
||||
cls._proxies[model] = source
|
||||
def get_inheritance(cls, model):
|
||||
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
|
||||
def register_inheritance(cls, model, related):
|
||||
cls._inheritances[model] = related
|
||||
|
||||
@classmethod
|
||||
def get_inheritance(cls, model):
|
||||
return cls._inheritances[model]
|
||||
def register_proxy(cls, source, model):
|
||||
cls._proxies[model] = source
|
||||
|
||||
@@ -22,6 +22,7 @@ from .links import (
|
||||
link_document_cabinet_remove, link_multiple_document_cabinet_remove
|
||||
)
|
||||
from .menus import menu_cabinets
|
||||
from .methods import method_get_document_cabinets
|
||||
from .permissions import (
|
||||
permission_cabinet_add_document, permission_cabinet_delete,
|
||||
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
|
||||
# effect.
|
||||
Document.add_to_class(
|
||||
name='document_cabinets',
|
||||
value=lambda document: DocumentCabinet.objects.filter(documents=document).order_by('parent__label', 'label')
|
||||
name='get_document_cabinets', value=method_get_document_cabinets
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
@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
|
||||
def done(cls, wizard):
|
||||
result = {}
|
||||
@@ -44,6 +36,14 @@ class WizardStepCabinets(WizardStep):
|
||||
|
||||
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
|
||||
def step_post_upload_process(cls, document, querystring=None):
|
||||
furl_instance = furl(querystring)
|
||||
|
||||
@@ -27,6 +27,10 @@ from .links import (
|
||||
link_checkout_list
|
||||
)
|
||||
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 (
|
||||
permission_document_checkin, permission_document_checkin_override,
|
||||
permission_document_checkout, permission_document_checkout_detail_view
|
||||
@@ -54,29 +58,15 @@ class CheckoutsApp(MayanAppConfig):
|
||||
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(
|
||||
name='check_in',
|
||||
value=lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user)
|
||||
name='get_checkout_info', value=method_get_checkout_info
|
||||
)
|
||||
Document.add_to_class(
|
||||
name='checkout_info',
|
||||
value=lambda document: DocumentCheckout.objects.document_checkout_info(
|
||||
document
|
||||
)
|
||||
name='get_checkout_state', value=method_get_checkout_state
|
||||
)
|
||||
Document.add_to_class(
|
||||
name='checkout_state',
|
||||
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
|
||||
)
|
||||
name='is_checked_out', value=method_is_checked_out
|
||||
)
|
||||
|
||||
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):
|
||||
Document = apps.get_model(
|
||||
app_label='documents', model_name='Document'
|
||||
@@ -94,6 +73,27 @@ class DocumentCheckoutManager(models.Manager):
|
||||
|
||||
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):
|
||||
if self.model.objects.filter(document=document):
|
||||
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'),
|
||||
'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'),
|
||||
'attribute': encapsulate(
|
||||
lambda document: document.checkout_info().checkout_datetime
|
||||
lambda document: document.get_checkout_info().checkout_datetime
|
||||
)
|
||||
},
|
||||
{
|
||||
'name': _('Checkout expiration'),
|
||||
'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,
|
||||
}
|
||||
|
||||
if document.checkout_info().user != self.request.user:
|
||||
if document.get_checkout_info().user != self.request.user:
|
||||
context['title'] = _(
|
||||
'You didn\'t originally checked out this document. '
|
||||
'Forcefully check in the document: %s?'
|
||||
@@ -159,7 +159,7 @@ class DocumentCheckinView(ConfirmView):
|
||||
def view_action(self):
|
||||
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(
|
||||
permissions=permission_document_checkin,
|
||||
user=self.request.user, obj=document
|
||||
|
||||
@@ -226,13 +226,27 @@ class ModelAttribute(object):
|
||||
def __str__(self):
|
||||
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:
|
||||
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(
|
||||
self.name if show_name else self.label, self.description
|
||||
self.name if show_name else self.get_label(), self.get_description()
|
||||
)
|
||||
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):
|
||||
@@ -344,31 +358,6 @@ class Package(object):
|
||||
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):
|
||||
_registry = {}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class ErrorLogEntryManager(models.Manager):
|
||||
app_label='common', model_name='ErrorLogEntry'
|
||||
)
|
||||
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_METADATA_TYPE_LABEL = 'test metadata label'
|
||||
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_DESCRIPTION_EXPRESSION = '{{ document.description }}'
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from django.apps import apps
|
||||
from django.db.models.signals import post_save
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from kombu import Exchange, Queue
|
||||
@@ -16,14 +14,12 @@ from mayan.apps.common import (
|
||||
menu_tools
|
||||
)
|
||||
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.signals import post_version_upload
|
||||
from mayan.apps.documents.widgets import document_link
|
||||
from mayan.apps.navigation import SourceColumn
|
||||
from mayan.celery import app
|
||||
|
||||
from .events import event_parsing_document_version_submit
|
||||
from .handlers import (
|
||||
handler_index_document, handler_initialize_new_parsing_settings,
|
||||
handler_parse_document_version
|
||||
@@ -35,36 +31,20 @@ from .links import (
|
||||
link_document_type_parsing_settings, link_document_type_submit,
|
||||
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 (
|
||||
permission_content_view, permission_document_type_parsing_setup,
|
||||
permission_parse_document
|
||||
)
|
||||
from .signals import post_document_version_parsing
|
||||
from .utils import document_property_content, get_document_content
|
||||
|
||||
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):
|
||||
app_namespace = 'document_parsing'
|
||||
app_url = 'parsing'
|
||||
@@ -96,26 +76,24 @@ class DocumentParsingApp(MayanAppConfig):
|
||||
)
|
||||
|
||||
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(
|
||||
name='content', value=document_property_content
|
||||
name='get_content', value=method_get_document_content
|
||||
)
|
||||
DocumentVersion.add_to_class(
|
||||
name='content', value=get_document_content
|
||||
name='get_content', value=method_get_document_version_content
|
||||
)
|
||||
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(
|
||||
model=Document, name='content', description=_(
|
||||
'The parsed content of the document.'
|
||||
)
|
||||
)
|
||||
ModelAttribute(model=Document, name='get_content')
|
||||
|
||||
ModelField(
|
||||
Document, name='versions__pages__content__content'
|
||||
model=Document, name='versions__pages__content__content'
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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.document = self.upload_document()
|
||||
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_parse_document
|
||||
)
|
||||
from ..utils import get_document_content
|
||||
from ..utils import get_document_content_iterator
|
||||
|
||||
from .literals import TEST_DOCUMENT_CONTENT
|
||||
|
||||
@@ -89,7 +89,7 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase):
|
||||
|
||||
self.assert_download_response(
|
||||
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()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
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):
|
||||
@@ -142,5 +142,5 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
||||
response = self._request_document_type_submit_view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
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
|
||||
|
||||
|
||||
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(
|
||||
app_label='document_parsing', model_name='DocumentPageContent'
|
||||
)
|
||||
|
||||
for page in document.pages.all():
|
||||
for page in document_version.pages.all():
|
||||
try:
|
||||
page_content = page.content.content
|
||||
except DocumentPageContent.DoesNotExist:
|
||||
return
|
||||
else:
|
||||
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.http import HttpResponseRedirect
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@@ -20,7 +21,7 @@ from .permissions import (
|
||||
permission_content_view, permission_document_type_parsing_setup,
|
||||
permission_parse_document
|
||||
)
|
||||
from .utils import get_document_content
|
||||
from .utils import get_document_content_iterator
|
||||
|
||||
|
||||
class DocumentContentView(SingleObjectDetailView):
|
||||
@@ -50,7 +51,7 @@ class DocumentContentDownloadView(SingleObjectDownloadView):
|
||||
|
||||
def get_file(self):
|
||||
file_object = DocumentContentDownloadView.TextIteratorIO(
|
||||
iterator=get_document_content(document=self.get_object())
|
||||
iterator=get_document_content_iterator(document=self.get_object())
|
||||
)
|
||||
return DocumentContentDownloadView.VirtualFile(
|
||||
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.celery import app
|
||||
|
||||
from .classes import DocumentStateHelper, WorkflowAction
|
||||
from .classes import WorkflowAction
|
||||
from .handlers import (
|
||||
handler_index_document, handler_trigger_transition, launch_workflow
|
||||
)
|
||||
from .methods import method_get_workflow
|
||||
from .links import (
|
||||
link_document_workflow_instance_list, link_setup_workflow_create,
|
||||
link_setup_workflow_delete, link_setup_workflow_document_types,
|
||||
@@ -84,28 +85,14 @@ class DocumentStatesApp(MayanAppConfig):
|
||||
)
|
||||
|
||||
Document.add_to_class(
|
||||
name='workflow', value=DocumentStateHelper.constructor
|
||||
name='get_workflow', value=method_get_workflow
|
||||
)
|
||||
|
||||
ErrorLogEntry.objects.register(model=WorkflowStateAction)
|
||||
|
||||
WorkflowAction.initialize()
|
||||
|
||||
ModelAttribute(
|
||||
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'
|
||||
)
|
||||
)
|
||||
ModelAttribute(model=Document, name='get_workflow')
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(permission_workflow_view,)
|
||||
|
||||
@@ -7,22 +7,10 @@ from django.apps import apps
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from mayan.apps.common.classes import PropertyHelper
|
||||
|
||||
__all__ = ('WorkflowAction',)
|
||||
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):
|
||||
_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_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
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ from mayan.apps.acls import ModelPermission
|
||||
from mayan.apps.common import (
|
||||
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.documents.search import document_page_search, document_search
|
||||
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_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 (
|
||||
permission_document_type_file_metadata_setup,
|
||||
permission_file_metadata_submit, permission_file_metadata_view
|
||||
)
|
||||
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):
|
||||
@@ -78,14 +79,16 @@ class FileMetadataApp(MayanAppConfig):
|
||||
name='get_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(
|
||||
name='get_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(
|
||||
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(
|
||||
model=Document, permissions=(
|
||||
permission_file_metadata_submit, permission_file_metadata_view,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .events import event_file_metadata_document_version_submit
|
||||
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):
|
||||
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
|
||||
except document_driver.entries.model.DoesNotExist:
|
||||
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(
|
||||
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(
|
||||
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:
|
||||
|
||||
@@ -27,7 +27,6 @@ from mayan.apps.events.permissions import permission_events_view
|
||||
from mayan.celery import app
|
||||
from mayan.apps.navigation import SourceColumn
|
||||
|
||||
from .classes import DocumentMetadataHelper
|
||||
from .events import (
|
||||
event_document_metadata_added, event_document_metadata_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_edit, link_setup_metadata_type_list,
|
||||
)
|
||||
from .methods import method_get_metadata
|
||||
from .permissions import (
|
||||
permission_metadata_document_add, permission_metadata_document_edit,
|
||||
permission_metadata_document_remove, permission_metadata_document_view,
|
||||
permission_metadata_type_delete, permission_metadata_type_edit,
|
||||
permission_metadata_type_view
|
||||
)
|
||||
|
||||
from .queues import * # NOQA
|
||||
from .search import metadata_type_search # NOQA
|
||||
from .widgets import get_metadata_string
|
||||
@@ -90,23 +89,18 @@ class MetadataApp(MayanAppConfig):
|
||||
MetadataType = self.get_model('MetadataType')
|
||||
|
||||
Document.add_to_class(
|
||||
name='metadata_value_of',
|
||||
value=DocumentMetadataHelper.constructor
|
||||
name='get_metadata', value=method_get_metadata
|
||||
)
|
||||
|
||||
ModelAttribute(
|
||||
Document, 'metadata_value_of',
|
||||
description=_(
|
||||
'Return the value of a specific document metadata'
|
||||
),
|
||||
)
|
||||
ModelAttribute(model=Document, name='get_metadata')
|
||||
|
||||
ModelField(
|
||||
Document, 'metadata__metadata_type__name',
|
||||
label=_('Metadata type name')
|
||||
label=_('Metadata type name'), model=Document,
|
||||
name='metadata__metadata_type__name'
|
||||
)
|
||||
ModelField(
|
||||
Document, 'metadata__value', label=_('Metadata type value'),
|
||||
label=_('Metadata type value'), model=Document,
|
||||
name='metadata__value',
|
||||
)
|
||||
|
||||
ModelEventType.register(
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
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):
|
||||
_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 datetime import timedelta
|
||||
import logging
|
||||
|
||||
from kombu import Exchange, Queue
|
||||
|
||||
from django.apps import apps
|
||||
from django.db.models.signals import post_save
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.acls import ModelPermission
|
||||
@@ -16,14 +14,12 @@ from mayan.apps.common import (
|
||||
menu_tools
|
||||
)
|
||||
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.signals import post_version_upload
|
||||
from mayan.apps.documents.widgets import document_link
|
||||
from mayan.apps.navigation import SourceColumn
|
||||
from mayan.celery import app
|
||||
|
||||
from .events import event_ocr_document_version_submit
|
||||
from .handlers import (
|
||||
handler_index_document, handler_initialize_new_ocr_settings,
|
||||
handler_ocr_document_version,
|
||||
@@ -35,37 +31,20 @@ from .links import (
|
||||
link_document_type_ocr_settings, link_document_type_submit,
|
||||
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 (
|
||||
permission_document_type_ocr_setup, permission_ocr_document,
|
||||
permission_ocr_content_view
|
||||
)
|
||||
from .queues import * # NOQA
|
||||
from .signals import post_document_version_ocr
|
||||
from .utils import document_property_ocr_content
|
||||
|
||||
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):
|
||||
app_namespace = 'ocr'
|
||||
app_url = 'ocr'
|
||||
@@ -96,22 +75,24 @@ class OCRApp(MayanAppConfig):
|
||||
DocumentVersionOCRError = self.get_model('DocumentVersionOCRError')
|
||||
|
||||
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(
|
||||
name='ocr_content', value=document_property_ocr_content
|
||||
name='submit_for_ocr', value=method_document_ocr_submit
|
||||
)
|
||||
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(
|
||||
model=Document, name='ocr_content', description=_(
|
||||
'The OCR content of the document.'
|
||||
)
|
||||
)
|
||||
ModelAttribute(model=Document, name='get_ocr_content')
|
||||
|
||||
ModelField(
|
||||
Document, name='versions__pages__ocr_content__content'
|
||||
model=Document, name='versions__pages__ocr_content__content'
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
|
||||
@@ -82,7 +82,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
||||
)
|
||||
self._request_document_submit_view()
|
||||
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):
|
||||
@@ -95,7 +95,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
||||
|
||||
def test_multiple_document_submit_view_no_permission(self):
|
||||
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):
|
||||
self.grant_access(
|
||||
@@ -103,7 +103,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
||||
)
|
||||
self._request_multiple_document_submit_view()
|
||||
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):
|
||||
@@ -126,7 +126,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
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()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
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):
|
||||
@@ -179,5 +179,5 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
||||
response = self._request_document_type_submit_view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
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
|
||||
|
||||
|
||||
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(
|
||||
app_label='ocr', model_name='DocumentPageOCRContent'
|
||||
)
|
||||
|
||||
for page in document.pages.all():
|
||||
for page in document_version.pages.all():
|
||||
try:
|
||||
page_content = page.ocr_content.content
|
||||
except DocumentPageOCRContent.DoesNotExist:
|
||||
return
|
||||
else:
|
||||
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_document_type_ocr_setup
|
||||
)
|
||||
from .utils import get_document_ocr_content
|
||||
from .utils import get_document_content_iterator
|
||||
|
||||
|
||||
class DocumentOCRContentView(SingleObjectDetailView):
|
||||
@@ -49,7 +49,7 @@ class DocumentOCRDownloadView(SingleObjectDownloadView):
|
||||
|
||||
def get_file(self):
|
||||
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(
|
||||
file=file_object, name='{}-OCR'.format(self.get_object())
|
||||
|
||||
@@ -117,7 +117,7 @@ class APIDocumentTagListView(generics.ListCreateAPIView):
|
||||
obj=document
|
||||
)
|
||||
|
||||
return document.attached_tags().all()
|
||||
return document.get_tags().all()
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
@@ -171,7 +171,7 @@ class APIDocumentTagView(generics.RetrieveDestroyAPIView):
|
||||
return document
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_document().attached_tags().all()
|
||||
return self.get_document().get_tags().all()
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
|
||||
@@ -11,7 +11,7 @@ from mayan.apps.common import (
|
||||
MayanAppConfig, menu_facet, menu_list_facet, menu_object, menu_main,
|
||||
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.events import ModelEventType
|
||||
from mayan.apps.events.links import (
|
||||
@@ -32,6 +32,7 @@ from .links import (
|
||||
link_tag_multiple_delete, link_tag_tagged_item_list
|
||||
)
|
||||
from .menus import menu_tags
|
||||
from .methods import method_get_tags
|
||||
from .permissions import (
|
||||
permission_tag_attach, permission_tag_delete, permission_tag_edit,
|
||||
permission_tag_remove, permission_tag_view
|
||||
@@ -65,10 +66,9 @@ class TagsApp(MayanAppConfig):
|
||||
DocumentTag = self.get_model('DocumentTag')
|
||||
Tag = self.get_model('Tag')
|
||||
|
||||
Document.add_to_class(
|
||||
name='attached_tags',
|
||||
value=lambda document: DocumentTag.objects.filter(documents=document)
|
||||
)
|
||||
Document.add_to_class(name='get_tags', value=method_get_tags)
|
||||
|
||||
ModelAttribute(model=Document, name='get_tags')
|
||||
|
||||
ModelEventType.register(
|
||||
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 (
|
||||
event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove
|
||||
)
|
||||
from .managers import DocumentTagManager
|
||||
from .widgets import widget_single_tag
|
||||
|
||||
|
||||
@@ -95,6 +96,8 @@ class Tag(models.Model):
|
||||
|
||||
|
||||
class DocumentTag(Tag):
|
||||
objects = DocumentTagManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = _('Document tag')
|
||||
|
||||
@@ -8,7 +8,7 @@ TEST_TAG_COLOR_EDITED = '#221100'
|
||||
TEST_TAG_INDEX_HAS_TAG = 'HAS_TAG'
|
||||
TEST_TAG_INDEX_NO_TAG = 'NO_TAG'
|
||||
TEST_TAG_INDEX_NODE_TEMPLATE = '''
|
||||
{{% for tag in document.tags.all() %}}
|
||||
{{% for tag in document.get_tags().all() %}}
|
||||
{{% if tag.label == "{}" %}}
|
||||
{}
|
||||
{{% else %}}
|
||||
|
||||
@@ -89,7 +89,7 @@ class TagAttachActionView(MultipleObjectFormActionView):
|
||||
return super(TagAttachActionView, self).get_post_action_redirect()
|
||||
|
||||
def object_action(self, form, instance):
|
||||
attached_tags = instance.attached_tags()
|
||||
attached_tags = instance.get_tags()
|
||||
|
||||
for tag in form.cleaned_data['tags']:
|
||||
AccessControlList.objects.check_access(
|
||||
@@ -269,7 +269,7 @@ class DocumentTagListView(TagListView):
|
||||
return context
|
||||
|
||||
def get_tag_queryset(self):
|
||||
return self.document.attached_tags().all()
|
||||
return self.document.get_tags().all()
|
||||
|
||||
|
||||
class TagRemoveActionView(MultipleObjectFormActionView):
|
||||
@@ -331,7 +331,7 @@ class TagRemoveActionView(MultipleObjectFormActionView):
|
||||
return super(TagRemoveActionView, self).get_post_action_redirect()
|
||||
|
||||
def object_action(self, form, instance):
|
||||
attached_tags = instance.attached_tags()
|
||||
attached_tags = instance.get_tags()
|
||||
|
||||
for tag in form.cleaned_data['tags']:
|
||||
AccessControlList.objects.check_access(
|
||||
|
||||
Reference in New Issue
Block a user