Commit b88388d6 authored by Tom van Bussel's avatar Tom van Bussel

Split photos tests and added new tests

parent d370b125
...@@ -9,22 +9,13 @@ from django.db import models ...@@ -9,22 +9,13 @@ from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from PIL import Image, ExifTags from PIL import Image
from photos.services import photo_determine_rotation
from utils.translation import ModelTranslateMeta, MultilingualField from utils.translation import ModelTranslateMeta, MultilingualField
COVER_FILENAME = 'cover.jpg'
EXIF_ORIENTATION = { COVER_FILENAME = 'cover.jpg'
1: 0,
2: 0,
3: 180,
4: 180,
5: 90,
6: 90,
7: 270,
8: 270,
}
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -37,18 +28,6 @@ def photo_uploadto(instance, filename): ...@@ -37,18 +28,6 @@ def photo_uploadto(instance, filename):
return os.path.join(Album.photosdir, instance.album.dirname, new_filename) return os.path.join(Album.photosdir, instance.album.dirname, new_filename)
def determine_rotation(pil_image):
if isinstance(pil_image, JpegImageFile) and pil_image._getexif():
exif = {
ExifTags.TAGS[k]: v
for k, v in pil_image._getexif().items()
if k in ExifTags.TAGS
}
if exif.get('Orientation'):
return EXIF_ORIENTATION[exif.get('Orientation')]
return 0
class Photo(models.Model): class Photo(models.Model):
album = models.ForeignKey( album = models.ForeignKey(
...@@ -98,7 +77,7 @@ class Photo(models.Model): ...@@ -98,7 +77,7 @@ class Photo(models.Model):
image_path, _ext = os.path.splitext(image_path) image_path, _ext = os.path.splitext(image_path)
image_path = "{}.jpg".format(image_path) image_path = "{}.jpg".format(image_path)
self.rotation = determine_rotation(image) self.rotation = photo_determine_rotation(image)
# Image.thumbnail does not upscale an image that is smaller # Image.thumbnail does not upscale an image that is smaller
image.thumbnail(settings.PHOTO_UPLOAD_SIZE, Image.ANTIALIAS) image.thumbnail(settings.PHOTO_UPLOAD_SIZE, Image.ANTIALIAS)
......
from django.db.models import When, Value, BooleanField, ExpressionWrapper, Q, \ from django.db.models import When, Value, BooleanField, ExpressionWrapper, Q, \
Case Case
from PIL.JpegImagePlugin import JpegImageFile
from PIL import ExifTags
def photo_determine_rotation(pil_image):
EXIF_ORIENTATION = {
1: 0,
2: 0,
3: 180,
4: 180,
5: 90,
6: 90,
7: 270,
8: 270,
}
if isinstance(pil_image, JpegImageFile) and pil_image._getexif():
exif = {
ExifTags.TAGS[k]: v
for k, v in pil_image._getexif().items()
if k in ExifTags.TAGS
}
if exif.get('Orientation'):
return EXIF_ORIENTATION[exif.get('Orientation')]
return 0
def is_album_accessible(request, album): def is_album_accessible(request, album):
if request.member and request.member.current_membership is not None: if request.member and request.member.current_membership is not None:
...@@ -15,24 +41,21 @@ def is_album_accessible(request, album): ...@@ -15,24 +41,21 @@ def is_album_accessible(request, album):
# Annotate the albums which are accessible by the user # Annotate the albums which are accessible by the user
def get_annotated_accessible_albums(request, albums): def get_annotated_accessible_albums(request, albums):
if request.member and request.member.current_membership is None: if request.member and request.member.current_membership is not None:
# The user is currently not a member albums = albums.annotate(accessible=ExpressionWrapper(
# so only show photos that were made during their membership Value(True), output_field=BooleanField()))
elif request.member and request.member.current_membership is None:
albums_filter = Q(pk__in=[]) albums_filter = Q(pk__in=[])
for membership in request.member.membership_set.all(): for membership in request.member.membership_set.all():
if membership.until is not None: albums_filter |= (Q(date__gte=membership.since) & Q(
albums_filter |= (Q(date__gte=membership.since) & Q( date__lte=membership.until))
date__lte=membership.until))
else:
albums_filter |= (Q(date__gte=membership.since))
albums = albums.annotate(accessible=Case( albums = albums.annotate(accessible=Case(
When(albums_filter, then=Value(True)), When(albums_filter, then=Value(True)),
default=Value(False), default=Value(False),
output_field=BooleanField())) output_field=BooleanField()))
else: else:
# The user is currently a member, so show all albums
albums = albums.annotate(accessible=ExpressionWrapper( albums = albums.annotate(accessible=ExpressionWrapper(
Value(True), output_field=BooleanField())) Value(False), output_field=BooleanField()))
return albums return albums
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from PIL import Image
from django.test import Client, TestCase, RequestFactory from django.test import Client, TestCase, RequestFactory
from django.utils.datetime_safe import datetime
from freezegun import freeze_time
from members.models import Member, Membership from members.models import Member
from photos import services from photos.models import Album, Photo
from .models import Photo, Album
from .models import determine_rotation
def create_zip(photos): def create_zip(photos):
...@@ -21,27 +16,6 @@ def create_zip(photos): ...@@ -21,27 +16,6 @@ def create_zip(photos):
return output_file return output_file
class PhotoRotationTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
def setUp(self):
self.client = Client()
self.client.force_login(self.member)
def test_rotation_detection(self):
orientations = [0, 0, 180, 180, 90, 90, 270, 270]
for i in range(1, 9):
with self.subTest(orentation=i):
with open('photos/fixtures/poker_{}.jpg'.format(i), 'rb') as f:
rot = determine_rotation(Image.open(f))
self.assertEqual(orientations[i - 1], rot)
class AlbumUploadTest(TestCase): class AlbumUploadTest(TestCase):
"""Tests album uploads in the admin.""" """Tests album uploads in the admin."""
...@@ -140,47 +114,3 @@ class AlbumUploadTest(TestCase): ...@@ -140,47 +114,3 @@ class AlbumUploadTest(TestCase):
follow=True) follow=True)
self.assertEqual(Photo.objects.first().rotation, 90) self.assertEqual(Photo.objects.first().rotation, 90)
class ServicesTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(username="testuser").first()
def setUp(self):
self.rf = RequestFactory()
@freeze_time('2017-01-01')
def test_is_album_accessible(self):
request = self.rf.get('/')
request.member = None
album = Album(date=datetime(year=2017, month=1, day=1))
with self.subTest(membership=None):
self.assertFalse(services.is_album_accessible(request, album))
request.member = self.member
with self.subTest(membership=None):
self.assertFalse(services.is_album_accessible(request, album))
membership = Membership.objects.create(
user=self.member, type=Membership.MEMBER,
since=datetime(year=2016, month=1, day=1))
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertTrue(services.is_album_accessible(request, album))
membership.until = datetime(year=2016, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertFalse(services.is_album_accessible(request, album))
membership.until = datetime(year=2017, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertTrue(services.is_album_accessible(request, album))
from PIL import Image
from django.test import Client, TestCase, RequestFactory
from django.utils.datetime_safe import datetime
from freezegun import freeze_time
from members.models import Member, Membership
from photos.models import Album
from photos.services import (is_album_accessible, photo_determine_rotation,
get_annotated_accessible_albums)
class IsAlbumAccesibleTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(username="testuser").first()
def setUp(self):
self.rf = RequestFactory()
@freeze_time('2017-01-01')
def test_is_album_accessible(self):
request = self.rf.get('/')
request.member = None
album = Album(date=datetime(year=2017, month=1, day=1))
with self.subTest(membership=None):
self.assertFalse(is_album_accessible(request, album))
request.member = self.member
with self.subTest(membership=None):
self.assertFalse(is_album_accessible(request, album))
membership = Membership.objects.create(
user=self.member, type=Membership.MEMBER,
since=datetime(year=2016, month=1, day=1))
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertTrue(is_album_accessible(request, album))
membership.until = datetime(year=2016, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertFalse(is_album_accessible(request, album))
membership.until = datetime(year=2017, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
self.assertTrue(is_album_accessible(request, album))
class GetAnnotatedAccessibleAlbumsTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(username="testuser").first()
def setUp(self):
self.rf = RequestFactory()
@freeze_time('2017-01-01')
def test_get_annotated_accessible_albums(self):
request = self.rf.get('/')
request.member = None
album = Album(date=datetime(year=2017, month=1, day=1))
album.save()
self.assertEqual(Album.objects.count(), 1)
with self.subTest(membership=None):
albums = Album.objects.all()
albums = get_annotated_accessible_albums(request, albums)
for album in albums:
self.assertFalse(album.accessible)
request.member = self.member
with self.subTest(membership=None):
albums = Album.objects.all()
albums = get_annotated_accessible_albums(request, albums)
for album in albums:
self.assertFalse(album.accessible)
membership = Membership.objects.create(
user=self.member, type=Membership.MEMBER,
since=datetime(year=2016, month=1, day=1))
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
albums = Album.objects.all()
albums = get_annotated_accessible_albums(request, albums)
for album in albums:
self.assertTrue(album.accessible)
membership.until = datetime(year=2016, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
albums = Album.objects.all()
albums = get_annotated_accessible_albums(request, albums)
for album in albums:
self.assertFalse(album.accessible)
membership.until = datetime(year=2017, month=1, day=1)
membership.save()
with self.subTest(membership_since=membership.since,
membership_until=membership.until):
albums = Album.objects.all()
albums = get_annotated_accessible_albums(request, albums)
for album in albums:
self.assertTrue(album.accessible)
class DetermineRotationTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
def setUp(self):
self.client = Client()
self.client.force_login(self.member)
def test_rotation_detection(self):
orientations = [0, 0, 180, 180, 90, 90, 270, 270]
for i in range(1, 9):
with self.subTest(orentation=i):
with open('photos/fixtures/poker_{}.jpg'.format(i), 'rb') as f:
rot = photo_determine_rotation(Image.open(f))
self.assertEqual(orientations[i - 1], rot)
from datetime import date
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client, TestCase
from django.urls import reverse
from members.models import Member, Membership
from photos.models import Album, Photo
class AlbumIndexTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
cls.client = Client()
def setUp(self):
self.client.force_login(self.member)
def test_index(self):
with self.subTest(album_objects__count=Album.objects.count()):
response = self.client.get(reverse('photos:index'))
self.assertEqual(response.status_code, 200)
for i in range(12):
Album.objects.create(
title_en='test_album_a%d' % i,
title_nl='test_album_a%d' % i,
date=date(year=2018, month=9, day=5),
slug='test_album_a%d' % i)
with self.subTest(album_objects__count=Album.objects.count()):
response = self.client.get(reverse('photos:index'))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(1, 2))
for i in range(12):
Album.objects.create(
title_en='test_album_b%d' % i,
title_nl='test_album_b%d' % i,
date=date(year=2018, month=9, day=5),
slug='test_album_b%d' % i)
with self.subTest(album_objects__count=Album.objects.count()):
response = self.client.get(reverse('photos:index'))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(1, 3))
for i in range(72):
Album.objects.create(
title_en='test_album_c%d' % i,
title_nl='test_album_c%d' % i,
date=date(year=2018, month=9, day=5),
slug='test_album_c%d' % i)
with self.subTest(album_objects__count=Album.objects.count()):
response = self.client.get(reverse('photos:index'))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(1, 6))
def test_empty_page(self):
Album.objects.create(
title_en='test_album',
title_nl='test_album',
date=date(year=2018, month=9, day=5),
slug='test_album')
response = self.client.get(reverse('photos:index') + '?page=5')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['page_range'], range(1, 2))
def test_many_pages(self):
for i in range(120):
Album.objects.create(
title_en='test_album_%d' % i,
title_nl='test_album_%d' % i,
date=date(year=2018, month=9, day=5),
slug='test_album_%d' % i)
with self.subTest(page=1):
response = self.client.get(reverse('photos:index') + '?page=1')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(1, 6))
with self.subTest(page=4):
response = self.client.get(reverse('photos:index') + '?page=4')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(2, 7))
with self.subTest(page=9):
response = self.client.get(reverse('photos:index') + '?page=9')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['albums']), 12)
self.assertEqual(response.context['page_range'], range(6, 11))
class AlbumTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(username="testuser").first()
cls.client = Client()
def setUp(self):
self.album = Album.objects.create(
title_en='test_album',
title_nl='test_album',
date=date(year=2017, month=9, day=5),
slug='test_album')
self.client.force_login(self.member)
def test_get(self):
Membership.objects.create(
type=Membership.MEMBER,
user=self.member,
since=date(year=2015, month=1, day=1),
until=None)
for i in range(10):
with open("photos/fixtures/thom_assessor.png", "rb") as f:
fi = SimpleUploadedFile(name='photo{}.png'.format(i),
content=f.read(),
content_type='image/png')
photo = Photo(album=self.album, file=fi)
photo.save()
response = self.client.get(reverse(
'photos:album', args=(self.album.slug,)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['album'], self.album)
self.assertEqual(len(response.context['photos']), 10)
def test_unaccessible(self):
Membership.objects.create(
type=Membership.MEMBER,
user=self.member,
since=date(year=2016, month=1, day=1),
until=date(year=2018, month=1, day=1))
with self.subTest():
self.album.date=date(year=2017, month=1, day=1)
self.album.save()
response = self.client.get(reverse(
'photos:album', args=(self.album.slug,)))
self.assertEqual(response.status_code, 200)
with self.subTest():
self.album.date=date(year=2018, month=9, day=5)
self.album.save()
response = self.client.get(reverse(
'photos:album', args=(self.album.slug,)))
self.assertEqual(response.status_code, 404)
class SharedAlbumTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(username="testuser").first()
cls.client = Client()
def setUp(self):
self.album = Album.objects.create(
title_en='test_album',
title_nl='test_album',
date=date(year=2017, month=9, day=5),
shareable=True,
slug='test_album')
def test_get(self):
for i in range(10):
with open("photos/fixtures/thom_assessor.png", "rb") as f:
fi = SimpleUploadedFile(name='photo{}.png'.format(i),
content=f.read(),
content_type='image/png')
photo = Photo(album=self.album, file=fi)
photo.save()
response = self.client.get(reverse(
'photos:shared_album',
args=(self.album.slug, self.album.access_token,)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['album'], self.album)
self.assertEqual(len(response.context['photos']), 10)
class DownloadTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()