Add control codes app proof of concept
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
3
mayan/apps/control_codes/__init__.py
Normal file
3
mayan/apps/control_codes/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
default_app_config = 'mayan.apps.control_codes.apps.ControlCodesApp'
|
||||||
60
mayan/apps/control_codes/apps.py
Normal file
60
mayan/apps/control_codes/apps.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.acls.classes import ModelPermission
|
||||||
|
from mayan.apps.acls.links import link_acl_list
|
||||||
|
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
|
||||||
|
from mayan.apps.documents.signals import post_version_upload
|
||||||
|
from mayan.apps.common.apps import MayanAppConfig
|
||||||
|
from mayan.apps.common.html_widgets import TwoStateWidget
|
||||||
|
from mayan.apps.common.menus import (
|
||||||
|
menu_facet, menu_list_facet, menu_object, menu_secondary, menu_setup
|
||||||
|
)
|
||||||
|
from mayan.apps.events.classes import ModelEventType
|
||||||
|
from mayan.apps.events.links import (
|
||||||
|
link_events_for_object, link_object_event_types_user_subcriptions_list
|
||||||
|
)
|
||||||
|
from mayan.apps.navigation.classes import SourceColumn
|
||||||
|
|
||||||
|
from .handlers import handler_process_document_version
|
||||||
|
from .methods import method_document_submit, method_document_version_submit
|
||||||
|
|
||||||
|
|
||||||
|
class ControlCodesApp(MayanAppConfig):
|
||||||
|
app_namespace = 'control_codes'
|
||||||
|
app_url = 'control_codes'
|
||||||
|
has_rest_api = False
|
||||||
|
has_tests = False
|
||||||
|
name = 'mayan.apps.control_codes'
|
||||||
|
verbose_name = _('Control codes')
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
super(ControlCodesApp, self).ready()
|
||||||
|
from actstream import registry
|
||||||
|
|
||||||
|
Document = apps.get_model(
|
||||||
|
app_label='documents', model_name='Document'
|
||||||
|
)
|
||||||
|
DocumentType = apps.get_model(
|
||||||
|
app_label='documents', model_name='DocumentType'
|
||||||
|
)
|
||||||
|
DocumentVersion = apps.get_model(
|
||||||
|
app_label='documents', model_name='DocumentVersion'
|
||||||
|
)
|
||||||
|
|
||||||
|
Document.add_to_class(
|
||||||
|
name='submit_for_control_codes_processing',
|
||||||
|
value=method_document_submit
|
||||||
|
)
|
||||||
|
DocumentVersion.add_to_class(
|
||||||
|
name='submit_for_control_codes_processing',
|
||||||
|
value=method_document_version_submit
|
||||||
|
)
|
||||||
|
|
||||||
|
post_version_upload.connect(
|
||||||
|
dispatch_uid='control_codes_handler_process_document_version',
|
||||||
|
receiver=handler_process_document_version, sender=DocumentVersion
|
||||||
|
)
|
||||||
101
mayan/apps/control_codes/classes.py
Normal file
101
mayan/apps/control_codes/classes.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from pyzbar.pyzbar import decode
|
||||||
|
import qrcode
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
from mayan.apps.common.serialization import yaml_dump, yaml_load
|
||||||
|
from mayan.apps.documents.literals import DOCUMENT_IMAGE_TASK_TIMEOUT
|
||||||
|
from mayan.apps.documents.tasks import task_generate_document_page_image
|
||||||
|
|
||||||
|
CONTROL_CODE_MAGIC_NUMBER = 'MCTRL'
|
||||||
|
CONTROL_CODE_SEPARATOR = ':'
|
||||||
|
CONTROL_CODE_VERSION = '1'
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ControlCode(object):
|
||||||
|
_registry = {}
|
||||||
|
arguments = ()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, name):
|
||||||
|
return cls._registry[name]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def process_document_version(cls, document_version):
|
||||||
|
logger.info(
|
||||||
|
'Starting processing document version: %s', document_version
|
||||||
|
)
|
||||||
|
|
||||||
|
for document_page in document_version.pages.all():
|
||||||
|
task = task_generate_document_page_image.apply_async(
|
||||||
|
kwargs=dict(
|
||||||
|
document_page_id=document_page.pk
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_filename = task.get(
|
||||||
|
timeout=DOCUMENT_IMAGE_TASK_TIMEOUT, disable_sync_subtasks=False
|
||||||
|
)
|
||||||
|
|
||||||
|
with document_page.cache_partition.get_file(filename=cache_filename).open() as file_object:
|
||||||
|
image = Image.open(file_object)
|
||||||
|
for code in decode(image):
|
||||||
|
logger.debug('code found: %s', code)
|
||||||
|
parts = code.data.split(CONTROL_CODE_SEPARATOR)
|
||||||
|
|
||||||
|
if parts[0] == CONTROL_CODE_MAGIC_NUMBER:
|
||||||
|
# Version
|
||||||
|
if parts[1] == '1':
|
||||||
|
try:
|
||||||
|
control_code_class = ControlCode.get(name=parts[2])
|
||||||
|
except KeyError:
|
||||||
|
# Unknown control code name
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
document_page.enabled = False
|
||||||
|
document_page.save()
|
||||||
|
arguments = CONTROL_CODE_SEPARATOR.join(parts[3:])
|
||||||
|
control_code = control_code_class(
|
||||||
|
**yaml_load(arguments)
|
||||||
|
)
|
||||||
|
control_code.execute()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls, control_code):
|
||||||
|
cls._registry[control_code.name] = control_code
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.kwargs = {}
|
||||||
|
for argument_name in self.arguments:
|
||||||
|
setattr(self, argument_name, kwargs.get(argument_name))
|
||||||
|
self.kwargs[argument_name] = kwargs.get(argument_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def image(self):
|
||||||
|
return qrcode.make(self.get_qrcode_string())
|
||||||
|
|
||||||
|
def get_qrcode_string(self):
|
||||||
|
result = []
|
||||||
|
result.append(CONTROL_CODE_MAGIC_NUMBER)
|
||||||
|
result.append(CONTROL_CODE_VERSION)
|
||||||
|
result.append(self.name)
|
||||||
|
result.append(
|
||||||
|
yaml_dump(
|
||||||
|
data=self.kwargs, allow_unicode=True, default_flow_style=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return CONTROL_CODE_SEPARATOR.join(result)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'Your %s class has not defined the required '
|
||||||
|
'execue() method.' % self.__class__.__name__
|
||||||
|
)
|
||||||
21
mayan/apps/control_codes/handlers.py
Normal file
21
mayan/apps/control_codes/handlers.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
#from .settings import setting_auto_process
|
||||||
|
|
||||||
|
|
||||||
|
def handler_initialize_new_document_type_settings(sender, instance, **kwargs):
|
||||||
|
DocumentTypeSettings = apps.get_model(
|
||||||
|
app_label='file_metadata', model_name='DocumentTypeSettings'
|
||||||
|
)
|
||||||
|
|
||||||
|
if kwargs['created']:
|
||||||
|
DocumentTypeSettings.objects.create(
|
||||||
|
auto_process=setting_auto_process.value, document_type=instance
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handler_process_document_version(sender, instance, **kwargs):
|
||||||
|
#if instance.document.document_type.file_metadata_settings.auto_process:
|
||||||
|
instance.submit_for_control_codes_processing()
|
||||||
6
mayan/apps/control_codes/icons.py
Normal file
6
mayan/apps/control_codes/icons.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from mayan.apps.appearance.classes import Icon
|
||||||
|
from mayan.apps.documents.icons import icon_document_type
|
||||||
|
|
||||||
|
icon_control_code = Icon(driver_name='fontawesome', symbol='gear')
|
||||||
17
mayan/apps/control_codes/links.py
Normal file
17
mayan/apps/control_codes/links.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.documents.permissions import permission_document_type_edit
|
||||||
|
from mayan.apps.navigation.classes import Link
|
||||||
|
|
||||||
|
from .icons import icon_control_code
|
||||||
|
from .permissions import (
|
||||||
|
permission_web_link_create, permission_web_link_delete,
|
||||||
|
permission_web_link_edit, permission_web_link_instance_view,
|
||||||
|
)
|
||||||
|
|
||||||
|
link_control_code_tools = Link(
|
||||||
|
icon_class=icon_control_code, text=_('Control codes'),
|
||||||
|
view='control_codes:control_code_create'
|
||||||
|
)
|
||||||
20
mayan/apps/control_codes/methods.py
Normal file
20
mayan/apps/control_codes/methods.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .tasks import task_process_document_version
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_submit(self):
|
||||||
|
latest_version = self.latest_version
|
||||||
|
# Don't error out if document has no version
|
||||||
|
if latest_version:
|
||||||
|
latest_version.submit_for_control_codes_processing()
|
||||||
|
|
||||||
|
|
||||||
|
def method_document_version_submit(self):
|
||||||
|
task_process_document_version.apply_async(
|
||||||
|
kwargs={
|
||||||
|
'document_version_id': self.pk,
|
||||||
|
}
|
||||||
|
)
|
||||||
14
mayan/apps/control_codes/queues.py
Normal file
14
mayan/apps/control_codes/queues.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.task_manager.classes import CeleryQueue
|
||||||
|
from mayan.apps.task_manager.workers import worker_medium
|
||||||
|
|
||||||
|
queue = CeleryQueue(
|
||||||
|
label=_('Control codes'), name='control_codes', worker=worker_medium
|
||||||
|
)
|
||||||
|
queue.add_task_type(
|
||||||
|
label=_('Process document version'),
|
||||||
|
dotted_path='mayan.apps.control_codes.tasks.task_process_document_version'
|
||||||
|
)
|
||||||
24
mayan/apps/control_codes/tasks.py
Normal file
24
mayan/apps/control_codes/tasks.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
from mayan.celery import app
|
||||||
|
|
||||||
|
from .classes import ControlCode
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(ignore_result=True)
|
||||||
|
def task_process_document_version(document_version_id):
|
||||||
|
DocumentVersion = apps.get_model(
|
||||||
|
app_label='documents', model_name='DocumentVersion'
|
||||||
|
)
|
||||||
|
|
||||||
|
document_version = DocumentVersion.objects.get(pk=document_version_id)
|
||||||
|
|
||||||
|
ControlCode.process_document_version(
|
||||||
|
document_version=document_version
|
||||||
|
)
|
||||||
0
mayan/apps/control_codes/tests/__init__.py
Normal file
0
mayan/apps/control_codes/tests/__init__.py
Normal file
2
mayan/apps/control_codes/tests/literals.py
Normal file
2
mayan/apps/control_codes/tests/literals.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
5
mayan/apps/control_codes/tests/mixins.py
Normal file
5
mayan/apps/control_codes/tests/mixins.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
38
mayan/apps/control_codes/tests/test_models.py
Normal file
38
mayan/apps/control_codes/tests/test_models.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.test import override_settings
|
||||||
|
|
||||||
|
from mayan.apps.common.tests.base import BaseTestCase
|
||||||
|
from mayan.apps.documents.tests.base import GenericDocumentTestCase
|
||||||
|
from mayan.apps.documents.tests.mixins import DocumentTestMixin
|
||||||
|
|
||||||
|
from ..classes import ControlCode
|
||||||
|
|
||||||
|
TEST_CONTROL_CODE_DOCUMENT_PATH = '/tmp/test_control_code.png'
|
||||||
|
|
||||||
|
|
||||||
|
class ControlCodeTest(ControlCode):
|
||||||
|
arguments = ('argument_1',)
|
||||||
|
label = 'Test'
|
||||||
|
name = 'test'
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
ControlCode.register(control_code=ControlCodeTest)
|
||||||
|
|
||||||
|
|
||||||
|
class ControlCodeTestCase(GenericDocumentTestCase):
|
||||||
|
auto_upload_document = False
|
||||||
|
test_document_path = TEST_CONTROL_CODE_DOCUMENT_PATH
|
||||||
|
|
||||||
|
def test_control_code_detection(self):
|
||||||
|
|
||||||
|
with open(TEST_CONTROL_CODE_DOCUMENT_PATH, mode='wb') as file_object:
|
||||||
|
control_code = ControlCodeTest(argument_1='test argument value')
|
||||||
|
control_code.image.save(file_object)
|
||||||
|
|
||||||
|
self.upload_document()
|
||||||
|
|
||||||
|
print self.test_document.pages.count()
|
||||||
5
mayan/apps/control_codes/urls.py
Normal file
5
mayan/apps/control_codes/urls.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
urlpatterns = []
|
||||||
@@ -89,6 +89,7 @@ INSTALLED_APPS = (
|
|||||||
'mayan.apps.authentication',
|
'mayan.apps.authentication',
|
||||||
'mayan.apps.autoadmin',
|
'mayan.apps.autoadmin',
|
||||||
'mayan.apps.common',
|
'mayan.apps.common',
|
||||||
|
'mayan.apps.control_codes',
|
||||||
'mayan.apps.converter',
|
'mayan.apps.converter',
|
||||||
'mayan.apps.dashboards',
|
'mayan.apps.dashboards',
|
||||||
'mayan.apps.dependencies',
|
'mayan.apps.dependencies',
|
||||||
|
|||||||
Reference in New Issue
Block a user