Files
mayan-edms/mayan/apps/sources/classes.py
Roberto Rosario 36a51eeb73 Switch to full app paths
Instead of inserting the path of the apps into the Python app,
the apps are now referenced by their full import path.

This solves name clashes with external or native Python libraries.
Example: Mayan statistics app vs. Python new statistics library.

Every app reference is now prepended with 'mayan.apps'.

Existing config.yml files need to be updated manually.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-04-05 02:02:57 -04:00

174 lines
5.7 KiB
Python

from __future__ import unicode_literals
import base64
import logging
import os
import time
from furl import furl
from django.core.files import File
from django.core.files.base import ContentFile
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.six.moves.urllib.parse import quote_plus, unquote_plus
from mayan.apps.converter import TransformationResize, converter_class
from .storages import storage_staging_file_image_cache
logger = logging.getLogger(__name__)
class PseudoFile(File):
def __init__(self, file, name):
self.name = name
self.file = file
self.file.seek(0, os.SEEK_END)
self.size = self.file.tell()
self.file.seek(0)
class SourceUploadedFile(File):
def __init__(self, source, file, extra_data=None):
self.file = file
self.source = source
self.extra_data = extra_data
@python_2_unicode_compatible
class StagingFile(object):
"""
Simple class to extend the File class to add preview capabilities
files in a directory on a storage
"""
def __init__(self, staging_folder, filename=None, encoded_filename=None):
self.staging_folder = staging_folder
if encoded_filename:
self.encoded_filename = str(encoded_filename)
self.filename = base64.urlsafe_b64decode(
unquote_plus(self.encoded_filename)
).decode('utf8')
else:
self.filename = filename
self.encoded_filename = quote_plus(base64.urlsafe_b64encode(
filename.encode('utf8')
))
def __str__(self):
return force_text(self.filename)
def as_file(self):
return File(
file=open(self.get_full_path(), mode='rb'), name=self.filename
)
@property
def cache_filename(self):
return '{}{}'.format(self.staging_folder.pk, self.encoded_filename)
def delete(self):
storage_staging_file_image_cache.delete(self.cache_filename)
os.unlink(self.get_full_path())
def generate_image(self, *args, **kwargs):
transformation_list = self.get_combined_transformation_list(*args, **kwargs)
# Check is transformed image is available
logger.debug('transformations cache filename: %s', self.cache_filename)
if storage_staging_file_image_cache.exists(self.cache_filename):
logger.debug(
'staging file cache file "%s" found', self.cache_filename
)
else:
logger.debug(
'staging file cache file "%s" not found', self.cache_filename
)
image = self.get_image(transformations=transformation_list)
with storage_staging_file_image_cache.open(self.cache_filename, 'wb+') as file_object:
file_object.write(image.getvalue())
return self.cache_filename
def get_api_image_url(self, *args, **kwargs):
final_url = furl()
final_url.args = kwargs
final_url.path = reverse(
'rest_api:stagingfolderfile-image-view', args=(
self.staging_folder.pk,
self.encoded_filename
)
)
return final_url.tostr()
def get_combined_transformation_list(self, *args, **kwargs):
"""
Return a list of transformation containing the server side
staging file 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 = self.staging_folder.preview_width
height = self.staging_folder.preview_height
# Generate transformation hash
transformation_list = []
# Interactive transformations second
for transformation in transformations:
transformation_list.append(transformation)
if width:
transformation_list.append(
TransformationResize(width=width, height=height)
)
return transformation_list
def get_date_time_created(self):
return time.ctime(os.path.getctime(self.get_full_path()))
def get_full_path(self):
return os.path.join(self.staging_folder.folder_path, self.filename)
def get_image(self, transformations=None):
cache_filename = self.cache_filename
file_object = None
try:
file_object = open(self.get_full_path(), mode='rb')
converter = converter_class(file_object=file_object)
page_image = converter.get_page()
# Since open "wb+" doesn't create files, check if the file
# exists, if not then create it
if not storage_staging_file_image_cache.exists(cache_filename):
storage_staging_file_image_cache.save(name=cache_filename, content=ContentFile(content=''))
with storage_staging_file_image_cache.open(cache_filename, 'wb+') as file_object:
file_object.write(page_image.getvalue())
except Exception as exception:
# Cleanup in case of error
logger.error(
'Error creating staging file cache "%s"; %s',
cache_filename, exception
)
storage_staging_file_image_cache.delete(cache_filename)
if file_object:
file_object.close()
raise
for transformation in transformations:
converter.transform(transformation=transformation)
result = converter.get_page()
file_object.close()
return result