Skip to content

Commit

Permalink
Merge pull request #1303 from gustavofonseca/preferred-alt-image
Browse files Browse the repository at this point in the history
Criação do atributo ArticleAsset.preferred_alt_file
  • Loading branch information
fabiobatalha authored Oct 4, 2016
2 parents 7f420b1 + 9f37fd1 commit 503bebb
Show file tree
Hide file tree
Showing 6 changed files with 629 additions and 2 deletions.

Large diffs are not rendered by default.

29 changes: 27 additions & 2 deletions scielomanager/journalmanager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
from scielo_extensions import modelfields
from tastypie.models import create_api_key
import celery
from PIL import Image

from scielomanager.utils import base28
from scielomanager.custom_fields import ContentTypeRestrictedFileField, XMLSPSField
from scielomanager.custom_fields import (
ContentTypeRestrictedFileField,
XMLSPSField,
)
from . import modelmanagers


Expand Down Expand Up @@ -1497,8 +1501,12 @@ class ArticleAsset(models.Model):
"""
article = models.ForeignKey('Article', on_delete=models.CASCADE,
related_name='assets')
file = models.FileField(upload_to=make_article_directory_path('assets'),
file = models.FileField(
upload_to=make_article_directory_path('assets'),
max_length=1024)
preferred_alt_file = models.FileField(
upload_to=make_article_directory_path('alt_assets'),
max_length=1024, default=u'')
owner = models.CharField(max_length=1024, default=u'')
use_license = models.TextField(default=u'')
updated_at = models.DateTimeField(auto_now=True)
Expand All @@ -1507,6 +1515,23 @@ def __repr__(self):
return u'<%s id="%s" url="%s">' % (self.__class__.__name__,
self.pk, self.file.url)

def is_image(self):
"""Verifica se o ativo digital é uma imagem.
"""
try:
img = Image.open(self.file)
except IOError:
return False
else:
return True

@property
def best_file(self):
if self.preferred_alt_file:
return self.preferred_alt_file
else:
return self.file


class ArticleHTMLRendition(models.Model):
"""Documento HTML de uma tradução de uma instância de Article.
Expand Down
85 changes: 85 additions & 0 deletions scielomanager/journalmanager/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from celery.utils.log import get_task_logger
from django.templatetags.static import static
import packtools
from PIL import Image

from scielomanager.celery import app
from scielomanager import connectors
Expand Down Expand Up @@ -389,10 +390,14 @@ def create_articleasset_from_bytes(aid, filename, content, owner=None,
_owner = owner or u''
_use_license = use_license or u''

# create and save the asset
asset = models.ArticleAsset(article=article, owner=_owner,
use_license=_use_license)
asset.file.save(filename, ContentFile(content))

# create a preferred alternative for the asset
create_preferred_image_file.delay(asset.pk)

logger.info('New ArticleAsset %s added to Article with aid: %s.',
repr(asset), aid)

Expand Down Expand Up @@ -434,3 +439,83 @@ def create_article_html_renditions(article_pk, css_url=None, valid_only=False):

return files_urls


def convert_image_to_jpeg(filepath, mode=None, **kwargs):
"""Converte a imagem no caminho `filepath` para o formato JPEG.
Retorna um buffer com o conteúdo convertido da imagem. Pode levantar
`ValueError` caso `filepath` não seja reconhecido como uma imagem.
:param **kwargs: (opcional) argumentos nomeados serão repassados para a
função `Image.save`, com exceção de `format` que foi pré-definido.
"""
output_buffer = io.BytesIO()
_ = kwargs.pop('format', None)

original = Image.open(filepath)
if mode:
_image = original.convert(mode=mode)
else:
_image = original

_image.save(output_buffer, format='jpeg', **kwargs)

return output_buffer


@app.task(throws=(ValueError, TypeError,))
def create_preferred_image_file(asset_pk):
"""Cria uma versão alternativa de `ArticleAsset.file`, preferida para o
manuseio.
Por hora a versão preferida é o JPEG ao invés de TIFF. Para os demais
formatos não serão geradas outras versões.
:param asset_pk: chave primária da instância de `ArticleAsset`.
"""
try:
asset = models.ArticleAsset.objects.get(pk=asset_pk)

except models.ArticleAsset.DoesNotExist:
raise ValueError('Cannot find ArticleAsset with pk: %s' % asset_pk)

try:
filepath = asset.file.path
except ValueError as exc:
logger.exception(exc)
logger.error('Cannot create a preferred alt file for %s. Skipping.',
filepath)
raise

if not asset.is_image():
logger.error('Cannot create a preferred alt file for %s. Skipping.',
filepath)
raise ValueError('Cannot create preferred alternatives for files '
'other than images.')

_file = Image.open(asset.file.path)
if _file.format.lower() != 'tiff':
raise ValueError('Image is already in a preferred format.')

try:
# Levanta IOError caso `filepath` não seja um arquivo de imagem.
jpeg_buffer = convert_image_to_jpeg(filepath)
except IOError as exc:
logger.exception(exc)
logger.error('Cannot create a preferred alt file for %s. Skipping.',
filepath)
raise

_, filename = os.path.split(filepath)
filename_head, _ = os.path.splitext(filename)

jpeg_filename = filename_head + '.jpeg'

asset.preferred_alt_file.save(jpeg_filename,
ContentFile(jpeg_buffer.getvalue()))

logger.info('Finished creating an alternative file for %s.',
repr(asset))

return asset.preferred_alt_file.url

Binary file not shown.
15 changes: 15 additions & 0 deletions scielomanager/journalmanager/tests/modelfactories.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from journalmanager import models
from django.contrib.auth.models import Group
from django.core.files.base import File


_HERE = os.path.dirname(os.path.abspath(__file__))
Expand All @@ -15,6 +16,10 @@
SAMPLE_XML = xml_file.read()


SAMPLE_TIFF_IMAGE = open(
os.path.join(_HERE, 'image_test', 'sample_tif_image.tif'))


with open(os.path.join(_HERE, 'xml_samples', '0034-8910-rsp-48-2-0216_related.xml')) as xml_file:
SAMPLE_XML_RELATED = xml_file.read()

Expand Down Expand Up @@ -232,3 +237,13 @@ class ArticleFactory(factory.Factory):
article_type = u'research-article'
doi = u'10.1590/S0034-8910.2014048004965'


class ArticleAssetFactory(factory.Factory):
FACTORY_FOR = models.ArticleAsset

article = factory.SubFactory(ArticleFactory)
file = File(SAMPLE_TIFF_IMAGE)
owner = u'SciELO'
use_license = u'Creative Commons - BY'


101 changes: 101 additions & 0 deletions scielomanager/journalmanager/tests/tests_tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#coding: utf-8
import os
import io
import copy
import unittest
Expand Down Expand Up @@ -898,3 +899,103 @@ def test_htmls_filenames_are_suffixed_with_lang(self):
for url, lang in urls:
self.assertTrue(url.endswith(u'-' + lang + u'.html'))


class ConvertImageToJpegTests(TestCase):
def test_convert_gif_image_to_jpeg(self):
from PIL import Image
image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
u'image_test', u'cover_too_heavy.gif')

# A conversão de GIF para JPEG depende da conversão do modo para a
# profundidade de cor por pixel. Por isso do argumento `mode`.
jpeg_buff = tasks.convert_image_to_jpeg(image_path, mode='RGB')
jpeg_buff.seek(0)

img = Image.open(jpeg_buff)
self.assertEquals(img.format.lower(), 'jpeg')

def test_convert_tif_image_to_jpeg(self):
from PIL import Image
image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
u'image_test', u'sample_tif_image.tif')

jpeg_buff = tasks.convert_image_to_jpeg(image_path)
jpeg_buff.seek(0)

img = Image.open(jpeg_buff)
self.assertEquals(img.format.lower(), 'jpeg')

def test_convert_pdf_to_jpeg(self):
"""That should raise a IOError.
"""
from PIL import Image
image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
u'image_test', u'logo.pdf')

self.assertRaises(IOError,
lambda: tasks.convert_image_to_jpeg(image_path))


class CreatePreferredImageFileTests(TestCase):
def setUp(self):
self.unlink_registry = []

def tearDown(self):
for path in self.unlink_registry:
os.unlink(path)

def test_create_alt_image_from_tiff(self):
asset = modelfactories.ArticleAssetFactory.create()
self.assertEquals(False, bool(asset.preferred_alt_file))

tasks.create_preferred_image_file(asset.pk)

modified_asset = models.ArticleAsset.objects.get(pk=asset.pk)
self.assertEquals(True, bool(modified_asset.preferred_alt_file))

# evitar que arquivos temporários sobrem no disco.
self.unlink_registry.append(asset.file.path)
self.unlink_registry.append(modified_asset.preferred_alt_file.path)

def test_alt_to_tiff_is_jpeg(self):
from PIL import Image
asset = modelfactories.ArticleAssetFactory.create()
tasks.create_preferred_image_file(asset.pk)
modified_asset = models.ArticleAsset.objects.get(pk=asset.pk)
self.assertEquals('jpeg',
Image.open(modified_asset.preferred_alt_file.path).format.lower())

# evitar que arquivos temporários sobrem no disco.
self.unlink_registry.append(asset.file.path)
self.unlink_registry.append(modified_asset.preferred_alt_file.path)

def test_non_images_raise_ValueError(self):
from django.core.files.base import File
SAMPLE_PDF = open(os.path.join(
os.path.dirname(os.path.abspath(__file__)),
u'image_test',
u'logo.pdf'))

asset = modelfactories.ArticleAssetFactory.build()
asset.file = File(SAMPLE_PDF)

self.assertRaises(ValueError,
lambda: tasks.create_preferred_image_file(asset.pk))

def test_images_other_than_tiff_raise_ValueError(self):
from django.core.files.base import File
SAMPLE_PDF = open(os.path.join(
os.path.dirname(os.path.abspath(__file__)),
u'image_test',
u'cover.gif'))

asset = modelfactories.ArticleAssetFactory.build()
asset.file = File(SAMPLE_PDF)

self.assertRaises(ValueError,
lambda: tasks.create_preferred_image_file(asset.pk))

def test_missing_asset_raise_ValueError(self):
self.assertRaises(ValueError,
lambda: tasks.create_preferred_image_file(999999))

0 comments on commit 503bebb

Please sign in to comment.