diff --git a/apps/converter/__init__.py b/apps/converter/__init__.py index 6ab5029f01..ffaef00c09 100644 --- a/apps/converter/__init__.py +++ b/apps/converter/__init__.py @@ -1,7 +1,16 @@ from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ImproperlyConfigured from navigation.api import register_sidebar_template +from converter.utils import load_backend +from converter.conf.settings import GRAPHICS_BACKEND + formats_list = {'text': _('file formats'), 'view': 'formats_list', 'famfam': 'pictures'} register_sidebar_template(['formats_list'], 'converter_file_formats_help.html') + +try: + backend = load_backend().ConverterClass() +except ImproperlyConfigured: + raise ImproperlyConfigured(u'Missing or incorrect converter backend: %s' % GRAPHICS_BACKEND) diff --git a/apps/converter/api.py b/apps/converter/api.py index 9de0ed737e..478783b299 100644 --- a/apps/converter/api.py +++ b/apps/converter/api.py @@ -3,7 +3,6 @@ import subprocess from django.utils.importlib import import_module from django.template.defaultfilters import slugify -from django.core.exceptions import ImproperlyConfigured from common import TEMPORARY_DIRECTORY from documents.utils import document_save_to_temp_dir @@ -12,21 +11,22 @@ from converter.conf.settings import UNPAPER_PATH from converter.conf.settings import OCR_OPTIONS from converter.conf.settings import UNOCONV_PATH from converter.exceptions import UnpaperError, OfficeConversionError -from converter.utils import load_backend -from converter.literals import DEFAULT_PAGE_INDEX_NUMBER, \ +from converter.literals import DEFAULT_PAGE_NUMBER, \ DEFAULT_OCR_FILE_FORMAT, QUALITY_DEFAULT, DEFAULT_ZOOM_LEVEL, \ DEFAULT_ROTATION, DEFAULT_FILE_FORMAT, QUALITY_PRINT +from converter import backend +from converter.literals import TRANSFORMATION_CHOICES +from converter.literals import TRANSFORMATION_RESIZE, \ + TRANSFORMATION_ROTATE, TRANSFORMATION_DENSITY, \ + TRANSFORMATION_ZOOM +from converter.literals import DIMENSION_SEPARATOR + + CONVERTER_OFFICE_FILE_EXTENSIONS = [ u'ods', u'docx', u'doc' ] -try: - backend = load_backend().ConverterClass() -except ImproperlyConfigured: - raise ImproperlyConfigured(u'Missing or incorrect converter backend: %s' % GRAPHICS_BACKEND) - - def cleanup(filename): """ Tries to remove the given filename. Ignores non-existent files @@ -107,18 +107,18 @@ def convert_document(document, *args, **kwargs): def convert(input_filepath, *args, **kwargs): size = kwargs.get('size') file_format = kwargs.get('file_format', DEFAULT_FILE_FORMAT) - extra_options = kwargs.get('extra_options', u'') zoom = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL) rotation = kwargs.get('rotation', DEFAULT_ROTATION) - page = kwargs.get('page', DEFAULT_PAGE_INDEX_NUMBER) + page = kwargs.get('page', DEFAULT_PAGE_NUMBER) cleanup_files = kwargs.get('cleanup_files', True) quality = kwargs.get('quality', QUALITY_DEFAULT) + transformations = kwargs.get('transformations', []) unoconv_output = None output_filepath = create_image_cache_filename(input_filepath, *args, **kwargs) - if os.path.exists(output_filepath): - return output_filepath + #if os.path.exists(output_filepath): + # return output_filepath path, extension = os.path.splitext(input_filepath) if extension[1:].lower() in CONVERTER_OFFICE_FILE_EXTENSIONS: @@ -128,18 +128,33 @@ def convert(input_filepath, *args, **kwargs): input_filepath = result extra_options = u'' - input_arg = u'%s[%s]' % (input_filepath, page) - extra_options += u' -resize %s' % size + #TODO: not here in the backend + input_arg = u'%s[%s]' % (input_filepath, page-1) + transformations.append( + { + 'transformation': TRANSFORMATION_RESIZE, + 'arguments': dict(zip([u'width', u'height'], size.split(DIMENSION_SEPARATOR))) + } + ) + if zoom != 100: - extra_options += u' -resize %d%% ' % zoom + transformations.append( + { + 'transformation': TRANSFORMATION_ZOOM, + 'arguments': {'percent': zoom} + } + ) if rotation != 0 and rotation != 360: - extra_options += u' -rotate %d ' % rotation + transformations.append( + { + 'transformation': TRANSFORMATION_ROTATE, + 'arguments': {'degrees': rotation} + } + ) - if format == u'jpg': - extra_options += u' -quality 85' try: - backend.convert_file(input_filepath=input_arg, arguments=extra_options, output_filepath=u'%s:%s' % (file_format, output_filepath), quality=quality) + backend.convert_file(input_filepath=input_arg, output_filepath=u'%s:%s' % (file_format, output_filepath), quality=quality, transformations=transformations) finally: if cleanup_files: cleanup(input_filepath) @@ -150,11 +165,7 @@ def convert(input_filepath, *args, **kwargs): def get_page_count(input_filepath): - try: - return len(backend.identify_file(unicode(input_filepath)).splitlines()) - except: - #TODO: send to other page number identifying program - return 1 + return backend.get_page_count(input_filepath) def get_document_dimensions(document, *args, **kwargs): @@ -166,7 +177,7 @@ def get_document_dimensions(document, *args, **kwargs): return [0, 0] -def convert_document_for_ocr(document, page=DEFAULT_PAGE_INDEX_NUMBER, file_format=DEFAULT_OCR_FILE_FORMAT): +def convert_document_for_ocr(document, page=DEFAULT_PAGE_NUMBER, file_format=DEFAULT_OCR_FILE_FORMAT): #Extract document file input_filepath = document_save_to_temp_dir(document, document.uuid) @@ -178,7 +189,7 @@ def convert_document_for_ocr(document, page=DEFAULT_PAGE_INDEX_NUMBER, file_form unpaper_output_file = u'%s_unpaper_out%s%spnm' % (temp_path, page, os.extsep) convert_output_file = u'%s_ocr%s%s%s' % (temp_path, page, os.extsep, file_format) - input_arg = u'%s[%s]' % (input_filepath, page) + input_arg = u'%s[%s]' % (input_filepath, page-1) try: document_page = document.documentpage_set.get(page_number=page + 1) @@ -198,3 +209,12 @@ def convert_document_for_ocr(document, page=DEFAULT_PAGE_INDEX_NUMBER, file_form cleanup(unpaper_output_file) return convert_output_file + + +def get_available_transformations_choices(): + result = [] + for transformation in backend.get_available_transformations(): + transformation_template = u'%s %s' % (TRANSFORMATION_CHOICES[transformation]['label'], u','.join(['<%s>' % argument['name'] if argument['required'] else '[%s]' % argument['name'] for argument in TRANSFORMATION_CHOICES[transformation]['arguments']])) + result.append([transformation, transformation_template]) + + return result diff --git a/apps/converter/backends/__init__.py b/apps/converter/backends/__init__.py index 1d81dd8149..0b42ec89c2 100644 --- a/apps/converter/backends/__init__.py +++ b/apps/converter/backends/__init__.py @@ -21,9 +21,6 @@ class ConverterBase(object): def get_available_transformations(self): raise NotImplementedError("Your %s class has not defined a get_available_transformations() method, which is required." % self.__class__.__name__) - def get_available_transformations_labels(self): - return ([(name, data['label']) for name, data in self.get_available_transformations().items()]) - def get_transformation_string(self, transformation_list): transformations = [] warnings = [] @@ -41,3 +38,5 @@ class ConverterBase(object): return u' '.join(transformations), warnings + def get_page_count(self): + raise NotImplementedError("Your %s class has not defined a get_page_count() method, which is required." % self.__class__.__name__) diff --git a/apps/converter/backends/graphicsmagick/base.py b/apps/converter/backends/graphicsmagick/base.py index 5570650038..8cb0f3fb55 100644 --- a/apps/converter/backends/graphicsmagick/base.py +++ b/apps/converter/backends/graphicsmagick/base.py @@ -8,6 +8,10 @@ from converter.conf.settings import GM_SETTINGS from converter.literals import QUALITY_DEFAULT, QUALITY_SETTINGS from converter.exceptions import ConvertError, UnknownFormat, IdentifyError from converter.backends import ConverterBase +from converter.literals import TRANSFORMATION_RESIZE, \ + TRANSFORMATION_ROTATE, TRANSFORMATION_DENSITY, \ + TRANSFORMATION_ZOOM +from converter.literals import DIMENSION_SEPARATOR CONVERTER_ERROR_STRING_NO_DECODER = u'No decode delegate for this image format' CONVERTER_ERROR_STARTS_WITH = u'starts with' @@ -28,7 +32,29 @@ class ConverterClass(ConverterBase): return proc.stdout.read() - def convert_file(self, input_filepath, output_filepath, quality=QUALITY_DEFAULT, arguments=None): + def convert_file(self, input_filepath, output_filepath, transformations=None, quality=QUALITY_DEFAULT): + arguments = [] + if transformations: + for transformation in transformations: + if transformation['transformation'] == TRANSFORMATION_RESIZE: + dimensions = [] + dimensions.append(unicode(transformation['arguments']['width'])) + if 'height' in transformation['arguments']: + dimensions.append(unicode(transformation['arguments']['height'])) + arguments.append(u'-resize') + arguments.append(u'%s' % DIMENSION_SEPARATOR.join(dimensions)) + + elif transformation['transformation'] == TRANSFORMATION_ZOOM: + arguments.append(u'-resize') + arguments.append(u'%d%%' % transformation['arguments']['zoom']) + + elif transformation['transformation'] == TRANSFORMATION_ROTATE: + arguments.append(u'-rotate') + arguments.append(u'%s' % transformation['arguments']['degrees']) + + print 'arguments: %s' % arguments + #if format == u'jpg': + # extra_options += u' -quality 85' command = [] command.append(unicode(GM_PATH)) command.append(u'convert') @@ -36,8 +62,9 @@ class ConverterClass(ConverterBase): command.extend(unicode(GM_SETTINGS).split()) command.append(unicode(input_filepath)) if arguments: - command.extend(unicode(arguments).split()) + command.extend(arguments) command.append(unicode(output_filepath)) + print 'command: %s' % command proc = subprocess.Popen(command, close_fds=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) return_code = proc.wait() if return_code != 0: @@ -76,10 +103,22 @@ class ConverterClass(ConverterBase): def get_available_transformations(self): - return { - 'rotate': { - 'label': _(u'Rotate [degrees]'), - 'arguments': [{'name': 'degrees'}], - 'command_line': u'-rotate %(degrees)d' - } - } + return [ + TRANSFORMATION_RESIZE, TRANSFORMATION_ROTATE, \ + TRANSFORMATION_DENSITY, TRANSFORMATION_ZOOM + ] + + + def get_page_count(self, input_filepath): + try: + return len(self.identify_file(unicode(input_filepath)).splitlines()) + except: + #TODO: send to other page number identifying program + return 1 + + + def _get_transformation_string(): + pass + #'command_line': u'-rotate %(degrees)d' + # } + #} diff --git a/apps/converter/backends/imagemagick/base.py b/apps/converter/backends/imagemagick/base.py index e2b8c40fdd..cd5b1ba53e 100644 --- a/apps/converter/backends/imagemagick/base.py +++ b/apps/converter/backends/imagemagick/base.py @@ -9,7 +9,10 @@ from converter.api import QUALITY_DEFAULT, QUALITY_SETTINGS from converter.exceptions import ConvertError, UnknownFormat, \ IdentifyError from converter.backends import ConverterBase - +from converter.literals import TRANSFORMATION_RESIZE, \ + TRANSFORMATION_ROTATE, TRANSFORMATION_DENSITY, \ + TRANSFORMATION_ZOOM + CONVERTER_ERROR_STRING_NO_DECODER = u'no decode delegate for this image format' @@ -29,6 +32,8 @@ class ConverterClass(ConverterBase): def convert_file(self, input_filepath, output_filepath, quality=QUALITY_DEFAULT, arguments=None): + #if format == u'jpg': + # extra_options += u' -quality 85' command = [] command.append(unicode(IM_CONVERT_PATH)) command.extend(unicode(QUALITY_SETTINGS[quality]).split()) @@ -73,10 +78,15 @@ class ConverterClass(ConverterBase): def get_available_transformations(self): - return { - 'rotate': { - 'label': _(u'Rotate [degrees]'), - 'arguments': [{'name': 'degrees'}], - 'command_line': u'-rotate %(degrees)d' - } - } + return [ + TRANSFORMATION_RESIZE, TRANSFORMATION_ROTATE, \ + TRANSFORMATION_DENSITY, TRANSFORMATION_ZOOM + ] + + + def get_page_count(self, input_filepath): + try: + return len(self.identify_file(unicode(input_filepath)).splitlines()) + except: + #TODO: send to other page number identifying program + return 1 diff --git a/apps/converter/backends/python/__init__.py b/apps/converter/backends/python/__init__.py new file mode 100644 index 0000000000..dfeca950f1 --- /dev/null +++ b/apps/converter/backends/python/__init__.py @@ -0,0 +1,3 @@ +from PIL import Image + +Image.init() diff --git a/apps/converter/backends/python/base.py b/apps/converter/backends/python/base.py new file mode 100644 index 0000000000..4d776454e4 --- /dev/null +++ b/apps/converter/backends/python/base.py @@ -0,0 +1,80 @@ +from PIL import Image + +from django.utils.translation import ugettext_lazy as _ + +from converter.literals import QUALITY_DEFAULT, QUALITY_SETTINGS +from converter.exceptions import ConvertError, UnknownFormat, IdentifyError +from converter.backends import ConverterBase +from converter.literals import TRANSFORMATION_RESIZE, \ + TRANSFORMATION_ROTATE + +class ConverterClass(ConverterBase): + def identify_file(self, input_filepath, arguments=None): + pass + + + def get_page_count(self, input_filepath): + page_count = 1 + im = Image.open(input_filepath) + + try: + while 1: + im.seek(im.tell()+1) + page_count += 1 + # do something to im + except EOFError: + pass # end of sequence + + return page_count + + + def convert_file(self, input_filepath, output_filepath, quality=QUALITY_DEFAULT, arguments=None): + im = Image.open(input_filepath) + outfile, format = output_filepath.split(u':') + im.save(outfile, format) + ''' + command = [] + command.append(unicode(GM_PATH)) + command.append(u'convert') + command.extend(unicode(QUALITY_SETTINGS[quality]).split()) + command.extend(unicode(GM_SETTINGS).split()) + command.append(unicode(input_filepath)) + if arguments: + command.extend(unicode(arguments).split()) + command.append(unicode(output_filepath)) + proc = subprocess.Popen(command, close_fds=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + return_code = proc.wait() + if return_code != 0: + #Got an error from convert program + error_line = proc.stderr.readline() + if (CONVERTER_ERROR_STRING_NO_DECODER in error_line) or (CONVERTER_ERROR_STARTS_WITH in error_line): + #Try to determine from error message which class of error is it + raise UnknownFormat + else: + raise ConvertError(error_line) + ''' + + def get_format_list(self): + """ + Introspect PIL's internal registry to obtain a list of the + supported file types + """ + formats = [] + for format_name in Image.ID: + formats.append((format_name, u'')) + + return formats + + + def get_available_transformations(self): + return [ + TRANSFORMATION_RESIZE, TRANSFORMATION_ROTATE + ] + + + def get_page_count(self, input_filepath): + try: + return len(self.identify_file(unicode(input_filepath)).splitlines()) + except: + #TODO: send to other page number identifying program + return 1 diff --git a/apps/converter/conf/settings.py b/apps/converter/conf/settings.py index f73c0f2b64..fcaa1ec9b0 100644 --- a/apps/converter/conf/settings.py +++ b/apps/converter/conf/settings.py @@ -12,7 +12,7 @@ register_settings( {'name': u'UNPAPER_PATH', 'global_name': u'CONVERTER_UNPAPER_PATH', 'default': u'/usr/bin/unpaper', 'description': _(u'File path to unpaper program.'), 'exists': True}, {'name': u'GM_PATH', 'global_name': u'CONVERTER_GM_PATH', 'default': u'/usr/bin/gm', 'description': _(u'File path to graphicsmagick\'s program.'), 'exists': True}, {'name': u'GM_SETTINGS', 'global_name': u'CONVERTER_GM_SETTINGS', 'default': u''}, - {'name': u'GRAPHICS_BACKEND', 'global_name': u'CONVERTER_GRAPHICS_BACKEND', 'default': u'converter.backends.imagemagick', 'description': _(u'Graphics conversion backend to use. Options are: converter.backends.imagemagick and converter.backends.graphicsmagick.')}, + {'name': u'GRAPHICS_BACKEND', 'global_name': u'CONVERTER_GRAPHICS_BACKEND', 'default': u'converter.backends.python', 'description': _(u'Graphics conversion backend to use. Options are: converter.backends.imagemagick, converter.backends.graphicsmagick and converter.backends.python.')}, {'name': u'UNOCONV_PATH', 'global_name': u'CONVERTER_UNOCONV_PATH', 'default': u'/usr/bin/unoconv', 'exists': True}, {'name': u'OCR_OPTIONS', 'global_name': u'CONVERTER_OCR_OPTIONS', 'default': u'-colorspace Gray -depth 8 -resample 200x200'}, {'name': u'DEFAULT_OPTIONS', 'global_name': u'CONVERTER_DEFAULT_OPTIONS', 'default': u''}, diff --git a/apps/converter/literals.py b/apps/converter/literals.py index 403400d229..7671fddf8a 100644 --- a/apps/converter/literals.py +++ b/apps/converter/literals.py @@ -1,3 +1,5 @@ +from django.utils.translation import ugettext_lazy as _ + from converter.conf.settings import DEFAULT_OPTIONS from converter.conf.settings import LOW_QUALITY_OPTIONS from converter.conf.settings import HIGH_QUALITY_OPTIONS @@ -5,7 +7,7 @@ from converter.conf.settings import PRINT_QUALITY_OPTIONS DEFAULT_ZOOM_LEVEL = 100 DEFAULT_ROTATION = 0 -DEFAULT_PAGE_INDEX_NUMBER = 0 +DEFAULT_PAGE_NUMBER = 1 DEFAULT_FILE_FORMAT = u'jpg' DEFAULT_OCR_FILE_FORMAT = u'tif' @@ -20,3 +22,43 @@ QUALITY_SETTINGS = { QUALITY_HIGH: HIGH_QUALITY_OPTIONS, QUALITY_PRINT: PRINT_QUALITY_OPTIONS } + +DIMENSION_SEPARATOR = u'x' + +TRANSFORMATION_RESIZE = u'resize' +TRANSFORMATION_ROTATE = u'rotate' +TRANSFORMATION_DENSITY = u'density' +TRANSFORMATION_ZOOM = u'zoom' + +TRANSFORMATION_CHOICES = { + TRANSFORMATION_RESIZE: { + 'label': _(u'Resize'), + 'description': _(u'Resize.'), + 'arguments': [ + {'name': 'width', 'label': _(u'width'), 'required': True}, + {'name': 'height', 'label': _(u'height'), 'required': False}, + ] + }, + TRANSFORMATION_ROTATE: { + 'label': _(u'Rotate'), + 'description': _(u'Rotate by n degress.'), + 'arguments': [ + {'name': 'degrees', 'label': _(u'degrees'), 'required': True} + ] + }, + TRANSFORMATION_DENSITY: { + 'label': _(u'Density'), + 'description': _(u'Change the resolution (ie: DPI) without resizing.'), + 'arguments': [ + {'name': 'width', 'label': _(u'width'), 'required': True}, + {'name': 'height', 'label': _(u'height'), 'required': False}, + ] + }, + TRANSFORMATION_ZOOM: { + 'label': _(u'Zoom'), + 'description': _(u'Zoom by n percent.'), + 'arguments': [ + {'name': 'percent', 'label': _(u'percent'), 'required': True} + ] + }, +} diff --git a/apps/converter/views.py b/apps/converter/views.py index ad95783539..ef7173f908 100644 --- a/apps/converter/views.py +++ b/apps/converter/views.py @@ -1,38 +1,18 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import render_to_response from django.template import RequestContext -from django.utils.importlib import import_module + +from converter import backend from converter.conf.settings import GRAPHICS_BACKEND - -def _lazy_load(fn): - _cached = [] - - def _decorated(): - if not _cached: - _cached.append(fn()) - return _cached[0] - return _decorated - - -@_lazy_load -def _get_backend(): - return import_module(GRAPHICS_BACKEND) - -try: - backend = _get_backend() -except ImportError: - raise ImportError(u'Missing or incorrect converter backend: %s' % GRAPHICS_BACKEND) - - def formats_list(request): #check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW]) context = { 'title': _(u'suported file formats'), 'hide_object': True, - 'object_list': backend.get_format_list(), + 'object_list': sorted(backend.get_format_list()), 'extra_columns': [ { 'name': _(u'name'), diff --git a/apps/documents/managers.py b/apps/documents/managers.py index 3b007a936e..d63b2d644f 100644 --- a/apps/documents/managers.py +++ b/apps/documents/managers.py @@ -13,3 +13,11 @@ class RecentDocumentManager(models.Manager): to_delete = self.model.objects.filter(user=user)[RECENT_COUNT:] for recent_to_delete in to_delete: recent_to_delete.delete() + + +class DocumentPageTransformationManager(models.Manager): + def get_for_document_page(self, document_page): + return self.model.objects.filter(document_page=document_page) + + def get_for_document_page_as_list(self, document_page): + return list([{'transformation': transformation['transformation'], 'arguments': eval(transformation['arguments'])} for transformation in self.get_for_document_page(document_page).values('transformation', 'arguments')]) diff --git a/apps/documents/models.py b/apps/documents/models.py index e0df918fc1..aa344dce48 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -12,12 +12,13 @@ from python_magic import magic from taggit.managers import TaggableManager from dynamic_search.api import register from converter.api import get_page_count -from converter.api import backend +from converter.api import get_available_transformations_choices from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION from documents.conf.settings import STORAGE_BACKEND -from documents.managers import RecentDocumentManager +from documents.managers import RecentDocumentManager, \ + DocumentPageTransformationManager def get_filename_from_uuid(instance, filename): @@ -259,9 +260,6 @@ class DocumentPage(models.Model): def get_absolute_url(self): return ('document_page_view', [self.pk]) - def get_transformation_string(self): - return backend.get_transformation_string(self.documentpagetransformation_set.values('transformation', 'arguments')) - class DocumentPageTransformation(models.Model): """ @@ -270,9 +268,11 @@ class DocumentPageTransformation(models.Model): """ document_page = models.ForeignKey(DocumentPage, verbose_name=_(u'document page')) order = models.PositiveIntegerField(default=0, blank=True, null=True, verbose_name=_(u'order'), db_index=True) - transformation = models.CharField(choices=backend.get_available_transformations_labels(), max_length=128, verbose_name=_(u'transformation')) + transformation = models.CharField(choices=get_available_transformations_choices(), max_length=128, verbose_name=_(u'transformation')) arguments = models.TextField(blank=True, null=True, verbose_name=_(u'arguments'), help_text=_(u'Use dictionaries to indentify arguments, example: {\'degrees\':90}')) + objects = DocumentPageTransformationManager() + def __unicode__(self): return u'"%s" for %s' % (self.get_transformation_display(), unicode(self.document_page)) diff --git a/apps/documents/views.py b/apps/documents/views.py index 4a3247c576..dcd383006d 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -285,7 +285,7 @@ def document_edit(request, document_id): 'object': document, }, context_instance=RequestContext(request)) - +''' def calculate_converter_arguments(document, *args, **kwargs): size = kwargs.pop('size', PREVIEW_SIZE) quality = kwargs.pop('quality', QUALITY_DEFAULT) @@ -308,7 +308,7 @@ def calculate_converter_arguments(document, *args, **kwargs): } return arguments, warnings - +''' def get_document_image(request, document_id, size=PREVIEW_SIZE, quality=QUALITY_DEFAULT): check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW]) @@ -327,14 +327,17 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, quality=QUALITY_ rotation = int(request.GET.get('rotation', 0)) % 360 - arguments, warnings = calculate_converter_arguments(document, size=size, file_format=DEFAULT_FILE_FORMAT, quality=quality, page=page, zoom=zoom, rotation=rotation) + #arguments, warnings = calculate_converter_arguments(document, size=size, file_format=DEFAULT_FILE_FORMAT, quality=quality, page=page, zoom=zoom, rotation=rotation) - if warnings and (request.user.is_staff or request.user.is_superuser): - for warning in warnings: - messages.warning(request, _(u'Page transformation error: %s') % warning) + #if warnings and (request.user.is_staff or request.user.is_superuser): + # for warning in warnings: + # messages.warning(request, _(u'Page transformation error: %s') % warning) + + transformations = DocumentPageTransformation.objects.get_for_document_page_as_list(document) try: - output_file = convert_document(document, **arguments) + #output_file = convert_document(document, **arguments) + output_file = convert_document(document, size=size, file_format=DEFAULT_FILE_FORMAT, quality=quality, page=page, zoom=zoom, rotation=rotation, transformations=transformations) except UnkownConvertError, e: if request.user.is_staff or request.user.is_superuser: messages.error(request, e) diff --git a/apps/sources/managers.py b/apps/sources/managers.py index aee45cf4c1..1fd2d38d21 100644 --- a/apps/sources/managers.py +++ b/apps/sources/managers.py @@ -6,3 +6,6 @@ class SourceTransformationManager(models.Manager): def get_for_object(self, obj): ct = ContentType.objects.get_for_model(obj) return self.model.objects.filter(content_type=ct).filter(object_id=obj.pk) + + def get_for_object_as_list(self, obj): + return list([{'transformation': transformation['transformation'], 'arguments': eval(transformation['arguments'])} for transformation in self.get_for_object(obj).values('transformation', 'arguments')]) diff --git a/apps/sources/models.py b/apps/sources/models.py index ffd4211fe6..795f269132 100644 --- a/apps/sources/models.py +++ b/apps/sources/models.py @@ -6,7 +6,8 @@ from django.contrib.contenttypes import generic from documents.models import DocumentType from documents.managers import RecentDocumentManager from metadata.models import MetadataType -from converter.api import backend +from converter.api import get_available_transformations_choices +from converter.literals import DIMENSION_SEPARATOR from sources.managers import SourceTransformationManager @@ -118,7 +119,7 @@ class StagingFolder(InteractiveBaseModel): if self.preview_height: dimensions.append(unicode(self.preview_height)) - return u'x'.join(dimensions) + return DIMENSION_SEPARATOR.join(dimensions) class Meta(InteractiveBaseModel.Meta): verbose_name = _(u'staging folder') @@ -162,8 +163,8 @@ class SourceTransformation(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') order = models.PositiveIntegerField(default=0, blank=True, null=True, verbose_name=_(u'order'), db_index=True) - transformation = models.CharField(choices=backend.get_available_transformations_labels(), max_length=128, verbose_name=_(u'transformation')) - arguments = models.TextField(blank=True, null=True, verbose_name=_(u'arguments'), help_text=_(u'Use dictionaries to indentify arguments, example: {\'degrees\':90}')) + transformation = models.CharField(choices=get_available_transformations_choices(), max_length=128, verbose_name=_(u'transformation')) + arguments = models.TextField(blank=True, null=True, verbose_name=_(u'arguments'), help_text=_(u'Use dictionaries to indentify arguments, example: %s') % u'{\'degrees\':90}') objects = SourceTransformationManager() diff --git a/apps/sources/staging.py b/apps/sources/staging.py index a608f1b30f..ebb31a59d9 100644 --- a/apps/sources/staging.py +++ b/apps/sources/staging.py @@ -106,16 +106,15 @@ class StagingFile(object): def upload(self): """ Return a StagingFile encapsulated in a File class instance to - allow for easier upload a staging files + allow for easier upload of staging files """ try: return File(file(self.filepath, 'rb'), name=self.filename) except Exception, exc: raise Exception(ugettext(u'Unable to upload staging file: %s') % exc) - def delete(self, preview_size): - # tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS) - cache_cleanup(self.filepath, size=preview_size)# , extra_options=tranformation_string) + def delete(self, preview_size, transformations): + cache_cleanup(self.filepath, size=preview_size, transformations=transformations) try: os.unlink(self.filepath) except OSError, exc: @@ -124,24 +123,7 @@ class StagingFile(object): else: raise OSError(ugettext(u'Unable to delete staging file: %s') % exc) - def preview(self, preview_size): + def preview(self, preview_size, transformations): errors = [] - # tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS) - # output_file = convert(self.filepath, size=STAGING_FILES_PREVIEW_SIZE, extra_options=tranformation_string, cleanup_files=False) - output_file = convert(self.filepath, size=preview_size, cleanup_files=False) + output_file = convert(self.filepath, size=preview_size, cleanup_files=False, transformations=transformations) return output_file, errors - - -def get_transformation_string(transformations): - transformation_list = [] - errors = [] - #for transformation in transformations: - # try: - # if transformation['name'] in TRANFORMATION_CHOICES: - # output = TRANFORMATION_CHOICES[transformation['name']] % eval(transformation['arguments']) - # transformation_list.append(output) - # except Exception, e: - # errors.append(e) - - #tranformation_string = ' '.join(transformation_list) - return tranformation_string, errors diff --git a/apps/sources/views.py b/apps/sources/views.py index 6a48dc1e34..5748f7ffd8 100644 --- a/apps/sources/views.py +++ b/apps/sources/views.py @@ -285,7 +285,10 @@ def staging_file_preview(request, source_type, source_id, staging_file_id): staging_folder = get_object_or_404(StagingFolder, pk=source_id) StagingFile = create_staging_file_class(request, staging_folder.folder_path) try: - output_file, errors = StagingFile.get(staging_file_id).preview(staging_folder.get_preview_size()) + output_file, errors = StagingFile.get(staging_file_id).preview( + preview_size=staging_folder.get_preview_size(), + transformations=SourceTransformation.objects.get_for_object_as_list(staging_folder) + ) if errors and (request.user.is_staff or request.user.is_superuser): for error in errors: messages.warning(request, _(u'Staging file transformation error: %(error)s') % { @@ -318,7 +321,10 @@ def staging_file_delete(request, source_type, source_id, staging_file_id): if request.method == 'POST': try: - staging_file.delete(staging_folder.get_preview_size()) + staging_file.delete( + preview_size=staging_folder.get_preview_size(), + transformations=SourceTransformation.objects.get_for_object_as_list(staging_folder) + ) messages.success(request, _(u'Staging file delete successfully.')) except Exception, e: messages.error(request, e) @@ -509,6 +515,8 @@ def setup_source_transformation_edit(request, transformation_id): form = SourceTransformationForm(instance=source_transformation, data=request.POST) if form.is_valid(): try: + # Test the validity of the argument field + eval(form.cleaned_data['arguments']) form.save() messages.success(request, _(u'Source transformation edited successfully')) return HttpResponseRedirect(next) @@ -598,6 +606,8 @@ def setup_source_transformation_create(request, source_type, source_id): form = SourceTransformationForm_create(request.POST) if form.is_valid(): try: + # Test the validity of the argument field + eval(form.cleaned_data['arguments']) source_tranformation = form.save(commit=False) source_tranformation.content_object = source source_tranformation.save()