Initial commit to support page mapping

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-10-08 18:45:53 -04:00
parent 653f55f84a
commit 8cf807899a
36 changed files with 914 additions and 180 deletions

View File

@@ -63,7 +63,7 @@ class DocumentParsingApp(MayanAppConfig):
app_label='documents', model_name='Document'
)
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
DocumentType = apps.get_model(
app_label='documents', model_name='DocumentType'
@@ -100,9 +100,9 @@ class DocumentParsingApp(MayanAppConfig):
)
)
ModelField(
model=Document, name='versions__version_pages__content__content'
)
#ModelField(
# model=Document, name='versions__pages__content__content'
#)
ModelPermission.register(
model=Document, permissions=(
@@ -133,7 +133,7 @@ class DocumentParsingApp(MayanAppConfig):
)
document_search.add_model_field(
field='versions__version_pages__content__content', label=_('Content')
field='versions__pages__content__content', label=_('Content')
)
document_page_search.add_model_field(

View File

@@ -17,8 +17,8 @@ class DocumentPageContent(models.Model):
This model store's the parsed content of a document page.
"""
document_page = models.OneToOneField(
on_delete=models.CASCADE, related_name='content', to=DocumentPage,
verbose_name=_('Document page')
on_delete=models.CASCADE, related_name='content',
to=DocumentPage, verbose_name=_('Document page')
)
content = models.TextField(
blank=True, help_text=_(

View File

@@ -12,7 +12,9 @@ from mayan.apps.common.generics import (
)
from mayan.apps.common.mixins import ExternalObjectMixin
from mayan.apps.documents.forms import DocumentTypeFilteredSelectForm
from mayan.apps.documents.models import Document, DocumentPage, DocumentType
from mayan.apps.documents.models import (
Document, DocumentType, DocumentVersionPage
)
from .forms import DocumentContentForm, DocumentPageContentForm
from .models import DocumentPageContent, DocumentVersionParseError
@@ -87,7 +89,7 @@ class DocumentContentDownloadView(SingleObjectDownloadView):
class DocumentPageContentView(SingleObjectDetailView):
form_class = DocumentPageContentForm
model = DocumentPage
model = DocumentVersionPage
object_permission = permission_content_view
def dispatch(self, request, *args, **kwargs):

View File

@@ -3,18 +3,12 @@ from __future__ import unicode_literals
from django.contrib import admin
from .models import (
DeletedDocument, Document, DocumentPage, DocumentType,
DocumentTypeFilename, DocumentVersion, DuplicatedDocument, RecentDocument
DeletedDocument, Document, DocumentType, DocumentTypeFilename,
DocumentVersion, DocumentVersionPage, DuplicatedDocument,
RecentDocument
)
class DocumentPageInline(admin.StackedInline):
model = DocumentPage
extra = 1
classes = ('collapse-open',)
allow_add = True
class DocumentTypeFilenameInline(admin.StackedInline):
model = DocumentTypeFilename
extra = 1
@@ -29,6 +23,13 @@ class DocumentVersionInline(admin.StackedInline):
allow_add = True
class DocumentVersionPageInline(admin.StackedInline):
model = DocumentVersionPage
extra = 1
classes = ('collapse-open',)
allow_add = True
@admin.register(DeletedDocument)
class DeletedDocumentAdmin(admin.ModelAdmin):
date_hierarchy = 'deleted_date_time'

View File

@@ -33,7 +33,8 @@ from .serializers import (
DocumentTypeSerializer, DocumentVersionSerializer,
NewDocumentSerializer, NewDocumentVersionSerializer,
RecentDocumentSerializer, WritableDocumentSerializer,
WritableDocumentTypeSerializer, WritableDocumentVersionSerializer
WritableDocumentTypeSerializer, WritableDocumentVersionSerializer,
DocumentVersionPageSerializer
)
from .settings import settings_document_page_image_cache_time
from .tasks import task_generate_document_page_image
@@ -168,13 +169,13 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
)
return document
def get_document_version(self):
return get_object_or_404(
self.get_document().versions.all(), pk=self.kwargs['version_pk']
)
#def get_document_version(self):
# return get_object_or_404(
# self.get_document().versions.all(), pk=self.kwargs['version_pk']
# )
def get_queryset(self):
return self.get_document_version().pages_all.all()
return self.get_document().pages_all.all()
def get_serializer(self, *args, **kwargs):
return None
@@ -221,6 +222,95 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
return response
class APIDocumentVersionPageImageView(generics.RetrieveAPIView):
"""
get: Returns an image representation of the selected document version page.
"""
lookup_url_kwarg = 'page_pk'
def get_document(self):
if self.request.method == 'GET':
permission_required = permission_document_view
else:
permission_required = permission_document_edit
document = get_object_or_404(Document.passthrough, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=(permission_required,),
user=self.request.user
)
return document
def get_document_version(self):
return get_object_or_404(
self.get_document().versions.all(), pk=self.kwargs['version_pk']
)
def get_queryset(self):
return self.get_document_version().pages_all.all()
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):
width = request.GET.get('width')
height = request.GET.get('height')
zoom = request.GET.get('zoom')
if zoom:
zoom = int(zoom)
rotation = request.GET.get('rotation')
if rotation:
rotation = int(rotation)
maximum_layer_order = request.GET.get('maximum_layer_order')
if maximum_layer_order:
maximum_layer_order = int(maximum_layer_order)
task = task_generate_document_version_page_image.apply_async(
kwargs=dict(
document_version_page_id=self.get_object().pk, width=width,
height=height, zoom=zoom, rotation=rotation,
maximum_layer_order=maximum_layer_order,
user_id=request.user.pk
)
)
cache_filename = task.get(timeout=DOCUMENT_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=response,
max_age=settings_document_page_image_cache_time.value
)
return response
class APIDocumentPageListView(generics.ListAPIView):
serializer_class = DocumentPageSerializer
def get_document(self):
document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
return document
def get_queryset(self):
return self.get_document().pages.all()
class APIDocumentPageView(generics.RetrieveUpdateAPIView):
"""
get: Returns the selected document page details.
@@ -230,6 +320,38 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView):
lookup_url_kwarg = 'page_pk'
serializer_class = DocumentPageSerializer
def get_document(self):
if self.request.method == 'GET':
permission_required = permission_document_view
else:
permission_required = permission_document_edit
document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=(permission_required,),
user=self.request.user
)
return document
#def get_document_version(self):
# return get_object_or_404(
# self.get_document().versions.all(), pk=self.kwargs['version_pk']
# )
def get_queryset(self):
return self.get_document().pages.all()
class APIDocumentVersionPageView(generics.RetrieveUpdateAPIView):
"""
get: Returns the selected document verion page details.
patch: Edit the selected document version page.
put: Edit the selected document version page.
"""
lookup_url_kwarg = 'page_pk'
serializer_class = DocumentVersionPageSerializer
def get_document(self):
if self.request.method == 'GET':
permission_required = permission_document_view
@@ -289,8 +411,7 @@ class APIDocumentTypeView(generics.RetrieveUpdateDestroyAPIView):
'GET': (permission_document_type_view,),
'PUT': (permission_document_type_edit,),
'PATCH': (permission_document_type_edit,),
'DELETE': (permission_document_type_delete,)
}
'DELETE': (permission_document_type_delete,) }
permission_classes = (MayanPermission,)
queryset = DocumentType.objects.all()

View File

@@ -120,8 +120,8 @@ class DocumentsApp(MayanAppConfig):
DeletedDocument = self.get_model(model_name='DeletedDocument')
Document = self.get_model(model_name='Document')
DocumentPage = self.get_model(model_name='DocumentPage')
DocumentPageResult = self.get_model(model_name='DocumentPageResult')
DocumentPage = self.get_model(model_name='DocumentVersionPage')
DocumentPageResult = self.get_model(model_name='DocumentVersionPageResult')
DocumentType = self.get_model(model_name='DocumentType')
DocumentTypeFilename = self.get_model(model_name='DocumentTypeFilename')
DocumentVersion = self.get_model(model_name='DocumentVersion')

View File

@@ -32,12 +32,12 @@ class DashboardWidgetDocumentPagesTotal(DashboardWidgetNumeric):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
DocumentVersionPage = apps.get_model(
app_label='documents', model_name='DocumentVersionPage'
)
self.count = AccessControlList.objects.restrict_queryset(
permission=permission_document_view, user=request.user,
queryset=DocumentPage.objects.all()
queryset=DocumentVersionPage.objects.all()
).count()
return super(DashboardWidgetDocumentPagesTotal, self).render(request)

View File

@@ -42,3 +42,5 @@ PAGE_RANGE_RANGE = 'range'
PAGE_RANGE_CHOICES = (
(PAGE_RANGE_ALL, _('All pages')), (PAGE_RANGE_RANGE, _('Page range'))
)
RETRY_DELAY_DOCUMENT_RESET_PAGES = 30

View File

@@ -28,15 +28,15 @@ class DocumentManager(models.Manager):
class DocumentPageManager(models.Manager):
def get_by_natural_key(self, page_number, document_version_natural_key):
DocumentVersion = apps.get_model(
app_label='documents', model_name='DocumentVersion'
Document = apps.get_model(
app_label='documents', model_name='Document'
)
try:
document_version = DocumentVersion.objects.get_by_natural_key(*document_version_natural_key)
except DocumentVersion.DoesNotExist:
document = Document.objects.get_by_natural_key(*document_version_natural_key)
except Document.DoesNotExist:
raise self.model.DoesNotExist
return self.get(document_version__pk=document_version.pk, page_number=page_number)
return self.get(document__pk=document.pk, page_number=page_number)
def get_queryset(self):
return models.QuerySet(
@@ -124,6 +124,24 @@ class DocumentVersionManager(models.Manager):
return self.get(document__pk=document.pk, checksum=checksum)
class DocumentVersionPageManager(models.Manager):
def get_by_natural_key(self, page_number, document_version_natural_key):
DocumentVersion = apps.get_model(
app_label='documents', model_name='DocumentVersion'
)
try:
document_version = DocumentVersion.objects.get_by_natural_key(*document_version_natural_key)
except DocumentVersion.DoesNotExist:
raise self.model.DoesNotExist
return self.get(document_version__pk=document_version.pk, page_number=page_number)
def get_queryset(self):
return models.QuerySet(
model=self.model, using=self._db
).filter(enabled=True)
class DuplicatedDocumentManager(models.Manager):
def clean_empty_duplicate_lists(self):
self.filter(documents=None).delete()

View File

@@ -0,0 +1,27 @@
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('documents', '0051_documentpage_enabled'),
]
operations = [
migrations.DeleteModel(
name='DocumentPageResult',
),
migrations.RenameModel('DocumentPage', 'DocumentVersionPage'),
migrations.AlterField(
model_name='documentversionpage',
name='document_version',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='version_pages', to='documents.DocumentVersion',
verbose_name='Document version'
),
),
]

View File

@@ -0,0 +1,27 @@
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('documents', '0052_auto_20191007_1921'),
('ocr', '0008_auto_20180917_0646'),
('document_parsing', '0004_auto_20180917_0645'),
]
operations = [
migrations.CreateModel(
name='DocumentPageResult',
fields=[
],
options={
'verbose_name': 'Document version page',
'verbose_name_plural': 'Document version pages',
'ordering': ('document_version__document', 'page_number'),
'proxy': True,
'indexes': [],
},
bases=('documents.documentversionpage',),
),
]

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-10-08 15:22
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('documents', '0053_auto_20191007_1922'),
]
operations = [
migrations.DeleteModel(
name='DocumentPageResult',
),
migrations.CreateModel(
name='DocumentVersionPageResult',
fields=[
],
options={
'verbose_name': 'Document version page',
'verbose_name_plural': 'Document version pages',
'ordering': ('document_version__document', 'page_number'),
'proxy': True,
'indexes': [],
},
bases=('documents.documentversionpage',),
),
]

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-10-08 21:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('documents', '0054_auto_20191008_1522'),
]
operations = [
migrations.CreateModel(
name='DocumentPage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enabled', models.BooleanField(default=True, verbose_name='Enabled')),
('page_number', models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='Page number')),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pages', to='documents.Document', verbose_name='Document')),
],
options={
'verbose_name': 'Document page',
'verbose_name_plural': 'Document pages',
'ordering': ('page_number',),
},
),
migrations.AlterModelOptions(
name='documentversionpage',
options={'ordering': ('page_number',), 'verbose_name': 'Document version page', 'verbose_name_plural': 'Document version pages'},
),
migrations.AlterField(
model_name='documentversionpage',
name='document_version',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pages', to='documents.DocumentVersion', verbose_name='Document version'),
),
migrations.AlterUniqueTogether(
name='documentpage',
unique_together=set([('document', 'page_number')]),
),
]

View File

@@ -2,4 +2,5 @@ from .document_models import * # NOQA
from .document_page_models import * # NOQA
from .document_type_models import * # NOQA
from .document_version_models import * # NOQA
from .document_version_page_models import * # NOQA
from .misc_models import * # NOQA

View File

@@ -5,9 +5,10 @@ import uuid
from django.apps import apps
from django.core.files import File
from django.db import models
from django.db import models, transaction
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext, ugettext_lazy as _
@@ -15,6 +16,7 @@ from ..events import (
event_document_create, event_document_properties_edit,
event_document_type_change,
)
from ..literals import DOCUMENT_IMAGES_CACHE_NAME
from ..managers import DocumentManager, PassthroughManager, TrashCanManager
from ..settings import setting_language
from ..signals import post_document_type_change
@@ -102,6 +104,18 @@ class Document(models.Model):
)
return RecentDocument.objects.add_document_for_user(user, self)
@cached_property
def cache(self):
Cache = apps.get_model(app_label='file_caching', model_name='Cache')
return Cache.objects.get(name=DOCUMENT_IMAGES_CACHE_NAME)
@cached_property
def cache_partition(self):
partition, created = self.cache.partitions.get_or_create(
name='document-{}'.format(self.uuid)
)
return partition
def delete(self, *args, **kwargs):
to_trash = kwargs.pop('to_trash', True)
@@ -165,6 +179,22 @@ class Document(models.Model):
"""
return self.latest_version.open(*args, **kwargs)
def reset_pages(self):
with transaction.atomic():
for page in self.pages.all():
page.delete()
self.latest_version.update_page_count()
for version_page in self.latest_version.pages.all():
document_page = self.pages.create(
#content_type = models.ForeignKey(
# on_delete=models.CASCADE, to=ContentType
#)
#object_id = models.PositiveIntegerField()
content_object = version_page
)
def restore(self):
self.in_trash = False
self.save()
@@ -234,28 +264,31 @@ class Document(models.Model):
@property
def page_count(self):
return self.latest_version.page_count
return self.pages.count()
#return self.latest_version.page_count
@property
def pages_all(self):
try:
return self.latest_version.pages_all
except AttributeError:
# Document has no version yet
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
)
return self.pages.all()
#try:
# return self.latest_version.pages_all
#except AttributeError:
# # Document has no version yet
# DocumentPage = apps.get_model(
# app_label='documents', model_name='DocumentPage'
# )
return DocumentPage.objects.none()
# return DocumentPage.objects.none()
@property
def pages(self):
try:
return self.latest_version.pages
except AttributeError:
# Document has no version yet
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
)
return self.pages.all()
#try:
# return self.latest_version.pages
#except AttributeError:
# # Document has no version yet
# DocumentPage = apps.get_model(
# app_label='documents', model_name='DocumentVersionPage'
# )
return DocumentPage.objects.none()
# return DocumentPage.objects.none()

View File

@@ -4,14 +4,16 @@ import logging
from furl import furl
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Max
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from mayan.apps.converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION
from mayan.apps.converter.models import LayerTransformation
from mayan.apps.converter.transformations import (
BaseTransformation, TransformationResize, TransformationRotate,
@@ -26,25 +28,32 @@ from ..settings import (
setting_zoom_min_level
)
from .document_version_models import DocumentVersion
from .document_models import Document
#from .document_version_page_models import DocumentVersionPage
__all__ = ('DocumentPage', 'DocumentPageResult')
__all__ = ('DocumentPage',)# 'DocumentPageResult')
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class DocumentPage(models.Model):
"""
Model that describes a document version page
Model that describes a document page
"""
document_version = models.ForeignKey(
on_delete=models.CASCADE, related_name='version_pages', to=DocumentVersion,
verbose_name=_('Document version')
document = models.ForeignKey(
on_delete=models.CASCADE, related_name='pages', to=Document,
verbose_name=_('Document')
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
page_number = models.PositiveIntegerField(
db_index=True, default=1, editable=False,
verbose_name=_('Page number')
db_index=True, blank=True, null=True, verbose_name=_('Page number')
)
content_type = models.ForeignKey(
on_delete=models.CASCADE, to=ContentType
)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(
ct_field='content_type', fk_field='object_id'
)
objects = DocumentPageManager()
@@ -52,6 +61,7 @@ class DocumentPage(models.Model):
class Meta:
ordering = ('page_number',)
unique_together = ('document', 'page_number')
verbose_name = _('Document page')
verbose_name_plural = _('Document pages')
@@ -60,7 +70,7 @@ class DocumentPage(models.Model):
@cached_property
def cache_partition(self):
partition, created = self.document_version.cache.partitions.get_or_create(
partition, created = self.document.cache.partitions.get_or_create(
name=self.uuid
)
return partition
@@ -69,19 +79,19 @@ class DocumentPage(models.Model):
self.cache_partition.delete()
super(DocumentPage, self).delete(*args, **kwargs)
def detect_orientation(self):
with self.document_version.open() as file_object:
converter = get_converter_class()(
file_object=file_object,
mime_type=self.document_version.mimetype
)
return converter.detect_orientation(
page_number=self.page_number
)
#def detect_orientation(self):
# with self.document_version.open() as file_object:
# converter = get_converter_class()(
# file_object=file_object,
# mime_type=self.document_version.mimetype
# )
# return converter.detect_orientation(
# page_number=self.page_number
# )
@property
def document(self):
return self.document_version.document
#@property
#def document(self):
# return self.document_version.document
def generate_image(self, user=None, **kwargs):
transformation_list = self.get_combined_transformation_list(user=user, **kwargs)
@@ -90,7 +100,7 @@ class DocumentPage(models.Model):
# Check is transformed image is available
logger.debug('transformations cache filename: %s', combined_cache_filename)
if not setting_disable_transformed_image_cache.value and self.cache_partition.get_file(filename=combined_cache_filename):
if self.cache_partition.get_file(filename=combined_cache_filename):
logger.debug(
'transformations cache file "%s" found', combined_cache_filename
)
@@ -128,7 +138,8 @@ class DocumentPage(models.Model):
final_url.args = kwargs
final_url.path = reverse(
viewname='rest_api:documentpage-image', kwargs={
'pk': self.document.pk, 'version_pk': self.document_version.pk,
'pk': self.document.pk,
#'version_pk': self.document_version.pk,
'page_pk': self.pk
}
)
@@ -190,12 +201,12 @@ class DocumentPage(models.Model):
return transformation_list
def get_image(self, transformations=None):
cache_filename = 'base_image'
cache_filename = 'document_page'
logger.debug('Page cache filename: %s', cache_filename)
cache_file = self.cache_partition.get_file(filename=cache_filename)
if not setting_disable_base_image_cache.value and cache_file:
if cache_file:
logger.debug('Page cache file "%s" found', cache_filename)
with cache_file.open() as file_object:
@@ -216,7 +227,12 @@ class DocumentPage(models.Model):
logger.debug('Page cache file "%s" not found', cache_filename)
try:
with self.document_version.get_intermediate_file() as file_object:
#with self.document_version.get_intermediate_file() as file_object:
#Render or get cached document version page
self.content_object.get_image()
cache_filename = 'base_image'
cache_file = self.content_object.cache_partition.get_file(filename=cache_filename)
with cache_file.open() as file_object:
converter = get_converter_class()(
file_object=file_object
)
@@ -241,28 +257,39 @@ class DocumentPage(models.Model):
)
raise
def get_label(self):
return _(
'Page %(page_number)d out of %(total_pages)d of %(document)s'
) % {
'document': force_text(self.document),
'page_number': self.page_number,
'total_pages': self.document.pages_all.count()
}
get_label.short_description = _('Label')
@property
def is_in_trash(self):
return self.document.is_in_trash
def get_label(self):
return _(
'Page %(page_num)d out of %(total_pages)d of %(document)s'
) % {
'document': force_text(self.document),
'page_num': self.page_number,
'total_pages': self.document_version.pages_all.count()
}
get_label.short_description = _('Label')
def natural_key(self):
return (self.page_number, self.document_version.natural_key())
natural_key.dependencies = ['documents.DocumentVersion']
return (self.page_number, self.document.natural_key())
natural_key.dependencies = ['documents.Document']
def save(self, *args, **kwargs):
if not self.page_number:
last_page_number = DocumentPage.objects.filter(
document=self.document
).aggregate(Max('page_number'))['page_number__max']
if last_page_number is not None:
self.page_number = last_page_number + 1
else:
self.page_number = 1
super(DocumentPage, self).save(*args, **kwargs)
@property
def siblings(self):
return DocumentPage.objects.filter(
document_version=self.document_version
document=self.document
)
@property
@@ -271,12 +298,12 @@ class DocumentPage(models.Model):
Make cache UUID a mix of version ID and page ID to avoid using stale
images
"""
return '{}-{}'.format(self.document_version.uuid, self.pk)
return '{}-{}'.format(self.document.uuid, self.pk)
class DocumentPageResult(DocumentPage):
class Meta:
ordering = ('document_version__document', 'page_number')
proxy = True
verbose_name = _('Document page')
verbose_name_plural = _('Document pages')
#class DocumentVersionPageResult(DocumentVersionPage):
# class Meta:
# ordering = ('document_version__document', 'page_number')
# proxy = True
# verbose_name = _('Document version page')
# verbose_name_plural = _('Document version pages')

View File

@@ -246,16 +246,16 @@ class DocumentVersion(models.Model):
return result
@property
def pages_all(self):
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
)
return DocumentPage.passthrough.filter(document_version=self)
#@property
#def pages_all(self):
# DocumentPage = apps.get_model(
# app_label='documents', model_name='DocumentVersionPage'
# )
# return DocumentPage.passthrough.filter(document_version=self)
@property
def pages(self):
return self.version_pages.all()
#@property
#def pages(self):
# return self.pages.all()
@property
def page_count(self):
@@ -410,7 +410,7 @@ class DocumentVersion(models.Model):
pass
else:
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
with transaction.atomic():

View File

@@ -0,0 +1,284 @@
from __future__ import absolute_import, unicode_literals
import logging
from furl import furl
from django.db import models
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from mayan.apps.converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION
from mayan.apps.converter.models import LayerTransformation
from mayan.apps.converter.transformations import (
BaseTransformation, TransformationResize, TransformationRotate,
TransformationZoom
)
from mayan.apps.converter.utils import get_converter_class
from ..managers import DocumentVersionPageManager
from ..settings import (
setting_disable_base_image_cache, setting_disable_transformed_image_cache,
setting_display_width, setting_display_height, setting_zoom_max_level,
setting_zoom_min_level
)
from .document_version_models import DocumentVersion
__all__ = ('DocumentVersionPage', 'DocumentVersionPageResult')
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class DocumentVersionPage(models.Model):
"""
Model that describes a document version page
"""
document_version = models.ForeignKey(
on_delete=models.CASCADE, related_name='pages', to=DocumentVersion,
verbose_name=_('Document version')
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
page_number = models.PositiveIntegerField(
db_index=True, default=1, editable=False,
verbose_name=_('Page number')
)
objects = DocumentVersionPageManager()
#passthrough = models.Manager()
class Meta:
ordering = ('page_number',)
verbose_name = _('Document version page')
verbose_name_plural = _('Document version pages')
def __str__(self):
return self.get_label()
@cached_property
def cache_partition(self):
partition, created = self.document_version.cache.partitions.get_or_create(
name=self.uuid
)
return partition
def delete(self, *args, **kwargs):
self.cache_partition.delete()
super(DocumentVersionPage, self).delete(*args, **kwargs)
#def detect_orientation(self):
# with self.document_version.open() as file_object:
# converter = get_converter_class()(
# file_object=file_object,
# mime_type=self.document_version.mimetype
# )
# return converter.detect_orientation(
# page_number=self.page_number
# )
@property
def document(self):
return self.document_version.document
def generate_image(self, user=None, **kwargs):
transformation_list = self.get_combined_transformation_list(user=user, **kwargs)
combined_cache_filename = BaseTransformation.combine(transformation_list)
# Check is transformed image is available
logger.debug('transformations cache filename: %s', combined_cache_filename)
if self.cache_partition.get_file(filename=combined_cache_filename):
logger.debug(
'transformations cache file "%s" found', combined_cache_filename
)
else:
logger.debug(
'transformations cache file "%s" not found', combined_cache_filename
)
image = self.get_image(transformations=transformation_list)
with self.cache_partition.create_file(filename=combined_cache_filename) as file_object:
file_object.write(image.getvalue())
return combined_cache_filename
#def get_absolute_url(self):
# return reverse(
# viewname='documents:document_version_page_view', kwargs={
# 'pk': self.pk
# }
# )
def get_api_image_url(self, *args, **kwargs):
"""
Create an unique URL combining:
- the page's image URL
- the interactive argument
- a hash from the server side and interactive transformations
The purpose of this unique URL is to allow client side caching
if document page images.
"""
transformations_hash = BaseTransformation.combine(
self.get_combined_transformation_list(*args, **kwargs)
)
kwargs.pop('transformations', None)
final_url = furl()
final_url.args = kwargs
final_url.path = reverse(
viewname='rest_api:documentversionpage-image', kwargs={
'pk': self.document.pk, 'version_pk': self.document_version.pk,
'page_pk': self.pk
}
)
final_url.args['_hash'] = transformations_hash
return final_url.tostr()
def get_combined_transformation_list(self, user=None, *args, **kwargs):
"""
Return a list of transformation containing the server side
document page transformation as well as tranformations created
from the arguments as transient interactive transformation.
"""
# Convert arguments into transformations
transformations = kwargs.get('transformations', [])
# Set sensible defaults if the argument is not specified or if the
# argument is None
width = kwargs.get('width', setting_display_width.value) or setting_display_width.value
height = kwargs.get('height', setting_display_height.value) or setting_display_height.value
rotation = kwargs.get('rotation', DEFAULT_ROTATION) or DEFAULT_ROTATION
zoom_level = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL) or DEFAULT_ZOOM_LEVEL
if zoom_level < setting_zoom_min_level.value:
zoom_level = setting_zoom_min_level.value
if zoom_level > setting_zoom_max_level.value:
zoom_level = setting_zoom_max_level.value
# Generate transformation hash
transformation_list = []
maximum_layer_order = kwargs.get('maximum_layer_order', None)
# Stored transformations first
for stored_transformation in LayerTransformation.objects.get_for_object(
self, maximum_layer_order=maximum_layer_order, as_classes=True,
user=user
):
transformation_list.append(stored_transformation)
# Interactive transformations second
for transformation in transformations:
transformation_list.append(transformation)
if rotation:
transformation_list.append(
TransformationRotate(degrees=rotation)
)
if width:
transformation_list.append(
TransformationResize(width=width, height=height)
)
if zoom_level:
transformation_list.append(TransformationZoom(percent=zoom_level))
return transformation_list
def get_image(self, transformations=None):
cache_filename = 'base_image'
logger.debug('Page cache filename: %s', cache_filename)
cache_file = self.cache_partition.get_file(filename=cache_filename)
if cache_file:
logger.debug('Page cache file "%s" found', cache_filename)
with cache_file.open() as file_object:
converter = get_converter_class()(
file_object=file_object
)
converter.seek_page(page_number=0)
# This code is also repeated below to allow using a context
# manager with cache_file.open and close it automatically.
# Apply runtime transformations
for transformation in transformations or []:
converter.transform(transformation=transformation)
return converter.get_page()
else:
logger.debug('Page cache file "%s" not found', cache_filename)
try:
with self.document_version.get_intermediate_file() as file_object:
converter = get_converter_class()(
file_object=file_object
)
converter.seek_page(page_number=self.page_number - 1)
page_image = converter.get_page()
# Since open "wb+" doesn't create files, create it explicitly
with self.cache_partition.create_file(filename=cache_filename) as file_object:
file_object.write(page_image.getvalue())
# Apply runtime transformations
for transformation in transformations or []:
converter.transform(transformation=transformation)
return converter.get_page()
except Exception as exception:
# Cleanup in case of error
logger.error(
'Error creating page cache file "%s"; %s',
cache_filename, exception
)
raise
#@property
#def is_in_trash(self):
# return self.document.is_in_trash
def get_label(self):
return _(
'Version page %(page_number)d out of %(total_pages)d of %(document)s'
) % {
'document': force_text(self.document),
'page_number': self.page_number,
'total_pages': self.document_version.pages.count()
}
get_label.short_description = _('Label')
def natural_key(self):
return (self.page_number, self.document_version.natural_key())
natural_key.dependencies = ['documents.DocumentVersion']
#@property
#def siblings(self):
# return DocumentVersionPage.objects.filter(
# document_version=self.document_version
# )
@property
def uuid(self):
"""
Make cache UUID a mix of version ID and page ID to avoid using stale
images
"""
return '{}-{}'.format(self.document_version.uuid, self.pk)
class DocumentVersionPageResult(DocumentVersionPage):
class Meta:
ordering = ('document_version__document', 'page_number')
proxy = True
verbose_name = _('Document version page')
verbose_name_plural = _('Document version pages')

View File

@@ -30,6 +30,10 @@ queue_converter.add_task_type(
dotted_path='mayan.apps.documents.tasks.task_generate_document_page_image',
label=_('Generate document page image')
)
queue_converter.add_task_type(
dotted_path='mayan.apps.documents.tasks.task_generate_document_version_page_image',
label=_('Generate document version page image')
)
queue_documents.add_task_type(
dotted_path='mayan.apps.documents.tasks.task_delete_document',
@@ -66,6 +70,10 @@ queue_tools.add_task_type(
label=_('Duplicated document scan')
)
queue_uploads.add_task_type(
dotted_path='mayan.apps.documents.tasks.task_document_reset_pages',
label=_('Reset document pages')
)
queue_uploads.add_task_type(
dotted_path='mayan.apps.documents.tasks.task_update_page_count',
label=_('Update document page count')

View File

@@ -20,7 +20,7 @@ def transformation_format_uuid(term_string):
def get_queryset_page_search_queryset():
# Ignore documents in trash can
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
return DocumentPage.objects.filter(document_version__document__in_trash=False)
@@ -49,7 +49,7 @@ document_search.add_model_field(
document_page_search = SearchModel(
app_label='documents', list_mode=LIST_MODE_CHOICE_ITEM,
model_name='DocumentPage', permission=permission_document_view,
model_name='DocumentVersionPage', permission=permission_document_view,
queryset=get_queryset_page_search_queryset,
serializer_path='mayan.apps.documents.serializers.DocumentPageSerializer'
)

View File

@@ -8,42 +8,40 @@ from rest_framework.reverse import reverse
from mayan.apps.common.models import SharedUploadedFile
from .models import (
Document, DocumentVersion, DocumentPage, DocumentType,
DocumentTypeFilename, RecentDocument
Document, DocumentPage, DocumentType, DocumentTypeFilename,
DocumentVersion, DocumentVersionPage, RecentDocument
)
from .settings import setting_language
from .tasks import task_upload_new_version
class DocumentPageSerializer(serializers.HyperlinkedModelSerializer):
document_version_url = serializers.SerializerMethodField()
document_url = serializers.SerializerMethodField()
image_url = serializers.SerializerMethodField()
url = serializers.SerializerMethodField()
class Meta:
fields = ('document_version_url', 'image_url', 'page_number', 'url')
fields = ('document_url', 'image_url', 'page_number', 'url')
model = DocumentPage
def get_document_version_url(self, instance):
def get_document_url(self, instance):
return reverse(
viewname='rest_api:documentversion-detail', args=(
instance.document.pk, instance.document_version.pk,
viewname='rest_api:document-detail', args=(
instance.document.pk,
), request=self.context['request'], format=self.context['format']
)
def get_image_url(self, instance):
return reverse(
viewname='rest_api:documentpage-image', args=(
instance.document.pk, instance.document_version.pk,
instance.pk,
instance.document.pk, instance.pk,
), request=self.context['request'], format=self.context['format']
)
def get_url(self, instance):
return reverse(
viewname='rest_api:documentpage-detail', args=(
instance.document.pk, instance.document_version.pk,
instance.pk,
instance.document.pk, instance.pk,
), request=self.context['request'], format=self.context['format']
)
@@ -97,6 +95,39 @@ class WritableDocumentTypeSerializer(serializers.ModelSerializer):
return obj.documents.count()
class DocumentVersionPageSerializer(serializers.HyperlinkedModelSerializer):
document_version_url = serializers.SerializerMethodField()
image_url = serializers.SerializerMethodField()
url = serializers.SerializerMethodField()
class Meta:
fields = ('document_version_url', 'image_url', 'page_number', 'url')
model = DocumentVersionPage
def get_document_version_url(self, instance):
return reverse(
viewname='rest_api:documentversion-detail', args=(
instance.document.pk, instance.document_version.pk,
), request=self.context['request'], format=self.context['format']
)
def get_image_url(self, instance):
return reverse(
viewname='rest_api:documentversionpage-image', args=(
instance.document.pk, instance.document_version.pk,
instance.pk,
), request=self.context['request'], format=self.context['format']
)
def get_url(self, instance):
return reverse(
viewname='rest_api:documentversionpage-detail', args=(
instance.document.pk, instance.document_version.pk,
instance.pk,
), request=self.context['request'], format=self.context['format']
)
class DocumentVersionSerializer(serializers.HyperlinkedModelSerializer):
document_url = serializers.SerializerMethodField()
download_url = serializers.SerializerMethodField()

View File

@@ -41,7 +41,7 @@ def new_documents_per_month():
def new_document_pages_per_month():
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
qss = qsstats.QuerySetStats(
@@ -106,7 +106,7 @@ def new_document_pages_this_month(user=None):
app_label='acls', model_name='AccessControlList'
)
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
queryset = DocumentPage.objects.all()
@@ -195,7 +195,7 @@ def total_document_version_per_month():
def total_document_page_per_month():
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
qss = qsstats.QuerySetStats(

View File

@@ -9,7 +9,8 @@ from django.db import OperationalError
from mayan.celery import app
from .literals import (
UPDATE_PAGE_COUNT_RETRY_DELAY, UPLOAD_NEW_VERSION_RETRY_DELAY
RETRY_DELAY_DOCUMENT_RESET_PAGES, UPDATE_PAGE_COUNT_RETRY_DELAY,
UPLOAD_NEW_VERSION_RETRY_DELAY
)
logger = logging.getLogger(__name__)
@@ -64,6 +65,25 @@ def task_delete_stubs():
logger.info(msg='Finshed')
@app.task(bind=True, default_retry_delay=RETRY_DELAY_DOCUMENT_RESET_PAGES, ignore_result=True)
def task_document_reset_pages(self, document_id):
Document = apps.get_model(
app_label='documents', model_name='Document'
)
document = Document.objects.get(pk=document_id)
try:
document.reset_pages()
except OperationalError as exception:
logger.warning(
'Operational error during attempt to reset pages for '
'document: %s; %s. Retrying.', document,
exception
)
raise self.retry(exc=exception)
@app.task()
def task_generate_document_page_image(document_page_id, user_id=None, **kwargs):
DocumentPage = apps.get_model(
@@ -80,6 +100,22 @@ def task_generate_document_page_image(document_page_id, user_id=None, **kwargs):
return document_page.generate_image(user=user, **kwargs)
@app.task()
def task_generate_document_version_page_image(document_version_page_id, user_id=None, **kwargs):
DocumentVersionPage = apps.get_model(
app_label='documents', model_name='DocumentVersionPage'
)
User = get_user_model()
if user_id:
user = User.objects.get(pk=user_id)
else:
user = None
document_version_page = DocumentVersionPage.objects.get(pk=document_version_page_id)
return document_version_page.generate_image(user=user, **kwargs)
@app.task(ignore_result=True)
def task_scan_duplicates_all():
DuplicatedDocument = apps.get_model(

View File

@@ -4,13 +4,15 @@ from django.conf.urls import url
from .api_views import (
APITrashedDocumentListView, APIDeletedDocumentRestoreView,
APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentView,
APIDocumentListView, APIDocumentVersionDownloadView,
APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentPageListView,
APIDocumentView, APIDocumentListView, APIDocumentVersionDownloadView,
APIDocumentPageImageView, APIDocumentPageView,
APIDocumentTypeDocumentListView, APIDocumentTypeListView,
APIDocumentTypeView, APIDocumentVersionsListView,
APIDocumentVersionPageListView, APIDocumentVersionView,
APIRecentDocumentListView
APIRecentDocumentListView,
APIDocumentVersionPageView,
APIDocumentVersionPageImageView
)
from .views.document_views import (
DocumentDocumentTypeEditView, DocumentDownloadFormView,
@@ -405,6 +407,11 @@ api_urls = [
view=APIDocumentVersionPageListView.as_view(),
name='documentversion-page-list'
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/pages/$',
view=APIDocumentPageListView.as_view(),
name='document-page-list'
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/versions/(?P<version_pk>[0-9]+)/download/$',
view=APIDocumentVersionDownloadView.as_view(),
@@ -416,12 +423,20 @@ api_urls = [
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/versions/(?P<version_pk>[0-9]+)/pages/(?P<page_pk>[0-9]+)$',
view=APIDocumentVersionPageView.as_view(), name='documentversionpage-detail'
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/pages/(?P<page_pk>[0-9]+)$',
view=APIDocumentPageView.as_view(), name='documentpage-detail'
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/versions/(?P<version_pk>[0-9]+)/pages/(?P<page_pk>[0-9]+)/image/$',
regex=r'^documents/(?P<pk>[0-9]+)/pages/(?P<page_pk>[0-9]+)/image/$',
view=APIDocumentPageImageView.as_view(), name='documentpage-image'
),
url(
regex=r'^documents/(?P<pk>[0-9]+)/versions/(?P<version_pk>[0-9]+)/pages/(?P<page_pk>[0-9]+)/image/$',
view=APIDocumentVersionPageImageView.as_view(), name='documentversionpage-image'
),
url(
regex=r'^trashed_documents/$',
view=APITrashedDocumentListView.as_view(), name='trasheddocument-list'

View File

@@ -21,7 +21,7 @@ from mayan.apps.converter.literals import DEFAULT_ROTATION, DEFAULT_ZOOM_LEVEL
from ..forms import DocumentPageForm
from ..icons import icon_document_pages
from ..links import link_document_update_page_count
from ..models import Document, DocumentPage
from ..models import Document, DocumentVersionPage
from ..permissions import permission_document_edit, permission_document_view
from ..settings import (
setting_rotation_step, setting_zoom_percent_step, setting_zoom_max_level,
@@ -69,7 +69,7 @@ class DocumentPageListView(ExternalObjectMixin, SingleObjectListView):
class DocumentPageNavigationBase(ExternalObjectMixin, RedirectView):
external_object_class = DocumentPage
external_object_class = DocumentVersionPage
external_object_permission = permission_document_view
external_object_pk_url_kwarg = 'pk'
@@ -164,7 +164,7 @@ class DocumentPageNavigationPrevious(DocumentPageNavigationBase):
class DocumentPageView(ExternalObjectMixin, SimpleView):
external_object_class = DocumentPage
external_object_class = DocumentVersionPage
external_object_permission = permission_document_view
external_object_pk_url_kwarg = 'pk'
template_name = 'appearance/generic_form.html'
@@ -204,7 +204,7 @@ class DocumentPageViewResetView(RedirectView):
class DocumentPageInteractiveTransformation(ExternalObjectMixin, RedirectView):
external_object_class = DocumentPage
external_object_class = DocumentVersionPage
external_object_permission = permission_document_view
external_object_pk_url_kwarg = 'pk'
@@ -289,7 +289,7 @@ class DocumentPageDisable(MultipleObjectConfirmActionView):
return result
def get_source_queryset(self):
return DocumentPage.passthrough.all()
return DocumentVersionPage.passthrough.all()
def object_action(self, form, instance):
instance.enabled = False
@@ -319,7 +319,7 @@ class DocumentPageEnable(MultipleObjectConfirmActionView):
return result
def get_source_queryset(self):
return DocumentPage.passthrough.all()
return DocumentVersionPage.passthrough.all()
def object_action(self, form, instance):
instance.enabled = True

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-10-08 15:10
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('file_caching', '0002_auto_20190729_0236'),
]
operations = [
migrations.AlterField(
model_name='cache',
name='name',
field=models.CharField(db_index=True, help_text='Internal name of the cache.', max_length=128, unique=True, verbose_name='Name'),
),
]

View File

@@ -76,7 +76,7 @@ class MetadataApp(MayanAppConfig):
app_label='documents', model_name='Document'
)
DocumentPageResult = apps.get_model(
app_label='documents', model_name='DocumentPageResult'
app_label='documents', model_name='DocumentVersionPageResult'
)
DocumentType = apps.get_model(

View File

@@ -62,7 +62,7 @@ class OCRApp(MayanAppConfig):
app_label='documents', model_name='Document'
)
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
DocumentType = apps.get_model(
app_label='documents', model_name='DocumentType'
@@ -96,9 +96,9 @@ class OCRApp(MayanAppConfig):
)
)
ModelField(
model=Document, name='versions__version_pages__ocr_content__content'
)
#ModelField(
# model=Document, name='versions__pages__ocr_content__content'
#)
ModelPermission.register(
model=Document, permissions=(

View File

@@ -4,7 +4,9 @@ from django.db import models
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from mayan.apps.documents.models import DocumentPage, DocumentType, DocumentVersion
from mayan.apps.documents.models import (
DocumentPage, DocumentType, DocumentVersion
)
from .managers import (
DocumentPageOCRContentManager, DocumentTypeSettingsManager

View File

@@ -19,8 +19,8 @@ def task_do_ocr(self, document_version_pk):
DocumentVersion = apps.get_model(
app_label='documents', model_name='DocumentVersion'
)
DocumentPageOCRContent = apps.get_model(
app_label='ocr', model_name='DocumentPageOCRContent'
DocumentVersionPageOCRContent = apps.get_model(
app_label='ocr', model_name='DocumentVersionPageOCRContent'
)
lock_id = 'task_do_ocr_doc_version-%d' % document_version_pk
@@ -39,7 +39,7 @@ def task_do_ocr(self, document_version_pk):
'Starting document OCR for document version: %s',
document_version
)
DocumentPageOCRContent.objects.process_document_version(
DocumentVersionPageOCRContent.objects.process_document_version(
document_version=document_version
)
except OperationalError as exception:

View File

@@ -65,7 +65,7 @@ class OCRAPITestCase(DocumentTestMixin, BaseAPITestCase):
hasattr(self.test_document.pages.first(), 'ocr_content')
)
def _request_document_page_content_view(self):
def _request_document_version_page_content_view(self):
return self.get(
viewname='rest_api:document-page-ocr-content-view', kwargs={
'document_pk': self.test_document.pk,
@@ -75,7 +75,7 @@ class OCRAPITestCase(DocumentTestMixin, BaseAPITestCase):
)
def test_get_document_version_page_content_no_access(self):
response = self._request_document_page_content_view()
response = self._request_document_version_page_content_view()
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_get_document_version_page_content_with_access(self):
@@ -83,7 +83,7 @@ class OCRAPITestCase(DocumentTestMixin, BaseAPITestCase):
self.grant_access(
permission=permission_ocr_content_view, obj=self.test_document
)
response = self._request_document_page_content_view()
response = self._request_document_version_page_content_view()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(
TEST_DOCUMENT_CONTENT in response.data['content']

View File

@@ -8,13 +8,13 @@ from ..events import (
event_ocr_document_content_deleted, event_ocr_document_version_submit,
event_ocr_document_version_finish
)
from ..models import DocumentPageOCRContent
from ..models import DocumentVersionPageOCRContent
class OCREventsTestCase(GenericDocumentTestCase):
def test_document_content_deleted_event(self):
Action.objects.all().delete()
DocumentPageOCRContent.objects.delete_content_for(
DocumentVersionPageOCRContent.objects.delete_content_for(
document=self.test_document
)

View File

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from mayan.apps.documents.tests.base import GenericDocumentViewTestCase
from ..models import DocumentPageOCRContent
from ..models import DocumentVersionPageOCRContent
from ..permissions import (
permission_ocr_content_view, permission_ocr_document,
permission_document_type_ocr_setup
@@ -27,9 +27,9 @@ class OCRViewTestMixin(object):
}
)
def _request_document_page_content_view(self):
def _request_document_version_page_content_view(self):
return self.get(
viewname='ocr:document_page_ocr_content', kwargs={
viewname='ocr:document_version_page_ocr_content', kwargs={
'pk': self.test_document.pages.first().pk
}
)
@@ -86,8 +86,8 @@ class OCRViewsTestCase(OCRViewTestMixin, GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 404)
self.assertTrue(
DocumentPageOCRContent.objects.filter(
document_page=self.test_document.pages.first()
DocumentVersionPageOCRContent.objects.filter(
document_version_page=self.test_document.pages.first()
).exists()
)
@@ -101,28 +101,11 @@ class OCRViewsTestCase(OCRViewTestMixin, GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 302)
self.assertFalse(
DocumentPageOCRContent.objects.filter(
document_page=self.test_document.pages.first()
DocumentVersionPageOCRContent.objects.filter(
document_version_page=self.test_document.pages.first()
).exists()
)
def test_document_page_content_view_no_permissions(self):
self.test_document.submit_for_ocr()
response = self._request_document_page_content_view()
self.assertEqual(response.status_code, 404)
def test_document_page_content_view_with_access(self):
self.test_document.submit_for_ocr()
self.grant_access(
obj=self.test_document, permission=permission_ocr_content_view
)
response = self._request_document_page_content_view()
self.assertContains(
response=response, text=TEST_DOCUMENT_CONTENT, status_code=200
)
def test_document_submit_view_no_permission(self):
response = self._request_document_submit_view()
self.assertEqual(response.status_code, 404)
@@ -188,6 +171,23 @@ class OCRViewsTestCase(OCRViewTestMixin, GenericDocumentViewTestCase):
),
)
def test_document_version_page_content_view_no_permissions(self):
self.test_document.submit_for_ocr()
response = self._request_document_version_page_content_view()
self.assertEqual(response.status_code, 404)
def test_document_version_page_content_view_with_access(self):
self.test_document.submit_for_ocr()
self.grant_access(
obj=self.test_document, permission=permission_ocr_content_view
)
response = self._request_document_version_page_content_view()
self.assertContains(
response=response, text=TEST_DOCUMENT_CONTENT, status_code=200
)
class DocumentTypeOCRViewTestMixin(object):
def _request_document_type_ocr_settings_view(self):

View File

@@ -12,7 +12,9 @@ from mayan.apps.common.generics import (
)
from mayan.apps.common.mixins import ExternalObjectMixin
from mayan.apps.documents.forms import DocumentTypeFilteredSelectForm
from mayan.apps.documents.models import Document, DocumentPage, DocumentType
from mayan.apps.documents.models import (
Document, DocumentType, DocumentVersionPage
)
from .forms import DocumentPageOCRContentForm, DocumentOCRContentForm
from .models import DocumentPageOCRContent, DocumentVersionOCRError
@@ -74,7 +76,7 @@ class DocumentOCRContentView(SingleObjectDetailView):
class DocumentPageOCRContentView(SingleObjectDetailView):
form_class = DocumentPageOCRContentForm
model = DocumentPage
model = DocumentVersionPage
object_permission = permission_ocr_content_view
def dispatch(self, request, *args, **kwargs):

View File

@@ -28,7 +28,7 @@ class RedactionsApp(MayanAppConfig):
super(RedactionsApp, self).ready()
DocumentPage = apps.get_model(
app_label='documents', model_name='DocumentPage'
app_label='documents', model_name='DocumentVersionPage'
)
link_redaction_list = link_transformation_list.copy(

View File

@@ -62,7 +62,7 @@ class TagsApp(MayanAppConfig):
)
DocumentPageResult = apps.get_model(
app_label='documents', model_name='DocumentPageResult'
app_label='documents', model_name='DocumentVersionPageResult'
)
DocumentTag = self.get_model(model_name='DocumentTag')