From 07be1f85161498bf432689457d9dad9b561bf04e Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Thu, 10 Sep 2020 21:41:01 +0000 Subject: [PATCH] Added a view to retrieve profile photos, in order to support Meetecho's need for such, at /person/{email}/photo. The default is to supply images with 80px width. Image scaling is available using the same query argument as Gravatar: ?size=200 or ?s=200 for 200-pixel wide images. - Legacy-Id: 18483 --- ietf/person/tests.py | 23 ++++++++++++++++++++++- ietf/person/urls.py | 1 + ietf/person/views.py | 27 ++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/ietf/person/tests.py b/ietf/person/tests.py index 684936c2e..5ba877df7 100644 --- a/ietf/person/tests.py +++ b/ietf/person/tests.py @@ -4,8 +4,10 @@ import datetime +from io import StringIO, BytesIO +from PIL import Image from pyquery import PyQuery -from io import StringIO + from django.http import HttpRequest from django.urls import reverse as urlreverse @@ -71,6 +73,25 @@ class PersonTests(TestCase): r = self.client.get(photo_url) self.assertEqual(r.status_code, 200) + def test_person_photo(self): + person = PersonFactory(with_bio=True) + + self.assertTrue(person.photo is not None) + self.assertTrue(person.photo.name is not None) + + url = urlreverse("ietf.person.views.photo", kwargs={ "email_or_name": person.email()}) + r = self.client.get(url) + self.assertEqual(r['Content-Type'], 'image/jpg') + self.assertEqual(r.status_code, 200) + img = Image.open(BytesIO(r.content)) + self.assertEqual(img.width, 80) + + r = self.client.get(url+'?size=200') + self.assertEqual(r['Content-Type'], 'image/jpg') + self.assertEqual(r.status_code, 200) + img = Image.open(BytesIO(r.content)) + self.assertEqual(img.width, 200) + def test_name_methods(self): person = PersonFactory(name="Dr. Jens F. Möller", ) diff --git a/ietf/person/urls.py b/ietf/person/urls.py index e9b68d9ed..5d5d113e7 100644 --- a/ietf/person/urls.py +++ b/ietf/person/urls.py @@ -6,4 +6,5 @@ urlpatterns = [ url(r'^search/(?P(person|email))/$', views.ajax_select2_search), url(r'^(?P[a-z0-9]+).json$', ajax.person_json), url(r'^(?P[^/]+)$', views.profile), + url(r'^(?P[^/]+)/photo/?$', views.photo), ] diff --git a/ietf/person/views.py b/ietf/person/views.py index 0fb967743..e57199a4d 100644 --- a/ietf/person/views.py +++ b/ietf/person/views.py @@ -3,7 +3,8 @@ import datetime -from io import StringIO +from io import StringIO, BytesIO +from PIL import Image from django.contrib import messages from django.db.models import Q @@ -78,6 +79,30 @@ def profile(request, email_or_name): return render(request, 'person/profile.html', {'persons': persons, 'today':datetime.date.today()}) +def photo(request, email_or_name): + if '@' in email_or_name: + persons = [ get_object_or_404(Email, address=email_or_name).person, ] + else: + aliases = Alias.objects.filter(name=email_or_name) + persons = list(set([ a.person for a in aliases ])) + if not persons: + raise Http404("No such person") + if len(persons) > 1: + return HttpResponse(r"\r\n".join([p.email() for p in persons]), status=300) + person = persons[0] + if not person.photo: + raise Http404("No photo found") + size = request.GET.get('s') or request.GET.get('size', '80') + if not size.isdigit(): + return HttpResponse("Size must be integer", status=400) + size = int(size) + img = Image.open(person.photo) + img = img.resize((size, img.height*size//img.width)) + bytes = BytesIO() + img.save(bytes, format='JPEG') + return HttpResponse(bytes.getvalue(), content_type='image/jpg') + + @role_required("Secretariat") def merge(request): form = MergeForm()