Skip to content

Commit 9eacdbf

Browse files
committed
Merged in ^/personal/henrik/6.21.1-biophoto@11313, with work from rjsparks@nostrum.com and henrik@levkowetz.com which provides support for profile biography and photo.
- Legacy-Id: 11314
2 parents e62c3ac + 4cb87b5 commit 9eacdbf

44 files changed

Lines changed: 1197 additions & 171 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docker/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ RUN apt-get update && apt-get install -qy \
3838
gawk \
3939
ipython \
4040
less \
41+
libjpeg8-dev \
4142
libmysqlclient-dev \
4243
libsvn1/wheezy-backports \
4344
libxml2-dev \

docker/settings_local.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@
1717
IDSUBMIT_REPOSITORY_PATH = "test/id/"
1818
IDSUBMIT_STAGING_PATH = "test/staging/"
1919
INTERNET_DRAFT_ARCHIVE_DIR = "test/archive/"
20+
21+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
22+
23+
MEDIA_ROOT = BASE_DIR + '/media/'
24+
MEDIA_URL = '/media/'
25+
26+
PHOTOS_DIRNAME = 'photos'
27+
PHOTOS_DIR = MEDIA_ROOT + PHOTOS_DIRNAME

ietf/bin/2016-05-25-collect-photos

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
#!/usr/bin/env python
2+
3+
import os, re, sys, shutil, pathlib
4+
from collections import namedtuple
5+
from PIL import Image
6+
7+
# boilerplate
8+
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
9+
sys.path = [ basedir ] + sys.path
10+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ietf.settings")
11+
12+
import django
13+
django.setup()
14+
15+
from django.conf import settings
16+
from django.utils.text import slugify
17+
18+
import debug
19+
20+
from ietf.group.models import Role, Person
21+
from ietf.person.name import name_parts
22+
23+
old_images_dir = ''
24+
new_images_dir = settings.PHOTOS_DIR
25+
26+
if not os.path.exists(new_images_dir):
27+
print("New images directory does not exist: %s" % new_images_dir)
28+
sys.exit(1)
29+
30+
old_image_files = []
31+
for dir in settings.OLD_PHOTO_DIRS:
32+
if not os.path.exists(dir):
33+
print("Old images directory does not exist: %s" % dir)
34+
sys.exit(1)
35+
old_image_files += [ f for f in pathlib.Path(dir).iterdir() if f.is_file() and f.suffix.lower() in ['.jpg', '.jpeg', '.png'] ]
36+
37+
photo = namedtuple('photo', ['path', 'name', 'ext', 'width', 'height', 'time', 'file'])
38+
39+
old_images = []
40+
for f in old_image_files:
41+
path = str(f)
42+
img = Image.open(path)
43+
old_images.append(photo(path, f.stem.decode('utf8'), f.suffix, img.size[0], img.size[1], f.stat().st_mtime, f))
44+
45+
# Fix up some names:
46+
47+
def fix_missing_surnames(images):
48+
replacement = {
49+
"alissa": "alissa-cooper",
50+
"alissa1": "alissa-cooper",
51+
"andrei": "andrei-robachevsky",
52+
"bernard": "bernard-aboba",
53+
"danny": "danny-mcpherson",
54+
"danny1": "danny-mcpherson",
55+
"dthaler": "dave-thaler",
56+
"eliot-mug": "eliot-lear",
57+
"erik.nordmark-300": "erik-nordmark",
58+
"hannes": "hannes-tschofenig",
59+
"hildebrand": "joe-hildebrand",
60+
"housley": "russ-housley",
61+
"jariarkko": "jari-arkko",
62+
"joel": "joel-jaeggli",
63+
"joel1": "joel-jaeggli",
64+
"joel2": "joel-jaeggli",
65+
"jon": "jon-peterson",
66+
"kessens": "david-kessens",
67+
"klensin": "john-klensin",
68+
"lars": "lars-eggert",
69+
"lars1": "lars-eggert",
70+
"marc_blanchet": "marc-blanchet",
71+
"marcelo": "marcelo-bagnulo",
72+
"olaf": "olaf-kolkman",
73+
"olaf1": "olaf-kolkman",
74+
"ross": "ross-callon",
75+
"spencer": "spencer-dawkins",
76+
"spencer1": "spencer-dawkins",
77+
"vijay": "vijay-gurbani",
78+
"xing": "xing-li",
79+
}
80+
81+
for i in range(len(images)):
82+
img = images[i]
83+
name = re.sub('-[0-9]+x[0-9]+', '', img.name)
84+
if '/iab/' in img.path and name in replacement:
85+
name = replacement[name]
86+
images[i] = photo(img.path, name, img.ext, img.width, img.height, img.time, img.file)
87+
88+
89+
fix_missing_surnames(old_images)
90+
91+
interesting_persons = set(Person.objects.all())
92+
93+
name_alias = {
94+
u"andy": [u"andrew", ],
95+
u"ben": [u"benjamin", ],
96+
u"bill": [u"william", ],
97+
u"bob": [u"robert", ],
98+
u"chris": [u"christopher", u"christian"],
99+
u"dan": [u"daniel", ],
100+
u"dave": [u"david", ],
101+
u"dick": [u"richard", ],
102+
u"fred": [u"alfred", ],
103+
u"geoff": [u"geoffrey", ],
104+
u"jake": [u"jacob", ],
105+
u"jerry": [u"gerald", ],
106+
u"jim": [u"james", ],
107+
u"joe": [u"joseph", ],
108+
u"jon": [u"jonathan", ],
109+
u"mike": [u"michael", ],
110+
u"ned": [u"edward", ],
111+
u"pete": [u"peter", ],
112+
u"ron": [u"ronald", ],
113+
u"russ": [u"russel", ],
114+
u"steve": [u"stephen", ],
115+
u"ted": [u"edward", ],
116+
u"terry": [u"terence", ],
117+
u"tom": [u"thomas", ],
118+
u"wes": [u"wesley", ],
119+
u"will": [u"william", ],
120+
121+
u"beth": [u"elizabeth", ],
122+
u"liz": [u"elizabeth", ],
123+
u"lynn": [u"carolyn", ],
124+
u"pat": [u"patricia", u"patrick", ],
125+
u"sue": [u"susan", ],
126+
}
127+
# Add lookups from long to short, from the initial set
128+
for key,value in name_alias.items():
129+
for item in value:
130+
if item in name_alias:
131+
name_alias[item] += [ key ];
132+
else:
133+
name_alias[item] = [ key ];
134+
135+
exceptions = {
136+
'Aboba' : 'aboba-bernard',
137+
'Bernardos' : 'cano-carlos',
138+
'Bormann' : 'bormann-carsten',
139+
'Hinden' : 'hinden-bob',
140+
'Hutton' : 'hutton-andy',
141+
'Narten' : 'narten-thomas', # but there's no picture of him
142+
'O\'Donoghue' : 'odonoghue-karen',
143+
'Przygienda' : 'przygienda-antoni',
144+
'Salowey' : 'salowey-joe',
145+
'Gunter Van de Velde' : 'vandevelde-gunter',
146+
'Eric Vyncke' : 'vynke-eric',
147+
'Zuniga' : 'zuniga-carlos-juan',
148+
'Zhen Cao' : 'zhen-cao',
149+
'Jamal Hadi Salim': 'hadi-salim-jamal',
150+
}
151+
152+
# Manually copied Bo Burman and Thubert Pascal from wg/photos/
153+
# Manually copied Victor Pascual (main image, not thumb) from wg/
154+
# Manually copied Eric Vync?ke (main image, not thumb) from wg/photos/
155+
# Manually copied Danial King (main image, not thumb) from wg/photos/
156+
# Manually copied the thumb (not labelled as such) for Tianran Zhou as both the main and thumb image from wg/photos/
157+
158+
processed_files = []
159+
160+
for person in sorted(list(interesting_persons),key=lambda x:x.last_name()+x.ascii):
161+
substr_pattern = None
162+
for exception in exceptions:
163+
if exception in person.ascii:
164+
substr_pattern = exceptions[exception]
165+
break
166+
if not person.ascii.strip():
167+
print(" Setting person.ascii for %s" % person.name)
168+
person.ascii = person.name.encode('ascii', errors='replace').decode('ascii')
169+
170+
_, first, _, last, _ = person.ascii_parts()
171+
first = first.lower()
172+
last = last. lower()
173+
if not substr_pattern:
174+
substr_pattern = slugify("%s %s" % (last, first))
175+
176+
if first in ['', '<>'] or last in ['', '<>']:
177+
continue
178+
179+
#debug.show('1, substr_pattern')
180+
181+
candidates = [x for x in old_images if x.name.lower().startswith(substr_pattern)]
182+
# Also check the reverse the name order (necessary for Deng Hui, for instance)
183+
substr_pattern = slugify("%s %s" % (first, last))
184+
#debug.show('2, substr_pattern')
185+
prev_len = len(candidates)
186+
candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
187+
if prev_len < len(candidates) :
188+
print(" Found match with '%s %s' for '%s %s'" % (last, first, first, last, ))
189+
# If no joy, try a short name
190+
if first in name_alias:
191+
prev_len = len(candidates)
192+
for alias in name_alias[first]:
193+
substr_pattern = slugify("%s %s" % (last, alias))
194+
#debug.show('3, substr_pattern')
195+
candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
196+
if prev_len < len(candidates):
197+
print(" Found match with '%s %s' for '%s %s'" % (alias, last, first, last, ))
198+
199+
200+
# # If still no joy, try with Person.plain_name() (necessary for Donald Eastlake)
201+
# if not candidates:
202+
# prefix, first, middle, last, suffix = person.name_parts()
203+
# name_parts = person.plain_name().lower().split()
204+
#
205+
# substr_pattern = u'-'.join(name_parts[-1:]+name_parts[0:1])
206+
# candidates = [x for x in old_images if x.name.lower().startswith(substr_pattern)]
207+
# # If no joy, try a short name
208+
# if not candidates and first in name_alias:
209+
# prev_len = len(candidates)
210+
# for alias in name_alias[first]:
211+
# substr_pattern = u'-'.join(name_parts[-1:]+[alias])
212+
# candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
213+
# if prev_len < len(candidates) :
214+
# print(" Used '%s %s' instead of '%s %s'" % (alias, last, first, last, ))
215+
216+
# # Fixup for other exceptional cases
217+
# if person.ascii=="David Oran":
218+
# candidates = ['oran-dave-th.jpg','oran-david.jpg']
219+
#
220+
# if person.ascii=="Susan Hares":
221+
# candidates = ['hares-sue-th.jpg','hares-susan.JPG']
222+
#
223+
# if person.ascii=="Mahesh Jethanandani":
224+
# candidates = ['Mahesh-Jethanandani-th.jpg','Jethanandani-Mahesh.jpg']
225+
226+
processed_files += [ c.path for c in candidates ]
227+
228+
# We now have a list of candidate photos.
229+
# * Consider anything less than 200x200 a thumbnail
230+
# * For the full photo, sort by size (width) and time
231+
# * For the thumbnail:
232+
# - first look for a square photo less than 200x200
233+
# - if none found, then for the first in the sorted list less than 200x200
234+
# - if none found, then the smallest photo
235+
if candidates:
236+
candidates.sort(key=lambda x: "%04d-%d" % (x.width, x.time))
237+
iesg_cand = [ c for c in candidates if '/iesg/' in c.path ]
238+
iab_cand = [ c for c in candidates if '/iab/' in c.path ]
239+
if iesg_cand:
240+
full = iesg_cand[-1]
241+
thumb = iesg_cand[-1]
242+
elif iab_cand:
243+
full = iab_cand[-1]
244+
thumb = iab_cand[0]
245+
else:
246+
full = candidates[-1]
247+
thumbs = [ c for c in candidates if c.width==c.height and c.width <= 200 ]
248+
if not thumbs:
249+
thumbs = [ c for c in candidates if c.width==c.height ]
250+
if not thumbs:
251+
thumbs = [ c for c in candidates if c.width <= 200 ]
252+
if not thumbs:
253+
thumbs = candidates[:1]
254+
thumb = thumbs[-1]
255+
candidates = [ thumb, full ]
256+
257+
# At this point we either have no candidates or two. If two, the first will be the thumb
258+
259+
def copy(old, new):
260+
if not os.path.exists(new):
261+
print("Copying "+old+" to "+new)
262+
shutil.copy(old, new)
263+
shutil.copystat(old, new)
264+
265+
assert(len(candidates) in [0,2])
266+
if len(candidates)==2:
267+
thumb, full = candidates
268+
269+
new_name = person.photo_name(thumb=False)+full.ext.lower()
270+
new_thumb_name = person.photo_name(thumb=True)+thumb.ext.lower()
271+
272+
copy( full.path, os.path.join(new_images_dir,new_name) )
273+
274+
#
275+
copy( thumb.path, os.path.join(new_images_dir,new_thumb_name) )
276+
277+
278+
print("")
279+
not_processed = 0
280+
for file in old_image_files:
281+
if ( file.is_file()
282+
and not file.suffix.lower() in ['.txt', '.lck', '.html',]
283+
and not file.name.startswith('index.')
284+
and not file.name.startswith('milestoneupdate')
285+
and not file.name.startswith('nopicture')
286+
and not file.name.startswith('robots.txt')
287+
):
288+
if not str(file).decode('utf8') in processed_files:
289+
not_processed += 1
290+
print(u"Not processed: "+str(file).decode('utf8'))
291+
print("")
292+
print("Not processed: %s files" % not_processed)

ietf/doc/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,10 @@ def get_absolute_url(self):
389389
else:
390390
filename = self.external_url
391391
if meeting.type_id == 'ietf':
392-
url = '%sproceedings/%s/%s/%s' % (settings.MEDIA_URL,meeting.number,self.type_id,filename)
392+
url = '%sproceedings/%s/%s/%s' % (settings.IETF_HOST_URL,meeting.number,self.type_id,filename)
393393
elif meeting.type_id == 'interim':
394394
url = "%sproceedings/interim/%s/%s/%s/%s" % (
395-
settings.MEDIA_URL,
395+
settings.IETF_HOST_URL,
396396
meeting.date.strftime('%Y/%m/%d'),
397397
session.group.acronym,
398398
self.type_id,

ietf/group/tests_info.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from django.template.defaultfilters import urlize
2020

2121
from ietf.doc.models import Document, DocAlias, DocEvent, State
22-
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions
22+
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, Role
2323
from ietf.group.utils import save_group_in_history, setup_default_community_list_for_group
2424
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
2525
from ietf.person.models import Person, Email
@@ -350,6 +350,33 @@ def test_feed(self):
350350
self.assertTrue(de.desc in unicontent(r))
351351

352352

353+
def test_chair_photos(self):
354+
make_test_data()
355+
url = urlreverse("ietf.group.views.chair_photos", kwargs={'group_type':'wg'})
356+
r = self.client.get(url)
357+
self.assertEqual(r.status_code, 200)
358+
q = PyQuery(r.content)
359+
chairs = Role.objects.filter(group__type='wg', group__state='active', name_id='chair')
360+
self.assertEqual(len(q('div.photo-thumbnail img')), chairs.count())
361+
362+
def test_wg_photos(self):
363+
make_test_data()
364+
url = urlreverse("ietf.group.views.group_photos", kwargs={'group_type':'wg', 'acronym':'mars'})
365+
r = self.client.get(url)
366+
self.assertEqual(r.status_code, 200)
367+
q = PyQuery(r.content)
368+
roles = Role.objects.filter(group__acronym='mars')
369+
self.assertEqual(len(q('div.photo-thumbnail img')), roles.count())
370+
371+
def test_group_photos(self):
372+
make_test_data()
373+
url = urlreverse("ietf.group.views.group_photos", kwargs={'acronym':'iab'})
374+
r = self.client.get(url)
375+
self.assertEqual(r.status_code, 200)
376+
q = PyQuery(r.content)
377+
roles = Role.objects.filter(group__acronym='iab')
378+
self.assertEqual(len(q('div.photo-thumbnail img')), roles.count())
379+
353380
class GroupEditTests(TestCase):
354381
def setUp(self):
355382
self.charter_dir = os.path.abspath("tmp-charter-dir")

ietf/group/urls_info.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
(r'^chartering/create/$', RedirectView.as_view(url='/group/chartering/create/%(group_type)s/')),
1919
(r'^bofs/$', views.bofs),
2020
(r'^email-aliases/$', 'ietf.group.views.email_aliases'),
21-
(r'^bofs/create/$', views_edit.edit, {'action': "create"}, "bof_create"),
21+
(r'^bofs/create/$', views_edit.edit, {'action': "create", "group_state":"bof"}, "bof_create"),
22+
(r'^photos/$', views.chair_photos),
2223
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/', include('ietf.group.urls_info_details')),
2324
)

ietf/group/urls_info_details.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.conf.urls import patterns, url
22
from django.views.generic import RedirectView
3+
import views
34

45
urlpatterns = patterns('',
56
(r'^$', 'ietf.group.views.group_home', None, "group_home"),
@@ -28,5 +29,6 @@
2829
(r'^materials/new/$', 'ietf.doc.views_material.choose_material_type'),
2930
(r'^materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
3031
(r'^archives/$', 'ietf.group.views.derived_archives'),
32+
(r'^photos/$', views.group_photos),
3133
url(r'^email-aliases/$', RedirectView.as_view(pattern_name='ietf.group.views.email',permanent=False),name='old_group_email_aliases'),
3234
)

0 commit comments

Comments
 (0)