Add control code preview generation
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
23
mayan/apps/control_codes/admin.py
Normal file
23
mayan/apps/control_codes/admin.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import ControlSheet, ControlSheetCode
|
||||
|
||||
|
||||
class ControlSheetCodeInline(admin.StackedInline):
|
||||
allow_add = True
|
||||
classes = ('collapse-open',)
|
||||
extra = 1
|
||||
model = ControlSheetCode
|
||||
|
||||
|
||||
@admin.register(ControlSheet)
|
||||
class ControlSheetAdmin(admin.ModelAdmin):
|
||||
inlines = (ControlSheetCodeInline,)
|
||||
list_display = ('label', 'get_codes_count')
|
||||
|
||||
def get_codes_count(self, instance):
|
||||
return instance.codes.count()
|
||||
get_codes_count.short_description = _('Codes')
|
||||
231
mayan/apps/control_codes/api_views.py
Normal file
231
mayan/apps/control_codes/api_views.py
Normal file
@@ -0,0 +1,231 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.decorators.cache import cache_control, patch_cache_control
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from mayan.apps.acls.models import AccessControlList
|
||||
from mayan.apps.documents.models import Document, DocumentType
|
||||
from mayan.apps.documents.permissions import permission_document_type_view
|
||||
from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter
|
||||
from mayan.apps.rest_api.permissions import MayanPermission
|
||||
|
||||
from .literals import CONTROL_SHEET_CODE_IMAGE_TASK_TIMEOUT
|
||||
from .models import ControlSheet
|
||||
#from .permissions import (
|
||||
# permission_workflow_create, permission_workflow_delete,
|
||||
# permission_workflow_edit, permission_workflow_view
|
||||
#)
|
||||
from .serializers import (
|
||||
ControlSheetSerializer, ControlSheetCodeSerializer
|
||||
)
|
||||
|
||||
from .settings import settings_control_sheet_code_image_cache_time
|
||||
from .tasks import task_generate_control_sheet_code_image
|
||||
|
||||
|
||||
class APIControlSheetListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
get: Returns a list of all the control sheets.
|
||||
post: Create a new control sheet.
|
||||
"""
|
||||
filter_backends = (MayanObjectPermissionsFilter,)
|
||||
#mayan_object_permissions = {'GET': (permission_control_sheet_view,)}
|
||||
#mayan_view_permissions = {'POST': (permission_control_sheet_create,)}
|
||||
permission_classes = (MayanPermission,)
|
||||
queryset = ControlSheet.objects.all()
|
||||
serializer_class = ControlSheetSerializer
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
return None
|
||||
|
||||
return super(APIControlSheetListView, self).get_serializer(
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
#def get_serializer_class(self):
|
||||
# if self.request.method == 'GET':
|
||||
# return ControlSheetSerializer
|
||||
# else:
|
||||
# return WritableControlSheetSerializer
|
||||
|
||||
|
||||
class APIControlSheetView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
delete: Delete the selected control sheet.
|
||||
get: Return the details of the selected control sheet.
|
||||
patch: Edit the selected control sheet.
|
||||
put: Edit the selected control sheet.
|
||||
"""
|
||||
filter_backends = (MayanObjectPermissionsFilter,)
|
||||
#mayan_object_permissions = {
|
||||
# 'DELETE': (permission_control sheet_delete,),
|
||||
# 'GET': (permission_control sheet_view,),
|
||||
# 'PATCH': (permission_control sheet_edit,),
|
||||
# 'PUT': (permission_control sheet_edit,)
|
||||
#}
|
||||
lookup_url_kwarg = 'control_sheet_id'
|
||||
queryset = ControlSheet.objects.all()
|
||||
serializer_class = ControlSheetSerializer
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
return None
|
||||
|
||||
return super(APIControlSheetView, self).get_serializer(
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
#def get_serializer_class(self):
|
||||
# if self.request.method == 'GET':
|
||||
# return ControlSheetSerializer
|
||||
# else:
|
||||
# return WritableControlSheetSerializer
|
||||
|
||||
|
||||
|
||||
class APIControlSheetCodeListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
get: Returns a list of all the control sheet codes.
|
||||
post: Create a new control sheet code.
|
||||
"""
|
||||
serializer_class = ControlSheetCodeSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_control_sheet().codes.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""
|
||||
Extra context provided to the serializer class.
|
||||
"""
|
||||
context = super(APIControlSheetCodeListView, self).get_serializer_context()
|
||||
if self.kwargs:
|
||||
context.update(
|
||||
{
|
||||
'control_sheet': self.get_control_sheet(),
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
def get_control_sheet(self):
|
||||
#if self.request.method == 'GET':
|
||||
# permission_required = permission_control_sheet_view
|
||||
#else:
|
||||
# permission_required = permission_control_sheet_edit
|
||||
|
||||
control_sheet = get_object_or_404(
|
||||
klass=ControlSheet, pk=self.kwargs['control_sheet_id']
|
||||
)
|
||||
|
||||
#AccessControlList.objects.check_access(
|
||||
# obj=control_sheet, permissions=(permission_required,),
|
||||
# user=self.request.user
|
||||
#)
|
||||
|
||||
return control_sheet
|
||||
|
||||
|
||||
class APIControlSheetCodeView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
delete: Delete the selected control sheet code.
|
||||
get: Return the details of the selected control sheet code.
|
||||
patch: Edit the selected control sheet code.
|
||||
put: Edit the selected control sheet code.
|
||||
"""
|
||||
lookup_url_kwarg = 'control_sheet_code_id'
|
||||
serializer_class = ControlSheetCodeSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_control_sheet().codes.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""
|
||||
Extra context provided to the serializer class.
|
||||
"""
|
||||
context = super(APIControlSheetCodeView, self).get_serializer_context()
|
||||
if self.kwargs:
|
||||
context.update(
|
||||
{
|
||||
'control_sheet': self.get_control_sheet(),
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
def get_control_sheet(self):
|
||||
#if self.request.method == 'GET':
|
||||
# permission_required = permission_control_sheet_view
|
||||
#else:
|
||||
# permission_required = permission_control_sheet_edit
|
||||
|
||||
control_sheet = get_object_or_404(
|
||||
klass=ControlSheet, pk=self.kwargs['control_sheet_id']
|
||||
)
|
||||
|
||||
#AccessControlList.objects.check_access(
|
||||
# obj=control_sheet, permissions=(permission_required,),
|
||||
# user=self.request.user
|
||||
#)
|
||||
|
||||
return control_sheet
|
||||
|
||||
|
||||
class APIControlSheetCodeImageView(generics.RetrieveAPIView):
|
||||
"""
|
||||
get: Returns an image representation of the selected control_sheet.
|
||||
"""
|
||||
filter_backends = (MayanObjectPermissionsFilter,)
|
||||
#mayan_object_permissions = {
|
||||
# 'GET': (permission_control_sheet_view,),
|
||||
#}
|
||||
lookup_url_kwarg = 'control_sheet_code_id'
|
||||
#queryset = ControlSheetCode.objects.all()
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_control_sheet().codes.all()
|
||||
|
||||
def get_control_sheet(self):
|
||||
#if self.request.method == 'GET':
|
||||
# permission_required = permission_control_sheet_view
|
||||
#else:
|
||||
# permission_required = permission_control_sheet_edit
|
||||
|
||||
control_sheet = get_object_or_404(
|
||||
klass=ControlSheet, pk=self.kwargs['control_sheet_id']
|
||||
)
|
||||
|
||||
#AccessControlList.objects.check_access(
|
||||
# obj=control_sheet, permissions=(permission_required,),
|
||||
# user=self.request.user
|
||||
#)
|
||||
|
||||
return control_sheet
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
def get_serializer_class(self):
|
||||
return None
|
||||
|
||||
@cache_control(private=True)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
task = task_generate_control_sheet_code_image.apply_async(
|
||||
kwargs=dict(
|
||||
control_sheet_code_id=self.get_object().pk,
|
||||
)
|
||||
)
|
||||
|
||||
cache_filename = task.get(timeout=CONTROL_SHEET_CODE_IMAGE_TASK_TIMEOUT)
|
||||
cache_file = self.get_object().cache_partition.get_file(filename=cache_filename)
|
||||
with cache_file.open() as file_object:
|
||||
response = HttpResponse(file_object.read(), content_type='image')
|
||||
if '_hash' in request.GET:
|
||||
patch_cache_control(
|
||||
response,
|
||||
max_age=settings_control_sheet_image_cache_time.value
|
||||
)
|
||||
return response
|
||||
@@ -19,6 +19,7 @@ from mayan.apps.events.links import (
|
||||
)
|
||||
from mayan.apps.navigation.classes import SourceColumn
|
||||
|
||||
from .control_codes import *
|
||||
from .handlers import handler_process_document_version
|
||||
from .methods import method_document_submit, method_document_version_submit
|
||||
|
||||
@@ -26,7 +27,7 @@ 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_rest_api = True
|
||||
has_tests = False
|
||||
name = 'mayan.apps.control_codes'
|
||||
verbose_name = _('Control codes')
|
||||
|
||||
@@ -8,14 +8,16 @@ import qrcode
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import transaction
|
||||
from django.utils.translation import string_concat, ugettext_lazy as _
|
||||
|
||||
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'
|
||||
from .literals import (
|
||||
CONTROL_CODE_MAGIC_NUMBER, CONTROL_CODE_SEPARATOR, CONTROL_CODE_VERSION
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -27,6 +29,28 @@ class ControlCode(object):
|
||||
def get(cls, name):
|
||||
return cls._registry[name]
|
||||
|
||||
@classmethod
|
||||
def get_label(cls):
|
||||
if cls.arguments:
|
||||
return string_concat(cls.label, ': ', ', '.join(cls.arguments))
|
||||
else:
|
||||
return cls.label
|
||||
|
||||
@classmethod
|
||||
def get_choices(cls):
|
||||
#if layer:
|
||||
# transformation_list = [
|
||||
# (transformation.name, transformation) for transformation in cls._layer_transformations[layer]
|
||||
# ]
|
||||
#else:
|
||||
#control_code_list = cls._registry.items()
|
||||
|
||||
return sorted(
|
||||
[
|
||||
(name, klass.get_label()) for name, klass in cls._registry.items()#transformation_list
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def process_document_version(cls, document_version):
|
||||
logger.info(
|
||||
|
||||
15
mayan/apps/control_codes/control_codes.py
Normal file
15
mayan/apps/control_codes/control_codes.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .classes import ControlCode
|
||||
|
||||
|
||||
class ControlCodeTest(ControlCode):
|
||||
arguments = ('argument_1',)
|
||||
label = 'Test'
|
||||
name = 'test'
|
||||
|
||||
def execute(self):
|
||||
pass
|
||||
|
||||
|
||||
ControlCode.register(control_code=ControlCodeTest)
|
||||
65
mayan/apps/control_codes/forms.py
Normal file
65
mayan/apps/control_codes/forms.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import yaml
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.common.serialization import yaml_load
|
||||
|
||||
from .classes import ControlCode
|
||||
from .models import ControlSheetCode
|
||||
|
||||
|
||||
class ControlCodeClassSelectionForm(forms.Form):
|
||||
control_code = forms.ChoiceField(
|
||||
choices=(), help_text=_('Available control codes.'),
|
||||
label=_('Control code'),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ControlCodeClassSelectionForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields[
|
||||
'control_code'
|
||||
].choices = ControlCode.get_choices()
|
||||
|
||||
|
||||
"""
|
||||
class ControlSheetCodeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
fields = ('arguments', 'order')
|
||||
model = LayerTransformation
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
transformation_name = kwargs.pop('transformation_name', None)
|
||||
super(LayerTransformationForm, self).__init__(*args, **kwargs)
|
||||
|
||||
if not transformation_name:
|
||||
# Get the template name when the transformation is being edited.
|
||||
template_name = getattr(
|
||||
self.instance.get_transformation_class(), 'template_name',
|
||||
None
|
||||
)
|
||||
else:
|
||||
# Get the template name when the transformation is being created
|
||||
template_name = getattr(
|
||||
BaseTransformation.get(name=transformation_name),
|
||||
'template_name', None
|
||||
)
|
||||
|
||||
if template_name:
|
||||
self.fields['arguments'].widget.attrs['class'] = 'hidden'
|
||||
self.fields['order'].widget.attrs['class'] = 'hidden'
|
||||
|
||||
def clean(self):
|
||||
try:
|
||||
yaml_load(stream=self.cleaned_data['arguments'])
|
||||
except yaml.YAMLError:
|
||||
raise ValidationError(
|
||||
_(
|
||||
'"%s" not a valid entry.'
|
||||
) % self.cleaned_data['arguments']
|
||||
)
|
||||
"""
|
||||
@@ -17,5 +17,5 @@ def handler_initialize_new_document_type_settings(sender, instance, **kwargs):
|
||||
|
||||
|
||||
def handler_process_document_version(sender, instance, **kwargs):
|
||||
#if instance.document.document_type.file_metadata_settings.auto_process:
|
||||
#if instance.document.document_type.control_codes_settings.auto_process:
|
||||
instance.submit_for_control_codes_processing()
|
||||
|
||||
11
mayan/apps/control_codes/literals.py
Normal file
11
mayan/apps/control_codes/literals.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
CONTROL_CODE_MAGIC_NUMBER = 'MCTRL'
|
||||
CONTROL_CODE_SEPARATOR = ':'
|
||||
CONTROL_CODE_VERSION = '1'
|
||||
|
||||
CONTROL_SHEET_CODE_IMAGE_CACHE_NAME = 'workflow_images'
|
||||
CONTROL_SHEET_CODE_IMAGE_CACHE_STORAGE_INSTANCE_PATH = 'mayan.apps.control_codes.storages.storage_controlsheetcodeimagecache'
|
||||
CONTROL_SHEET_CODE_IMAGE_TASK_TIMEOUT = 60
|
||||
|
||||
DEFAULT_CONTROL_SHEET_CODE_IMAGE_CACHE_MAXIMUM_SIZE = 50 * 2 ** 20 # 50 Megabytes
|
||||
45
mayan/apps/control_codes/migrations/0001_initial.py
Normal file
45
mayan/apps/control_codes/migrations/0001_initial.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.22 on 2019-09-01 08:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import mayan.apps.common.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ControlSheet',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('label', models.CharField(max_length=196, unique=True, verbose_name='Label')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('label',),
|
||||
'verbose_name': 'Control sheet',
|
||||
'verbose_name_plural': 'Control sheets',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ControlSheetCode',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('order', models.PositiveIntegerField(blank=True, db_index=True, default=0, help_text='Order in which the transformations will be executed. If left unchanged, an automatic order value will be assigned.', verbose_name='Order')),
|
||||
('name', models.CharField(choices=[('test', 'Test: argument_1')], max_length=128, verbose_name='Name')),
|
||||
('arguments', models.TextField(blank=True, help_text='Enter the arguments for the control code as a YAML dictionary.', validators=[mayan.apps.common.validators.YAMLValidator()], verbose_name='Arguments')),
|
||||
('enabled', models.BooleanField(default=True, verbose_name='Enabled')),
|
||||
('control_sheet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='codes', to='control_codes.ControlSheet', verbose_name='Control sheet')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Control sheet code',
|
||||
'verbose_name_plural': 'Control sheet codes',
|
||||
},
|
||||
),
|
||||
]
|
||||
0
mayan/apps/control_codes/migrations/__init__.py
Normal file
0
mayan/apps/control_codes/migrations/__init__.py
Normal file
154
mayan/apps/control_codes/models.py
Normal file
154
mayan/apps/control_codes/models.py
Normal file
@@ -0,0 +1,154 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
|
||||
from furl import furl
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core import serializers
|
||||
from django.db import models
|
||||
from django.db.models import Max
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import (
|
||||
force_bytes, force_text, python_2_unicode_compatible
|
||||
)
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.common.serialization import yaml_load
|
||||
from mayan.apps.common.validators import YAMLValidator
|
||||
|
||||
from .classes import ControlCode
|
||||
from .literals import CONTROL_SHEET_CODE_IMAGE_CACHE_NAME
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ControlSheet(models.Model):
|
||||
label = models.CharField(
|
||||
max_length=196, unique=True, verbose_name=_('Label')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse(
|
||||
viewname='control_codes:control_sheet_detail', kwargs={
|
||||
'control_sheet_id': self.pk
|
||||
}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('label',)
|
||||
verbose_name = _('Control sheet')
|
||||
verbose_name_plural = _('Control sheets')
|
||||
|
||||
|
||||
class ControlSheetCode(models.Model):
|
||||
control_sheet = models.ForeignKey(
|
||||
on_delete=models.CASCADE, related_name='codes', to=ControlSheet,
|
||||
verbose_name=_('Control sheet')
|
||||
)
|
||||
order = models.PositiveIntegerField(
|
||||
blank=True, db_index=True, default=0, help_text=_(
|
||||
'Order in which the transformations will be executed. If left '
|
||||
'unchanged, an automatic order value will be assigned.'
|
||||
), verbose_name=_('Order')
|
||||
)
|
||||
name = models.CharField(
|
||||
choices=ControlCode.get_choices(),
|
||||
max_length=128, verbose_name=_('Name')
|
||||
)
|
||||
arguments = models.TextField(
|
||||
blank=True, help_text=_(
|
||||
'Enter the arguments for the control code as a YAML '
|
||||
'dictionary.'
|
||||
), validators=[YAMLValidator()], verbose_name=_('Arguments')
|
||||
)
|
||||
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Control sheet code')
|
||||
verbose_name_plural = _('Control sheet codes')
|
||||
|
||||
@cached_property
|
||||
def cache(self):
|
||||
Cache = apps.get_model(app_label='file_caching', model_name='Cache')
|
||||
return Cache.objects.get(name=CONTROL_SHEET_CODE_IMAGE_CACHE_NAME)
|
||||
|
||||
@cached_property
|
||||
def cache_partition(self):
|
||||
partition, created = self.cache.partitions.get_or_create(
|
||||
name='{}'.format(self.pk)
|
||||
)
|
||||
return partition
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
self.cache_partition.delete()
|
||||
return super(Workflow, self).delete(*args, **kwargs)
|
||||
|
||||
def generate_image(self):
|
||||
cache_filename = '{}'.format(self.get_hash())
|
||||
|
||||
if self.cache_partition.get_file(filename=cache_filename):
|
||||
logger.debug(
|
||||
'workflow cache file "%s" found', cache_filename
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
'workflow cache file "%s" not found', cache_filename
|
||||
)
|
||||
|
||||
image = self.render()
|
||||
with self.cache_partition.create_file(filename=cache_filename) as file_object:
|
||||
#file_object.write(image)
|
||||
image.save(file_object)
|
||||
|
||||
return cache_filename
|
||||
|
||||
def get_api_image_url(self, *args, **kwargs):
|
||||
final_url = furl()
|
||||
final_url.args = kwargs
|
||||
final_url.path = reverse(
|
||||
viewname='rest_api:workflow-image',
|
||||
kwargs={'pk': self.pk}
|
||||
)
|
||||
final_url.args['_hash'] = self.get_hash()
|
||||
|
||||
return final_url.tostr()
|
||||
|
||||
def get_arguments(self):
|
||||
return yaml_load(self.arguments or '{}')
|
||||
|
||||
def get_control_code_class(self):
|
||||
return ControlCode.get(name=self.name)
|
||||
|
||||
def get_hash(self):
|
||||
objects_lists = list(
|
||||
ControlSheetCode.objects.filter(pk=self.pk)
|
||||
)
|
||||
|
||||
return hashlib.sha256(
|
||||
force_bytes(
|
||||
serializers.serialize('json', objects_lists)
|
||||
)
|
||||
).hexdigest()
|
||||
|
||||
def render(self):
|
||||
return self.get_control_code_class()(**self.get_arguments()).image
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.order:
|
||||
last_order = ControlSheetCode.objects.filter(
|
||||
control_sheet=self.control_sheet
|
||||
).aggregate(Max('order'))['order__max']
|
||||
if last_order is not None:
|
||||
self.order = last_order + 1
|
||||
super(ControlSheetCode, self).save(*args, **kwargs)
|
||||
79
mayan/apps/control_codes/serializers.py
Normal file
79
mayan/apps/control_codes/serializers.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from mayan.apps.documents.models import DocumentType
|
||||
from mayan.apps.documents.serializers import DocumentTypeSerializer
|
||||
from mayan.apps.rest_api.relations import (
|
||||
FilteredPrimaryKeyRelatedField, MultiKwargHyperlinkedIdentityField
|
||||
)
|
||||
from mayan.apps.user_management.serializers import UserSerializer
|
||||
|
||||
from .models import ControlSheet, ControlSheetCode
|
||||
|
||||
|
||||
class ControlSheetSerializer(serializers.HyperlinkedModelSerializer):
|
||||
#states = ControlSheetStateSerializer(many=True, required=False)
|
||||
#transitions = ControlSheetTransitionSerializer(many=True, required=False)
|
||||
|
||||
code_list_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='control_sheet_id',
|
||||
view_name='rest_api:controlsheet-code-list'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
extra_kwargs = {
|
||||
'url': {
|
||||
'lookup_url_kwarg': 'control_sheet_id',
|
||||
'view_name': 'rest_api:controlsheet-detail'
|
||||
},
|
||||
}
|
||||
fields = ('code_list_url', 'id', 'label', 'url')
|
||||
model = ControlSheet
|
||||
|
||||
|
||||
class ControlSheetCodeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
control_sheet = ControlSheetSerializer(read_only=True)
|
||||
image_url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'control_sheet_id',
|
||||
'lookup_url_kwarg': 'control_sheet_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk',
|
||||
'lookup_url_kwarg': 'control_sheet_code_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:controlsheet-code-image'
|
||||
)
|
||||
url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'control_sheet_id',
|
||||
'lookup_url_kwarg': 'control_sheet_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk',
|
||||
'lookup_url_kwarg': 'control_sheet_code_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:controlsheet-code-detail'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = (
|
||||
'arguments', 'control_sheet', 'id', 'image_url', 'name',
|
||||
'order', 'url'
|
||||
)
|
||||
model = ControlSheetCode
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['control_sheet'] = self.context['control_sheet']
|
||||
return super(ControlSheetCodeSerializer, self).create(validated_data)
|
||||
|
||||
44
mayan/apps/control_codes/settings.py
Normal file
44
mayan/apps/control_codes/settings.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.smart_settings.classes import Namespace
|
||||
|
||||
from .literals import DEFAULT_CONTROL_SHEET_CODE_IMAGE_CACHE_MAXIMUM_SIZE
|
||||
from .utils import callback_update_control_sheet_image_cache_size
|
||||
|
||||
namespace = Namespace(label=_('Control codes'), name='control_codes')
|
||||
|
||||
setting_control_sheet_code_image_cache_maximum_size = namespace.add_setting(
|
||||
global_name='CONTROL_SHEET_CODE_IMAGE_CACHE_MAXIMUM_SIZE',
|
||||
default=DEFAULT_CONTROL_SHEET_CODE_IMAGE_CACHE_MAXIMUM_SIZE,
|
||||
help_text=_(
|
||||
'The threshold at which the CONTROL_SHEET_CODE_IMAGE_CACHE_STORAGE_BACKEND '
|
||||
'will start deleting the oldest control sheet code image cache files. '
|
||||
'Specify the size in bytes.'
|
||||
), post_edit_function=callback_update_control_sheet_image_cache_size
|
||||
)
|
||||
settings_control_sheet_code_image_cache_time = namespace.add_setting(
|
||||
global_name='CONTROL_SHEETS_CODE_IMAGE_CACHE_TIME', default='31556926',
|
||||
help_text=_(
|
||||
'Time in seconds that the browser should cache the supplied control sheet '
|
||||
'code images. The default of 31559626 seconds corresponde to 1 year.'
|
||||
)
|
||||
)
|
||||
setting_control_sheet_code_image_cache_storage = namespace.add_setting(
|
||||
global_name='CONTROL_SHEETS_CODE_IMAGE_CACHE_STORAGE_BACKEND',
|
||||
default='django.core.files.storage.FileSystemStorage', help_text=_(
|
||||
'Path to the Storage subclass to use when storing the cached '
|
||||
'control sheet code image files.'
|
||||
)
|
||||
)
|
||||
setting_control_sheet_code_image_cache_storage_arguments = namespace.add_setting(
|
||||
global_name='CONTROL_SHEETS_CODE_IMAGE_CACHE_STORAGE_BACKEND_ARGUMENTS',
|
||||
default={'location': os.path.join(settings.MEDIA_ROOT, 'control_sheets')},
|
||||
help_text=_(
|
||||
'Arguments to pass to the CONTROL_SHEETS_CODE_IMAGE_CACHE_STORAGE_BACKEND.'
|
||||
)
|
||||
)
|
||||
12
mayan/apps/control_codes/storages.py
Normal file
12
mayan/apps/control_codes/storages.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from mayan.apps.storage.utils import get_storage_subclass
|
||||
|
||||
from .settings import (
|
||||
setting_control_sheet_code_image_cache_storage,
|
||||
setting_control_sheet_code_image_storage_arguments
|
||||
)
|
||||
|
||||
storage_controlsheetcodeimagecache = get_storage_subclass(
|
||||
dotted_path=setting_control_sheet_code_image_cache_storage.value
|
||||
)(**setting_control_sheet_code_image_storage_arguments.value)
|
||||
@@ -11,6 +11,17 @@ from .classes import ControlCode
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@app.task()
|
||||
def task_generate_control_sheet_code_image(control_sheet_code_id):
|
||||
ControlSheetCode = apps.get_model(
|
||||
app_label='control_codes', model_name='ControlSheetCode'
|
||||
)
|
||||
|
||||
control_sheet_code = ControlSheetCode.objects.get(pk=control_sheet_code_id)
|
||||
|
||||
return control_sheet_code.generate_image()
|
||||
|
||||
|
||||
@app.task(ignore_result=True)
|
||||
def task_process_document_version(document_version_id):
|
||||
DocumentVersion = apps.get_model(
|
||||
|
||||
@@ -2,4 +2,40 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
urlpatterns = []
|
||||
from .api_views import (
|
||||
APIControlSheetCodeListView, APIControlSheetCodeView,
|
||||
APIControlSheetListView, APIControlSheetView,
|
||||
APIControlSheetCodeImageView
|
||||
)
|
||||
from .views import ControlSheetDetailView
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
regex=r'^control_sheets/(?P<control_sheet_id>\d+)/$',
|
||||
view=ControlSheetDetailView.as_view(), name='control_sheet_detail'
|
||||
),
|
||||
]
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
regex=r'^control_sheets/$', view=APIControlSheetListView.as_view(),
|
||||
name='controlsheet-list'
|
||||
),
|
||||
url(
|
||||
regex=r'^control_sheets/(?P<control_sheet_id>[0-9]+)/$',
|
||||
view=APIControlSheetView.as_view(),
|
||||
name='controlsheet-detail'
|
||||
),
|
||||
url(
|
||||
regex=r'^control_sheets/(?P<control_sheet_id>[0-9]+)/codes/$',
|
||||
view=APIControlSheetCodeListView.as_view(), name='controlsheet-code-list'
|
||||
),
|
||||
url(
|
||||
regex=r'^control_sheets/(?P<control_sheet_id>[0-9]+)/codes/(?P<control_sheet_code_id>[0-9]+)/$',
|
||||
view=APIControlSheetCodeView.as_view(), name='controlsheet-code-detail'
|
||||
),
|
||||
url(
|
||||
regex=r'^control_sheets/(?P<control_sheet_id>[0-9]+)/codes/(?P<control_sheet_code_id>[0-9]+)/image/$',
|
||||
name='controlsheet-code-image', view=APIControlSheetCodeImageView.as_view()
|
||||
),
|
||||
]
|
||||
|
||||
12
mayan/apps/control_codes/utils.py
Normal file
12
mayan/apps/control_codes/utils.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
|
||||
from .literals import CONTROL_SHEET_CODE_IMAGE_CACHE_NAME
|
||||
|
||||
|
||||
def callback_update_control_sheet_image_cache_size(setting):
|
||||
Cache = apps.get_model(app_label='file_caching', model_name='Cache')
|
||||
cache = Cache.objects.get(name=CONTROL_SHEET_CODE_IMAGE_CACHE_NAME)
|
||||
cache.maximum_size = setting.value
|
||||
cache.save()
|
||||
294
mayan/apps/control_codes/views.py
Normal file
294
mayan/apps/control_codes/views.py
Normal file
@@ -0,0 +1,294 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.template import RequestContext
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.common.generics import (
|
||||
FormView, SimpleView, SingleObjectCreateView, SingleObjectDeleteView,
|
||||
SingleObjectDetailView, SingleObjectEditView, SingleObjectListView
|
||||
)
|
||||
from mayan.apps.common.mixins import ExternalContentTypeObjectMixin
|
||||
|
||||
from .forms import ControlCodeClassSelectionForm
|
||||
#from .links import link_transformation_select
|
||||
from .models import ControlSheet
|
||||
#from .transformations import BaseTransformation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ControlSheetDetailView(SingleObjectDetailView):
|
||||
fields = ('label',)
|
||||
pk_url_kwarg = 'control_sheet_id'
|
||||
model = ControlSheet
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'object': self.object,
|
||||
'title': _(
|
||||
'Details for control sheet: %s'
|
||||
) % self.object
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
class TransformationCreateView(
|
||||
LayerViewMixin, ExternalContentTypeObjectMixin, SingleObjectCreateView
|
||||
):
|
||||
form_class = LayerTransformationForm
|
||||
|
||||
def form_valid(self, form):
|
||||
layer = self.layer
|
||||
content_type = self.get_content_type()
|
||||
object_layer, created = ObjectLayer.objects.get_or_create(
|
||||
content_type=content_type, object_id=self.external_object.pk,
|
||||
stored_layer=layer.stored_layer
|
||||
)
|
||||
|
||||
instance = form.save(commit=False)
|
||||
instance.content_object = self.external_object
|
||||
instance.name = self.kwargs['transformation_name']
|
||||
instance.object_layer = object_layer
|
||||
try:
|
||||
instance.full_clean()
|
||||
instance.save()
|
||||
except Exception as exception:
|
||||
logger.debug('Invalid form, exception: %s', exception)
|
||||
return super(TransformationCreateView, self).form_invalid(form)
|
||||
else:
|
||||
return super(TransformationCreateView, self).form_valid(form)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'content_object': self.external_object,
|
||||
'form_field_css_classes': 'hidden' if hasattr(
|
||||
self.get_transformation_class(), 'template_name'
|
||||
) else '',
|
||||
'layer': self.layer,
|
||||
'layer_name': self.layer.name,
|
||||
'navigation_object_list': ('content_object',),
|
||||
'title': _(
|
||||
'Create layer "%(layer)s" transformation '
|
||||
'"%(transformation)s" for: %(object)s'
|
||||
) % {
|
||||
'layer': self.layer,
|
||||
'transformation': self.get_transformation_class(),
|
||||
'object': self.external_object,
|
||||
}
|
||||
}
|
||||
|
||||
def get_form_extra_kwargs(self):
|
||||
return {
|
||||
'transformation_name': self.kwargs['transformation_name']
|
||||
}
|
||||
|
||||
def get_external_object_permission(self):
|
||||
return self.layer.permissions.get('create', None)
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse(
|
||||
viewname='converter:transformation_list', kwargs={
|
||||
'app_label': self.kwargs['app_label'],
|
||||
'model': self.kwargs['model'],
|
||||
'object_id': self.kwargs['object_id'],
|
||||
'layer_name': self.kwargs['layer_name']
|
||||
}
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.layer.get_transformations_for(
|
||||
obj=self.content_object
|
||||
)
|
||||
|
||||
def get_template_names(self):
|
||||
return [
|
||||
getattr(
|
||||
self.get_transformation_class(), 'template_name',
|
||||
self.template_name
|
||||
)
|
||||
]
|
||||
|
||||
def get_transformation_class(self):
|
||||
return BaseTransformation.get(name=self.kwargs['transformation_name'])
|
||||
|
||||
|
||||
class TransformationDeleteView(LayerViewMixin, SingleObjectDeleteView):
|
||||
model = LayerTransformation
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'content_object': self.object.object_layer.content_object,
|
||||
'layer_name': self.layer.name,
|
||||
'navigation_object_list': ('content_object', 'transformation'),
|
||||
'previous': reverse(
|
||||
viewname='converter:transformation_list', kwargs={
|
||||
'app_label': self.object.object_layer.content_type.app_label,
|
||||
'model': self.object.object_layer.content_type.model,
|
||||
'object_id': self.object.object_layer.object_id,
|
||||
'layer_name': self.object.object_layer.stored_layer.name
|
||||
}
|
||||
),
|
||||
'title': _(
|
||||
'Delete transformation "%(transformation)s" for: '
|
||||
'%(content_object)s?'
|
||||
) % {
|
||||
'transformation': self.object,
|
||||
'content_object': self.object.object_layer.content_object
|
||||
},
|
||||
'transformation': self.object,
|
||||
}
|
||||
|
||||
def get_object_permission(self):
|
||||
return self.layer.permissions.get('delete', None)
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse(
|
||||
viewname='converter:transformation_list', kwargs={
|
||||
'app_label': self.object.object_layer.content_type.app_label,
|
||||
'model': self.object.object_layer.content_type.model,
|
||||
'object_id': self.object.object_layer.object_id,
|
||||
'layer_name': self.object.object_layer.stored_layer.name
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class TransformationEditView(LayerViewMixin, SingleObjectEditView):
|
||||
form_class = LayerTransformationForm
|
||||
model = LayerTransformation
|
||||
|
||||
def form_valid(self, form):
|
||||
instance = form.save(commit=False)
|
||||
try:
|
||||
instance.full_clean()
|
||||
instance.save()
|
||||
except Exception as exception:
|
||||
logger.debug('Invalid form, exception: %s', exception)
|
||||
return super(TransformationEditView, self).form_invalid(form=form)
|
||||
else:
|
||||
return super(TransformationEditView, self).form_valid(form=form)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'content_object': self.object.object_layer.content_object,
|
||||
'form_field_css_classes': 'hidden' if hasattr(
|
||||
self.object.get_transformation_class(), 'template_name'
|
||||
) else '',
|
||||
'layer': self.layer,
|
||||
'layer_name': self.layer.name,
|
||||
'navigation_object_list': ('content_object', 'transformation'),
|
||||
'title': _(
|
||||
'Edit transformation "%(transformation)s" '
|
||||
'for: %(content_object)s'
|
||||
) % {
|
||||
'transformation': self.object,
|
||||
'content_object': self.object.object_layer.content_object
|
||||
},
|
||||
'transformation': self.object,
|
||||
}
|
||||
|
||||
def get_object_permission(self):
|
||||
return self.layer.permissions.get('edit', None)
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse(
|
||||
viewname='converter:transformation_list', kwargs={
|
||||
'app_label': self.object.object_layer.content_type.app_label,
|
||||
'model': self.object.object_layer.content_type.model,
|
||||
'object_id': self.object.object_layer.object_id,
|
||||
'layer_name': self.object.object_layer.stored_layer.name
|
||||
}
|
||||
)
|
||||
|
||||
def get_template_names(self):
|
||||
return [
|
||||
getattr(
|
||||
self.object.get_transformation_class(), 'template_name',
|
||||
self.template_name
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class TransformationListView(
|
||||
LayerViewMixin, ExternalContentTypeObjectMixin, SingleObjectListView
|
||||
):
|
||||
def get_external_object_permission(self):
|
||||
return self.layer.permissions.get('view', None)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'object': self.external_object,
|
||||
'hide_link': True,
|
||||
'hide_object': True,
|
||||
'layer_name': self.layer.name,
|
||||
'no_results_icon': self.layer.get_icon(),
|
||||
'no_results_main_link': link_transformation_select.resolve(
|
||||
context=RequestContext(
|
||||
request=self.request, dict_={
|
||||
'resolved_object': self.external_object,
|
||||
'layer_name': self.kwargs['layer_name'],
|
||||
}
|
||||
)
|
||||
),
|
||||
'no_results_text': self.layer.get_empty_results_text(),
|
||||
'no_results_title': _(
|
||||
'There are no entries for layer "%(layer_name)s"'
|
||||
) % {'layer_name': self.layer.label},
|
||||
'title': _(
|
||||
'Layer "%(layer)s" transformations for: %(object)s'
|
||||
) % {
|
||||
'layer': self.layer,
|
||||
'object': self.external_object,
|
||||
}
|
||||
}
|
||||
|
||||
def get_source_queryset(self):
|
||||
return self.layer.get_transformations_for(obj=self.external_object)
|
||||
|
||||
|
||||
class TransformationSelectView(
|
||||
ExternalContentTypeObjectMixin, LayerViewMixin, FormView
|
||||
):
|
||||
form_class = LayerTransformationSelectForm
|
||||
template_name = 'appearance/generic_form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
return HttpResponseRedirect(
|
||||
redirect_to=reverse(
|
||||
viewname='converter:transformation_create',
|
||||
kwargs={
|
||||
'app_label': self.kwargs['app_label'],
|
||||
'model': self.kwargs['model'],
|
||||
'object_id': self.kwargs['object_id'],
|
||||
'layer_name': self.kwargs['layer_name'],
|
||||
'transformation_name': form.cleaned_data[
|
||||
'transformation'
|
||||
]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'layer': self.layer,
|
||||
'layer_name': self.kwargs['layer_name'],
|
||||
'navigation_object_list': ('content_object',),
|
||||
'content_object': self.external_object,
|
||||
'submit_label': _('Select'),
|
||||
'title': _(
|
||||
'Select new layer "%(layer)s" transformation '
|
||||
'for: %(object)s'
|
||||
) % {
|
||||
'layer': self.layer,
|
||||
'object': self.external_object,
|
||||
}
|
||||
}
|
||||
|
||||
def get_form_extra_kwargs(self):
|
||||
return {
|
||||
'layer': self.layer
|
||||
}
|
||||
"""
|
||||
91
mayan/apps/rest_api/relations.py
Normal file
91
mayan/apps/rest_api/relations.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.models import Manager
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.relations import HyperlinkedIdentityField
|
||||
|
||||
from mayan.apps.common.utils import resolve_attribute
|
||||
|
||||
|
||||
class FilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
|
||||
def __init__(self, **kwargs):
|
||||
self.source_model = kwargs.pop('source_model', None)
|
||||
self.source_permission = kwargs.pop('source_permission', None)
|
||||
self.source_queryset = kwargs.pop('source_queryset', None)
|
||||
self.source_queryset_method = kwargs.pop('source_queryset_method', None)
|
||||
super(FilteredPrimaryKeyRelatedField, self).__init__(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
|
||||
if self.source_model:
|
||||
queryset = self.source_model._meta.default_manager.all()
|
||||
elif self.source_queryset:
|
||||
queryset = self.source_queryset
|
||||
if isinstance(queryset, (QuerySet, Manager)):
|
||||
# Ensure queryset is re-evaluated whenever used.
|
||||
queryset = queryset.all()
|
||||
else:
|
||||
method_name = self.source_queryset_method or 'get_{}_queryset'.format(
|
||||
self.field_name
|
||||
)
|
||||
try:
|
||||
queryset = getattr(self.parent, method_name)()
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
'Need to provide a source_model, a '
|
||||
'source_queryset, a source_queryset_method, or '
|
||||
'a method named "%s".' % method_name
|
||||
)
|
||||
|
||||
assert 'request' in self.context, (
|
||||
"`%s` requires the request in the serializer"
|
||||
" context. Add `context={'request': request}` when instantiating "
|
||||
"the serializer." % self.__class__.__name__
|
||||
)
|
||||
|
||||
request = self.context['request']
|
||||
|
||||
if self.source_permission:
|
||||
return AccessControlList.objects.restrict_queryset(
|
||||
permission=self.source_permission, queryset=queryset,
|
||||
user=request.user
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
||||
class MultiKwargHyperlinkedIdentityField(HyperlinkedIdentityField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.view_kwargs = kwargs.pop('view_kwargs', [])
|
||||
super(MultiKwargHyperlinkedIdentityField, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_url(self, obj, view_name, request, format):
|
||||
"""
|
||||
Extends HyperlinkedRelatedField to allow passing more than one view
|
||||
keyword argument.
|
||||
----
|
||||
Given an object, return the URL that hyperlinks to the object.
|
||||
|
||||
May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
|
||||
attributes are not configured to correctly match the URL conf.
|
||||
"""
|
||||
# Unsaved objects will not yet have a valid URL.
|
||||
if hasattr(obj, 'pk') and obj.pk in (None, ''):
|
||||
return None
|
||||
|
||||
kwargs = {}
|
||||
for entry in self.view_kwargs:
|
||||
kwargs[entry['lookup_url_kwarg']] = resolve_attribute(
|
||||
obj=obj, attribute=entry['lookup_field']
|
||||
)
|
||||
|
||||
return self.reverse(
|
||||
viewname=view_name, kwargs=kwargs, request=request, format=format
|
||||
)
|
||||
Reference in New Issue
Block a user