Skip to content

Commit

Permalink
feat: Image dimensions update management command (#1434)
Browse files Browse the repository at this point in the history
* first draft: image dimensions update managemenet command

* fix: import corrected

* fix: enable for bitmap images

* fix: self>image

* test ok

* catch file not fonund and corrupt images

* fix flake8 and silly error

* add svg test

* add svg test
  • Loading branch information
benzkji authored Nov 8, 2023
1 parent 8039f55 commit 2e37d58
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 1 deletion.
49 changes: 49 additions & 0 deletions filer/management/commands/filer_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from django.core.management.base import BaseCommand
from django.utils.module_loading import import_string

from PIL import UnidentifiedImageError

from filer import settings as filer_settings


Expand Down Expand Up @@ -41,6 +43,13 @@ def add_arguments(self, parser):
default=False,
help="Delete references in database if files are missing in media folder.",
)
parser.add_argument(
'--image-dimensions',
action='store_true',
dest='image_dimensions',
default=False,
help="Look for images without dimensions set, set them accordingly.",
)
parser.add_argument(
'--noinput',
'--no-input',
Expand Down Expand Up @@ -72,6 +81,8 @@ def handle(self, *args, **options):
self.stdout.write("Aborted: Delete orphaned files from storage.")
return
self.verify_storages(options)
if options['image_dimensions']:
self.image_dimensions(options)

def verify_references(self, options):
from filer.models.filemodels import File
Expand Down Expand Up @@ -112,3 +123,41 @@ def walk(prefix):
filer_public = filer_settings.FILER_STORAGES['public']['main']
storage = import_string(filer_public['ENGINE'])()
walk(filer_public['UPLOAD_TO_PREFIX'])

def image_dimensions(self, options):
from django.db.models import Q

import easy_thumbnails
from easy_thumbnails.VIL import Image as VILImage

from filer.models.imagemodels import Image
from filer.utils.compatibility import PILImage

no_dimensions = Image.objects.filter(
Q(_width=0) | Q(_width__isnull=True)
)
self.stdout.write(f"trying to set dimensions on {no_dimensions.count()} files")
for image in no_dimensions:
if image.file_ptr:
file_holder = image.file_ptr
else:
file_holder = image
try:
imgfile = file_holder.file
imgfile.seek(0)
except (FileNotFoundError):
pass
else:
if image.file.name.lower().endswith('.svg'):
with VILImage.load(imgfile) as vil_image:
# invalid svg doesnt throw errors
image._width, image._height = vil_image.size
else:
try:
with PILImage.open(imgfile) as pil_image:
image._width, image._height = pil_image.size
image._transparent = easy_thumbnails.utils.is_transparent(pil_image)
except UnidentifiedImageError:
continue
image.save()
return
91 changes: 90 additions & 1 deletion tests/test_filer_check.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import shutil
from io import StringIO
from io import BytesIO, StringIO

from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command
Expand All @@ -9,10 +9,21 @@

from filer import settings as filer_settings
from filer.models.filemodels import File
from filer.models.imagemodels import Image
from tests.helpers import create_image


class FilerCheckTestCase(TestCase):

svg_file_string = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "
http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" width="50" height="50" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900"
stroke="#004400"/>
{}
</svg>"""

def setUp(self):
# ensure that filer_public directory is empty from previous tests
storage = import_string(filer_settings.FILER_STORAGES['public']['main']['ENGINE'])()
Expand Down Expand Up @@ -67,3 +78,81 @@ def test_delete_orphans(self):

call_command('filer_check', delete_orphans=True, interactive=False, verbosity=0)
self.assertFalse(os.path.exists(orphan_file))

def test_image_dimensions_corrupted_file(self):
original_filename = 'testimage.jpg'
file_obj = SimpleUploadedFile(
name=original_filename,
# corrupted!
content=create_image().tobytes(),
content_type='image/jpeg')
self.filer_image = Image.objects.create(
file=file_obj,
original_filename=original_filename)

self.filer_image._width = 0
self.filer_image.save()
call_command('filer_check', image_dimensions=True)

def test_image_dimensions_file_not_found(self):
self.filer_image = Image.objects.create(
file="123.jpg",
original_filename="123.jpg")
call_command('filer_check', image_dimensions=True)
self.filer_image.refresh_from_db()

def test_image_dimensions(self):

original_filename = 'testimage.jpg'
with BytesIO() as jpg:
create_image().save(jpg, format='JPEG')
jpg.seek(0)
file_obj = SimpleUploadedFile(
name=original_filename,
content=jpg.read(),
content_type='image/jpeg')
self.filer_image = Image.objects.create(
file=file_obj,
original_filename=original_filename)

self.filer_image._width = 0
self.filer_image.save()

call_command('filer_check', image_dimensions=True)
self.filer_image.refresh_from_db()
self.assertGreater(self.filer_image._width, 0)

def test_image_dimensions_invalid_svg(self):

original_filename = 'test.svg'
svg_file = bytes("<asdva>" + self.svg_file_string, "utf-8")
file_obj = SimpleUploadedFile(
name=original_filename,
content=svg_file,
content_type='image/svg+xml')
self.filer_image = Image.objects.create(
file=file_obj,
original_filename=original_filename)

self.filer_image._width = 0
self.filer_image.save()
call_command('filer_check', image_dimensions=True)

def test_image_dimensions_svg(self):

original_filename = 'test.svg'
svg_file = bytes(self.svg_file_string, "utf-8")
file_obj = SimpleUploadedFile(
name=original_filename,
content=svg_file,
content_type='image/svg+xml')
self.filer_image = Image.objects.create(
file=file_obj,
original_filename=original_filename)

self.filer_image._width = 0
self.filer_image.save()

call_command('filer_check', image_dimensions=True)
self.filer_image.refresh_from_db()
self.assertGreater(self.filer_image._width, 0)

0 comments on commit 2e37d58

Please sign in to comment.