Skip to content

Commit

Permalink
Resize photo before saving
Browse files Browse the repository at this point in the history
  • Loading branch information
VirginiaDooley committed Apr 30, 2023
1 parent 7b54321 commit dbbcb43
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 6 deletions.
16 changes: 11 additions & 5 deletions ynr/apps/moderation_queue/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.urls import reverse
from moderation_queue.helpers import convert_image_to_png, rotate_photo
from moderation_queue.helpers import (
convert_image_to_png,
resize_photo,
rotate_photo,
)
from people.forms.forms import StrippedCharField
from PIL import Image as PILImage

Expand Down Expand Up @@ -55,11 +59,13 @@ def clean(self):

def save(self, commit):
"""
Before saving, rotate the image and convert the image to a PNG.
This is done while the image is still an InMemoryUpload object
Before saving, resize and rotate the image as needed
and convert the image to a PNG. This is done while the
image is still an InMemoryUpload object.
"""
photo = rotate_photo(image=self.instance.image)
# pass a PIL image to the convert_image_to_png function

photo = resize_photo(self.instance.image)
photo = rotate_photo(image=photo)
png_image = convert_image_to_png(photo)
filename = self.instance.image.name
extension = filename.split(".")[-1]
Expand Down
15 changes: 14 additions & 1 deletion ynr/apps/moderation_queue/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ def image_form_valid_response(request, person, image_form):
)


def resize_photo(photo):
if photo.size > 5000000:
image_path = photo.path

photo = PillowImage.open(photo)
# resize the photo to less than or equal to 5MB and return it
resized_photo = photo.resize(
(photo.width // 2, photo.height // 2), PillowImage.ANTIALIAS
)
photo = resized_photo.save(image_path)
return photo


def convert_image_to_png(photo):
# Some uploaded images are CYMK, which gives you an error when
# you try to write them as PNG, so convert to RGBA (this is
Expand All @@ -54,7 +67,7 @@ def convert_image_to_png(photo):
# If the photo is not already a PillowImage object
# coming from the form, then we need to
# open it as a PillowImage object before
# converting
# converting it to RGBA.
if not isinstance(photo, PillowImage.Image):
photo = PillowImage.open(photo)
converted = photo.convert("RGBA")
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions ynr/apps/moderation_queue/tests/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
QUEUED_IMAGE_FILENAME = abspath(
join(dirname(__file__), "media/queued-images/example-queued-image.png")
)

XL_IMAGE_FILENAME = abspath(
join(dirname(__file__), "example-queued-image-xl.jpg")
)
39 changes: 39 additions & 0 deletions ynr/apps/moderation_queue/tests/test_upload_photo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from os.path import dirname, join, realpath
from shutil import rmtree
from urllib.parse import urlsplit
Expand All @@ -19,6 +20,7 @@
from moderation_queue.tests.paths import (
EXAMPLE_IMAGE_FILENAME,
ROTATED_IMAGE_FILENAME,
XL_IMAGE_FILENAME,
)
from people.tests.factories import PersonFactory
from PIL import Image as PillowImage
Expand All @@ -34,6 +36,7 @@ class PhotoUploadImageTests(UK2015ExamplesMixin, WebTest):

example_image_filename = EXAMPLE_IMAGE_FILENAME
rotated_image_filename = ROTATED_IMAGE_FILENAME
xl_image_filename = XL_IMAGE_FILENAME

@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -96,6 +99,25 @@ def successful_get_rotated_image(self, *all_mock_requests, **kwargs):
),
)

def successful_get_oversized_image(self, *all_mock_requests, **kwargs):
content_type = kwargs.get("content_type", "image/jpeg")
headers = {"content-type": content_type}
with open(self.xl_image_filename, "rb") as image:
image_data = image.read()
for mock_method in self.get_and_head_methods(*all_mock_requests):
setattr(
mock_method,
"return_value",
Mock(
status_code=200,
headers=headers,
# The chunk size is larger than the example
# image, so we don't need to worry about
# returning subsequent chunks.
iter_content=lambda **kwargs: [image_data],
),
)

def test_queued_images_form_visibility(self):
QueuedImage.objects.create(
person_id=2009,
Expand Down Expand Up @@ -160,6 +182,23 @@ def test_shows_photo_policy_text_in_photo_upload_page(self):
response = self.app.get(upload_form_url, user=self.test_upload_user)
self.assertContains(response, "Photo policy")

def test_resize_image(self, *all_mock_requests):
# Test that the image is less than or equal to 5MB after
# upload and before saving to the database.
image_size = os.path.getsize(self.xl_image_filename)
self.assertGreater(image_size, 5000000)

upload_form_file = reverse("photo-upload", kwargs={"person_id": "2009"})
self.form_page_response = self.app.get(
upload_form_file, user=self.test_upload_user
)
self.successful_get_oversized_image(*all_mock_requests)
self.valid_form().submit()

queued_image = QueuedImage.objects.filter(person_id=2009).last()
image_size = queued_image.image.size
self.assertLessEqual(image_size, 5000000)

def test_rotate_image_from_file_upload(self, *all_mock_requests):
exif = PillowImage.open(ROTATED_IMAGE_FILENAME)._getexif()
# assert exif[274] == 1 before upload
Expand Down

0 comments on commit dbbcb43

Please sign in to comment.