From 0e86f2ad8a858b4c8710b8a3627b116f41f82ca5 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sat, 15 Dec 2018 04:49:40 -0400 Subject: [PATCH] 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 --- HISTORY.rst | 12 +++- mayan/apps/acls/classes.py | 42 +++++++------- mayan/apps/cabinets/apps.py | 4 +- mayan/apps/cabinets/methods.py | 13 +++++ mayan/apps/cabinets/wizard_steps.py | 16 +++--- mayan/apps/checkouts/apps.py | 26 +++------ mayan/apps/checkouts/managers.py | 42 +++++++------- mayan/apps/checkouts/methods.py | 33 +++++++++++ mayan/apps/checkouts/views.py | 10 ++-- mayan/apps/common/classes.py | 45 ++++++--------- mayan/apps/common/managers.py | 2 +- .../apps/document_indexing/tests/literals.py | 2 +- mayan/apps/document_parsing/apps.py | 48 +++++----------- mayan/apps/document_parsing/methods.py | 56 +++++++++++++++++++ mayan/apps/document_parsing/tests/literals.py | 2 +- .../document_parsing/tests/test_models.py | 2 +- .../apps/document_parsing/tests/test_views.py | 8 +-- mayan/apps/document_parsing/utils.py | 18 +++--- mayan/apps/document_parsing/views.py | 5 +- mayan/apps/document_states/apps.py | 21 ++----- mayan/apps/document_states/classes.py | 12 ---- mayan/apps/document_states/methods.py | 15 +++++ mayan/apps/document_states/tests/literals.py | 2 +- mayan/apps/file_metadata/apps.py | 30 +++++++--- .../file_metadata/{utils.py => methods.py} | 15 +++++ mayan/apps/file_metadata/models.py | 6 +- mayan/apps/metadata/apps.py | 20 +++---- mayan/apps/metadata/classes.py | 12 ---- mayan/apps/metadata/methods.py | 15 +++++ mayan/apps/ocr/apps.py | 49 +++++----------- mayan/apps/ocr/methods.py | 51 +++++++++++++++++ mayan/apps/ocr/tests/literals.py | 2 +- mayan/apps/ocr/tests/test_views.py | 12 ++-- mayan/apps/ocr/utils.py | 18 +++--- mayan/apps/ocr/views.py | 4 +- mayan/apps/tags/api_views.py | 4 +- mayan/apps/tags/apps.py | 10 ++-- mayan/apps/tags/managers.py | 8 +++ mayan/apps/tags/methods.py | 13 +++++ mayan/apps/tags/models.py | 3 + mayan/apps/tags/tests/literals.py | 2 +- mayan/apps/tags/views.py | 6 +- 42 files changed, 434 insertions(+), 282 deletions(-) create mode 100644 mayan/apps/cabinets/methods.py create mode 100644 mayan/apps/checkouts/methods.py create mode 100644 mayan/apps/document_parsing/methods.py create mode 100644 mayan/apps/document_states/methods.py rename mayan/apps/file_metadata/{utils.py => methods.py} (77%) create mode 100644 mayan/apps/metadata/methods.py create mode 100644 mayan/apps/ocr/methods.py create mode 100644 mayan/apps/tags/managers.py create mode 100644 mayan/apps/tags/methods.py diff --git a/HISTORY.rst b/HISTORY.rst index bc5096a737..cc8d55c862 100644 --- a/HISTORY.rst +++ b/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) ================== diff --git a/mayan/apps/acls/classes.py b/mayan/apps/acls/classes.py index 21f66e95a0..94ee4c6f69 100644 --- a/mayan/apps/acls/classes.py +++ b/mayan/apps/acls/classes.py @@ -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 diff --git a/mayan/apps/cabinets/apps.py b/mayan/apps/cabinets/apps.py index 65000ac672..8625f49dc8 100644 --- a/mayan/apps/cabinets/apps.py +++ b/mayan/apps/cabinets/apps.py @@ -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( diff --git a/mayan/apps/cabinets/methods.py b/mayan/apps/cabinets/methods.py new file mode 100644 index 0000000000..25344f9d22 --- /dev/null +++ b/mayan/apps/cabinets/methods.py @@ -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' + ) diff --git a/mayan/apps/cabinets/wizard_steps.py b/mayan/apps/cabinets/wizard_steps.py index 7c37006e70..b211661ff1 100644 --- a/mayan/apps/cabinets/wizard_steps.py +++ b/mayan/apps/cabinets/wizard_steps.py @@ -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) diff --git a/mayan/apps/checkouts/apps.py b/mayan/apps/checkouts/apps.py index 5ed7ac6968..1a44e449f8 100644 --- a/mayan/apps/checkouts/apps.py +++ b/mayan/apps/checkouts/apps.py @@ -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( diff --git a/mayan/apps/checkouts/managers.py b/mayan/apps/checkouts/managers.py index f397f4ea42..c7aa2cc922 100644 --- a/mayan/apps/checkouts/managers.py +++ b/mayan/apps/checkouts/managers.py @@ -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 diff --git a/mayan/apps/checkouts/methods.py b/mayan/apps/checkouts/methods.py new file mode 100644 index 0000000000..b22755b79f --- /dev/null +++ b/mayan/apps/checkouts/methods.py @@ -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) diff --git a/mayan/apps/checkouts/views.py b/mayan/apps/checkouts/views.py index 91a0febaee..03440268bc 100644 --- a/mayan/apps/checkouts/views.py +++ b/mayan/apps/checkouts/views.py @@ -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 diff --git a/mayan/apps/common/classes.py b/mayan/apps/common/classes.py index 340653c6e2..380e8d9a18 100644 --- a/mayan/apps/common/classes.py +++ b/mayan/apps/common/classes.py @@ -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 = {} diff --git a/mayan/apps/common/managers.py b/mayan/apps/common/managers.py index 942b7c7e7d..1bcfb9d30b 100644 --- a/mayan/apps/common/managers.py +++ b/mayan/apps/common/managers.py @@ -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) ) diff --git a/mayan/apps/document_indexing/tests/literals.py b/mayan/apps/document_indexing/tests/literals.py index 557e108947..d1269ec272 100644 --- a/mayan/apps/document_indexing/tests/literals.py +++ b/mayan/apps/document_indexing/tests/literals.py @@ -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 }}' diff --git a/mayan/apps/document_parsing/apps.py b/mayan/apps/document_parsing/apps.py index cfc52ee054..037b785065 100644 --- a/mayan/apps/document_parsing/apps.py +++ b/mayan/apps/document_parsing/apps.py @@ -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( diff --git a/mayan/apps/document_parsing/methods.py b/mayan/apps/document_parsing/methods.py new file mode 100644 index 0000000000..5c07b8d855 --- /dev/null +++ b/mayan/apps/document_parsing/methods.py @@ -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.' +) diff --git a/mayan/apps/document_parsing/tests/literals.py b/mayan/apps/document_parsing/tests/literals.py index e054d56c13..36b7ef5a17 100644 --- a/mayan/apps/document_parsing/tests/literals.py +++ b/mayan/apps/document_parsing/tests/literals.py @@ -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 %}' diff --git a/mayan/apps/document_parsing/tests/test_models.py b/mayan/apps/document_parsing/tests/test_models.py index f24eefeedd..b5856b00c5 100644 --- a/mayan/apps/document_parsing/tests/test_models.py +++ b/mayan/apps/document_parsing/tests/test_models.py @@ -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() ) diff --git a/mayan/apps/document_parsing/tests/test_views.py b/mayan/apps/document_parsing/tests/test_views.py index 1ab8a393b3..a7bcb0a1c5 100644 --- a/mayan/apps/document_parsing/tests/test_views.py +++ b/mayan/apps/document_parsing/tests/test_views.py @@ -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() ) diff --git a/mayan/apps/document_parsing/utils.py b/mayan/apps/document_parsing/utils.py index ac98780fee..fe9098dcaa 100644 --- a/mayan/apps/document_parsing/utils.py +++ b/mayan/apps/document_parsing/utils.py @@ -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)) diff --git a/mayan/apps/document_parsing/views.py b/mayan/apps/document_parsing/views.py index 4064efab6a..dd86530c0f 100644 --- a/mayan/apps/document_parsing/views.py +++ b/mayan/apps/document_parsing/views.py @@ -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()) diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index 933a24ff89..1a243c1c0b 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -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,) diff --git a/mayan/apps/document_states/classes.py b/mayan/apps/document_states/classes.py index 498c19dac2..d49c6ac514 100644 --- a/mayan/apps/document_states/classes.py +++ b/mayan/apps/document_states/classes.py @@ -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 = {} diff --git a/mayan/apps/document_states/methods.py b/mayan/apps/document_states/methods.py new file mode 100644 index 0000000000..b64fe531bb --- /dev/null +++ b/mayan/apps/document_states/methods.py @@ -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.' +) diff --git a/mayan/apps/document_states/tests/literals.py b/mayan/apps/document_states/tests/literals.py index 53f1735aa6..3803c27b48 100644 --- a/mayan/apps/document_states/tests/literals.py +++ b/mayan/apps/document_states/tests/literals.py @@ -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 ) diff --git a/mayan/apps/file_metadata/apps.py b/mayan/apps/file_metadata/apps.py index 59d68b4ea4..29ef506768 100644 --- a/mayan/apps/file_metadata/apps.py +++ b/mayan/apps/file_metadata/apps.py @@ -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, diff --git a/mayan/apps/file_metadata/utils.py b/mayan/apps/file_metadata/methods.py similarity index 77% rename from mayan/apps/file_metadata/utils.py rename to mayan/apps/file_metadata/methods.py index a47d126ced..30d7d277a7 100644 --- a/mayan/apps/file_metadata/utils.py +++ b/mayan/apps/file_metadata/methods.py @@ -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.' +) diff --git a/mayan/apps/file_metadata/models.py b/mayan/apps/file_metadata/models.py index 77c237d5c1..5d07b194df 100644 --- a/mayan/apps/file_metadata/models.py +++ b/mayan/apps/file_metadata/models.py @@ -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: diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py index 43ec115770..2b9dbaf45e 100644 --- a/mayan/apps/metadata/apps.py +++ b/mayan/apps/metadata/apps.py @@ -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( diff --git a/mayan/apps/metadata/classes.py b/mayan/apps/metadata/classes.py index bbd15b89b5..215cd9eb34 100644 --- a/mayan/apps/metadata/classes.py +++ b/mayan/apps/metadata/classes.py @@ -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 = [] diff --git a/mayan/apps/metadata/methods.py b/mayan/apps/metadata/methods.py new file mode 100644 index 0000000000..d23e74846c --- /dev/null +++ b/mayan/apps/metadata/methods.py @@ -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.' +) diff --git a/mayan/apps/ocr/apps.py b/mayan/apps/ocr/apps.py index ce8eaa2cd9..df13d05c58 100644 --- a/mayan/apps/ocr/apps.py +++ b/mayan/apps/ocr/apps.py @@ -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( diff --git a/mayan/apps/ocr/methods.py b/mayan/apps/ocr/methods.py new file mode 100644 index 0000000000..a683a5780d --- /dev/null +++ b/mayan/apps/ocr/methods.py @@ -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) + ) diff --git a/mayan/apps/ocr/tests/literals.py b/mayan/apps/ocr/tests/literals.py index 155288f064..8fae0ef951 100644 --- a/mayan/apps/ocr/tests/literals.py +++ b/mayan/apps/ocr/tests/literals.py @@ -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' diff --git a/mayan/apps/ocr/tests/test_views.py b/mayan/apps/ocr/tests/test_views.py index f1b63da923..babc27ddff 100644 --- a/mayan/apps/ocr/tests/test_views.py +++ b/mayan/apps/ocr/tests/test_views.py @@ -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() ) diff --git a/mayan/apps/ocr/utils.py b/mayan/apps/ocr/utils.py index ab40b0d040..a064ffac0b 100644 --- a/mayan/apps/ocr/utils.py +++ b/mayan/apps/ocr/utils.py @@ -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)) diff --git a/mayan/apps/ocr/views.py b/mayan/apps/ocr/views.py index c55b31d79a..04a19db664 100644 --- a/mayan/apps/ocr/views.py +++ b/mayan/apps/ocr/views.py @@ -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()) diff --git a/mayan/apps/tags/api_views.py b/mayan/apps/tags/api_views.py index d62a6e50c2..8cf75a8761 100644 --- a/mayan/apps/tags/api_views.py +++ b/mayan/apps/tags/api_views.py @@ -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: diff --git a/mayan/apps/tags/apps.py b/mayan/apps/tags/apps.py index 0992a985ee..43f0e53c32 100644 --- a/mayan/apps/tags/apps.py +++ b/mayan/apps/tags/apps.py @@ -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=( diff --git a/mayan/apps/tags/managers.py b/mayan/apps/tags/managers.py new file mode 100644 index 0000000000..006a7c3e25 --- /dev/null +++ b/mayan/apps/tags/managers.py @@ -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) diff --git a/mayan/apps/tags/methods.py b/mayan/apps/tags/methods.py new file mode 100644 index 0000000000..83761fc92f --- /dev/null +++ b/mayan/apps/tags/methods.py @@ -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()') diff --git a/mayan/apps/tags/models.py b/mayan/apps/tags/models.py index dc04737070..1e734f4477 100644 --- a/mayan/apps/tags/models.py +++ b/mayan/apps/tags/models.py @@ -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') diff --git a/mayan/apps/tags/tests/literals.py b/mayan/apps/tags/tests/literals.py index 0674c1139a..6949b2d9dd 100644 --- a/mayan/apps/tags/tests/literals.py +++ b/mayan/apps/tags/tests/literals.py @@ -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 %}} diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index b14d47ab4a..433eae91ac 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -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(