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
from django.urls import reverse
from django.utils.functional import cached_property
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
COVER_FILENAME = 'cover.jpg'
EXIF_ORIENTATION = {
1: 0,
2: 0,
3: 180,
4: 180,
5: 90,
6: 90,
7: 270,
8: 270,
}
COVER_FILENAME = 'cover.jpg'
logger = logging.getLogger(__name__)
......@@ -37,18 +28,6 @@ def photo_uploadto(instance, 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):
album = models.ForeignKey(
......@@ -98,7 +77,7 @@ class Photo(models.Model):
image_path, _ext = os.path.splitext(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(settings.PHOTO_UPLOAD_SIZE, Image.ANTIALIAS)
......
from django.db.models import When, Value, BooleanField, ExpressionWrapper, Q, \
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):
if request.member and request.member.current_membership is not None:
......@@ -15,24 +41,21 @@ def is_album_accessible(request, album):
# Annotate the albums which are accessible by the user
def get_annotated_accessible_albums(request, albums):
if request.member and request.member.current_membership is None:
# The user is currently not a member
# so only show photos that were made during their membership
if request.member and request.member.current_membership is not None:
albums = albums.annotate(accessible=ExpressionWrapper(
Value(True), output_field=BooleanField()))
elif request.member and request.member.current_membership is None:
albums_filter = Q(pk__in=[])
for membership in request.member.membership_set.all():
if membership.until is not None:
albums_filter |= (Q(date__gte=membership.since) & Q(
date__lte=membership.until))
else:
albums_filter |= (Q(date__gte=membership.since))
albums = albums.annotate(accessible=Case(
When(albums_filter, then=Value(True)),
default=Value(False),
output_field=BooleanField()))
else:
# The user is currently a member, so show all albums
albums = albums.annotate(accessible=ExpressionWrapper(
Value(True), output_field=BooleanField()))
Value(False), output_field=BooleanField()))
return albums
from io import BytesIO
from zipfile import ZipFile
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 import services
from .models import Photo, Album
from .models import determine_rotation
from members.models import Member
from photos.models import Album, Photo
def create_zip(photos):
......@@ -21,27 +16,6 @@ def create_zip(photos):
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):
"""Tests album uploads in the admin."""
......@@ -140,47 +114,3 @@ class AlbumUploadTest(TestCase):
follow=True)
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()
def setUp(self):
self.client = Client()
self.album = Album.objects.create(
title_en='test_album',
title_nl='test_album',
date=date(year=2017, month=9, day=5),
slug='test_album')
with open("photos/fixtures/thom_assessor.png", "rb") as f:
fi = SimpleUploadedFile(
name='photo.png',
content=f.read(),
content_type='image/png')
self.photo = Photo(album=self.album, file=fi)
self.photo.save()
def test_download(self):
self.client.force_login(self.member)
response = self.client.get(reverse(
'photos:download', args=(self.photo,)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'image/jpeg')
def test_logged_out(self):
response = self.client.get(reverse(
'photos:download', args=(self.photo,)))
self.assertEqual(response.status_code, 302)
class SharedDownloadTest(TestCase):
fixtures = ['members.json']
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
cls.client = Client()