Add support for new document page structure
Documents now have their own dedicated DocumentPage submodel. The old DocumentPage is now called DocumentVersionPage. This allows mappings between document pages and document version pages, allowing renumbering, appending pages. DocumentPages have a content_object to map them to any other object. For now they only map to DocumentVersionPages. New option added to the version upload form to append the pages of the new version. A new view was added to just append new pages with wraps the new document version upload form and hides the append pages checkbox set to True. Add a new action, reset_pages to reset the pages of the document to those of the latest version. Missing: appending tests, checks for proper content_object in OCR and document parsing. Author: Roberto Rosario <roberto.rosario@mayan-edms.com> Date: Thu Oct 11 12:00:25 2019 -0400
This commit is contained in:
@@ -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,7 +28,7 @@ from ..settings import (
|
||||
setting_zoom_min_level
|
||||
)
|
||||
|
||||
from .document_version_models import DocumentVersion
|
||||
from .document_models import Document
|
||||
|
||||
__all__ = ('DocumentPage', 'DocumentPageResult')
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -35,16 +37,22 @@ 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 +60,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 +69,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 +78,15 @@ 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
|
||||
)
|
||||
|
||||
@property
|
||||
def document(self):
|
||||
return self.document_version.document
|
||||
#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 generate_image(self, user=None, **kwargs):
|
||||
transformation_list = self.get_combined_transformation_list(user=user, **kwargs)
|
||||
@@ -90,7 +95,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,8 +133,7 @@ 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,
|
||||
'page_pk': self.pk
|
||||
'pk': self.document.pk, 'page_pk': self.pk
|
||||
}
|
||||
)
|
||||
final_url.args['_hash'] = transformations_hash
|
||||
@@ -190,12 +194,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,14 +220,25 @@ 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.generate_image()
|
||||
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
|
||||
)
|
||||
converter.seek_page(page_number=self.page_number - 1)
|
||||
converter.seek_page(page_number=0)
|
||||
#self.page_number - 1)
|
||||
|
||||
page_image = converter.get_page()
|
||||
|
||||
cache_filename = 'document_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())
|
||||
@@ -241,28 +256,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 +297,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')
|
||||
ordering = ('document', 'page_number')
|
||||
proxy = True
|
||||
verbose_name = _('Document page')
|
||||
verbose_name_plural = _('Document pages')
|
||||
verbose_name = _('Document page result')
|
||||
verbose_name_plural = _('Document pages result')
|
||||
|
||||
Reference in New Issue
Block a user