3838
3939from django .shortcuts import get_object_or_404 , render
4040from django .template .loader import render_to_string
41- from django .http import HttpResponse , Http404
41+ from django .http import HttpResponse , Http404 , HttpResponseRedirect
4242from django .conf import settings
4343from django .core .urlresolvers import reverse as urlreverse
4444from django .views .decorators .cache import cache_page
4545from django .db .models import Q
46+ from django .utils .safestring import mark_safe
4647
4748from ietf .doc .views_search import SearchForm , retrieve_search_results
4849from ietf .doc .models import State , DocAlias , RelatedDocument
@@ -58,10 +59,31 @@ def roles(group, role_name):
5859
5960def fill_in_charter_info (group , include_drafts = False ):
6061 group .areadirector = group .ad .role_email ("ad" , group .parent ) if group .ad else None
61- group .chairs = roles (group , "chair" )
62- group .techadvisors = roles (group , "techadv" )
63- group .editors = roles (group , "editor" )
64- group .secretaries = roles (group , "secr" )
62+
63+ personnel = {}
64+ for r in Role .objects .filter (group = group ).select_related ("email" , "person" , "name" ):
65+ if r .name_id not in personnel :
66+ personnel [r .name_id ] = []
67+ personnel [r .name_id ].append (r )
68+
69+ if group .parent and group .parent .type_id == "area" and group .ad and "ad" not in personnel :
70+ ad_roles = list (Role .objects .filter (group = group .parent , name = "ad" , person = group .ad ))
71+ if ad_roles :
72+ personnel ["ad" ] = ad_roles
73+
74+ group .personnel = []
75+ for role_name_slug , roles in personnel .iteritems ():
76+ label = roles [0 ].name .name
77+ if len (roles ) > 1 :
78+ if label .endswith ("y" ):
79+ label = label [:- 1 ] + "ies"
80+ else :
81+ label += "s"
82+
83+ group .personnel .append ((role_name_slug , label , roles ))
84+
85+ group .personnel .sort (key = lambda t : t [2 ][0 ].name .order )
86+
6587 milestone_state = "charter" if group .state_id == "proposed" else "active"
6688 group .milestones = group .groupmilestone_set .filter (state = milestone_state ).order_by ('due' )
6789
@@ -70,18 +92,6 @@ def fill_in_charter_info(group, include_drafts=False):
7092 else :
7193 group .charter_text = u"Not chartered yet."
7294
73- if include_drafts :
74- aliases = DocAlias .objects .filter (document__type = "draft" , document__group = group ).select_related ('document' ).order_by ("name" )
75- group .drafts = []
76- group .rfcs = []
77- for a in aliases :
78- if a .name .startswith ("draft" ):
79- group .drafts .append (a )
80- else :
81- group .rfcs .append (a )
82- a .rel = RelatedDocument .objects .filter (source = a .document ).distinct ()
83- a .invrel = RelatedDocument .objects .filter (target = a ).distinct ()
84-
8595def extract_last_name (role ):
8696 return role .person .name_parts ()[3 ]
8797
@@ -113,6 +123,32 @@ def wg_summary_acronym(request, group_type):
113123 'groups' : groups },
114124 content_type = 'text/plain; charset=UTF-8' )
115125
126+ def fill_in_wg_roles (group ):
127+ def get_roles (slug , default ):
128+ for role_slug , label , roles in group .personnel :
129+ if slug == role_slug :
130+ return roles
131+ return default
132+
133+ group .chairs = get_roles ("chair" , [])
134+ ads = get_roles ("ad" , [])
135+ group .areadirector = ads [0 ] if ads else None
136+ group .techadvisors = get_roles ("techadv" , [])
137+ group .editors = get_roles ("editor" , [])
138+ group .secretaries = get_roles ("secr" , [])
139+
140+ def fill_in_wg_drafts (group ):
141+ aliases = DocAlias .objects .filter (document__type = "draft" , document__group = group ).select_related ('document' ).order_by ("name" )
142+ group .drafts = []
143+ group .rfcs = []
144+ for a in aliases :
145+ if a .name .startswith ("draft" ):
146+ group .drafts .append (a )
147+ else :
148+ group .rfcs .append (a )
149+ a .rel = RelatedDocument .objects .filter (source = a .document ).distinct ()
150+ a .invrel = RelatedDocument .objects .filter (target = a ).distinct ()
151+
116152def wg_charters (request , group_type ):
117153 if group_type != "wg" :
118154 raise Http404
@@ -121,7 +157,9 @@ def wg_charters(request, group_type):
121157 area .ads = sorted (roles (area , "ad" ), key = extract_last_name )
122158 area .groups = Group .objects .filter (parent = area , type = "wg" , state = "active" ).order_by ("name" )
123159 for group in area .groups :
124- fill_in_charter_info (group , include_drafts = True )
160+ fill_in_charter_info (group )
161+ fill_in_wg_roles (group )
162+ fill_in_wg_drafts (group )
125163 group .area = area
126164 return render (request , 'group/1wg-charters.txt' ,
127165 { 'areas' : areas },
@@ -137,7 +175,9 @@ def wg_charters_by_acronym(request, group_type):
137175
138176 groups = Group .objects .filter (type = "wg" , state = "active" ).exclude (parent = None ).order_by ("acronym" )
139177 for group in groups :
140- fill_in_charter_info (group , include_drafts = True )
178+ fill_in_charter_info (group )
179+ fill_in_wg_roles (group )
180+ fill_in_wg_drafts (group )
141181 group .area = areas .get (group .parent_id )
142182 return render (request , 'group/1wg-charters-by-acronym.txt' ,
143183 { 'groups' : groups },
@@ -216,29 +256,52 @@ def concluded_groups(request):
216256 return render (request , 'group/concluded_groups.html' ,
217257 dict (group_types = group_types ))
218258
219- def construct_group_menu_context (request , group , selected , others ):
259+ def construct_group_menu_context (request , group , selected , group_type , others ):
220260 """Return context with info for the group menu filled in."""
261+ kwargs = dict (acronym = group .acronym )
262+ if group_type :
263+ kwargs ["group_type" ] = group_type
264+
265+ # entries
266+ entries = []
267+ if group .features ().has_documents :
268+ entries .append (("Documents" , urlreverse ("ietf.group.info.group_documents" , kwargs = kwargs )))
269+ entries .append (("Charter" , urlreverse ("ietf.group.info.group_charter" , kwargs = kwargs )))
270+ entries .append (("History" , urlreverse ("ietf.group.info.history" , kwargs = kwargs )))
271+ if group .features ().has_documents :
272+ entries .append (("Dependency Graph" , urlreverse ("ietf.group.info.dependencies_pdf" , kwargs = kwargs )))
273+
274+ if group .list_archive .startswith ("http:" ) or group .list_archive .startswith ("https:" ) or group .list_archive .startswith ("ftp:" ):
275+ entries .append ((mark_safe ("List Archive »" ), group .list_archive ))
276+ if group .has_tools_page ():
277+ entries .append ((mark_safe ("Tools %s Page »" % group .type .name ), "https://tools.ietf.org/%s/%s/" % (group .type_id , group .acronym )))
278+
279+
280+ # actions
221281 actions = []
222282
223283 is_chair = group .has_role (request .user , "chair" )
224284 can_manage = can_manage_group_type (request .user , group .type_id )
225285
226- if group .state_id != "proposed" and (is_chair or can_manage ):
227- actions .append ((u"Add or edit milestones" , urlreverse ("group_edit_milestones" , kwargs = dict (group_type = group .type_id , acronym = group .acronym ))))
286+ if group .features ().has_milestones :
287+ if group .state_id != "proposed" and (is_chair or can_manage ):
288+ actions .append ((u"Add or edit milestones" , urlreverse ("group_edit_milestones" , kwargs = kwargs )))
228289
229290 if group .state_id != "conclude" and can_manage :
230- actions .append ((u"Edit group" , urlreverse ("group_edit" , kwargs = dict ( group_type = group . type_id , acronym = group . acronym ) )))
291+ actions .append ((u"Edit group" , urlreverse ("group_edit" , kwargs = kwargs )))
231292
232- if is_chair or can_manage :
233- actions .append ((u"Customize workflow" , urlreverse ("ietf.group.edit.customize_workflow" , kwargs = dict ( group_type = group . type_id , acronym = group . acronym ) )))
293+ if group . features (). customize_workflow and ( is_chair or can_manage ) :
294+ actions .append ((u"Customize workflow" , urlreverse ("ietf.group.edit.customize_workflow" , kwargs = kwargs )))
234295
235296 if group .state_id in ("active" , "dormant" ) and can_manage :
236- actions .append ((u"Request closing group" , urlreverse ("ietf.group.edit.conclude" , kwargs = dict ( group_type = group . type_id , acronym = group . acronym ) )))
297+ actions .append ((u"Request closing group" , urlreverse ("ietf.group.edit.conclude" , kwargs = kwargs )))
237298
238299 d = {
239300 "group" : group ,
240- "selected" : selected ,
301+ "selected_menu_entry" : selected ,
302+ "menu_entries" : entries ,
241303 "menu_actions" : actions ,
304+ "group_type" : group_type ,
242305 }
243306
244307 d .update (others )
@@ -278,22 +341,37 @@ def search_for_group_documents(group):
278341
279342 return docs , meta , docs_related , meta_related
280343
281- def group_documents (request , group_type , acronym ):
282- group = get_object_or_404 (Group , type = group_type , acronym = acronym )
344+ def get_group_or_404 (acronym , group_type ):
345+ """Helper to overcome the schism between group-type prefixed URLs and generic."""
346+ possible_groups = Group .objects .all ()
347+ if group_type :
348+ possible_groups = possible_groups .filter (type = group_type )
349+
350+ return get_object_or_404 (possible_groups , acronym = acronym )
351+
352+ def group_home (request , acronym , group_type = None ):
353+ group = get_group_or_404 (acronym , group_type )
354+ kwargs = dict (acronym = group .acronym )
355+ if group_type :
356+ kwargs ["group_type" ] = group_type
357+ return HttpResponseRedirect (urlreverse (group .features ().default_tab , kwargs = kwargs ))
358+
359+ def group_documents (request , acronym , group_type = None ):
360+ group = get_group_or_404 (acronym , group_type )
283361
284362 docs , meta , docs_related , meta_related = search_for_group_documents (group )
285363
286364 return render (request , 'group/group_documents.html' ,
287- construct_group_menu_context (request , group , "documents" , {
365+ construct_group_menu_context (request , group , "documents" , group_type , {
288366 'docs' : docs ,
289367 'meta' : meta ,
290368 'docs_related' : docs_related ,
291369 'meta_related' : meta_related
292370 }))
293371
294- def group_documents_txt (request , group_type , acronym ):
372+ def group_documents_txt (request , acronym , group_type = None ):
295373 """Return tabulator-separated rows with documents for group."""
296- group = get_object_or_404 ( Group , type = group_type , acronym = acronym )
374+ group = get_group_or_404 ( acronym , group_type )
297375
298376 docs , meta , docs_related , meta_related = search_for_group_documents (group )
299377
@@ -316,39 +394,45 @@ def group_documents_txt(request, group_type, acronym):
316394 return HttpResponse (u"\n " .join (rows ), content_type = 'text/plain; charset=UTF-8' )
317395
318396
319- def group_charter (request , group_type , acronym ):
320- group = get_object_or_404 ( Group , type = group_type , acronym = acronym )
397+ def group_charter (request , acronym , group_type = None ):
398+ group = get_group_or_404 ( acronym , group_type )
321399
322- fill_in_charter_info (group , include_drafts = False )
323- group .delegates = roles (group , "delegate" )
400+ fill_in_charter_info (group )
324401
325402 e = group .latest_event (type__in = ("changed_state" , "requested_close" ,))
326403 requested_close = group .state_id != "conclude" and e and e .type == "requested_close"
327404
328- long_group_types = dict (
329- wg = "Working Group" ,
330- rg = "Research Group" ,
331- )
405+ verbose_group_types = {
406+ "wg" : "Working Group" ,
407+ "rg" : "Research Group" ,
408+ "team" : "Team" ,
409+ }
332410
333411 can_manage = can_manage_group_type (request .user , group .type_id )
334412
413+ if group .features ().has_chartering_process :
414+ description = group .charter_text
415+ else :
416+ description = group .description or "No description entered yet."
417+
335418 return render (request , 'group/group_charter.html' ,
336- construct_group_menu_context (request , group , "charter" , {
419+ construct_group_menu_context (request , group , "charter" , group_type , {
337420 "milestones_in_review" : group .groupmilestone_set .filter (state = "review" ),
338421 "milestone_reviewer" : milestone_reviewer_for_group_type (group_type ),
339422 "requested_close" : requested_close ,
340- "long_group_type" : long_group_types .get (group_type , "Group" ),
423+ "verbose_group_type" : verbose_group_types .get (group . type_id , "Group" ),
341424 "can_manage" : can_manage ,
425+ "description" : description ,
342426 }))
343427
344428
345- def history (request , group_type , acronym ):
346- group = get_object_or_404 ( Group , acronym = acronym )
429+ def history (request , acronym , group_type = None ):
430+ group = get_group_or_404 ( acronym , group_type )
347431
348432 events = group .groupevent_set .all ().select_related ('by' ).order_by ('-time' , '-id' )
349433
350434 return render (request , 'group/history.html' ,
351- construct_group_menu_context (request , group , "history" , {
435+ construct_group_menu_context (request , group , "history" , group_type , {
352436 "events" : events ,
353437 }))
354438
@@ -470,19 +554,17 @@ def make_dot(group):
470554 dict ( nodes = nodes , edges = edges )
471555 )
472556
473- def dependencies_dot (request , group_type , acronym ):
474-
475- group = get_object_or_404 (Group , acronym = acronym )
557+ def dependencies_dot (request , acronym , group_type = None ):
558+ group = get_group_or_404 (acronym , group_type )
476559
477560 return HttpResponse (make_dot (group ),
478561 content_type = 'text/plain; charset=UTF-8'
479562 )
480563
481564@cache_page ( 60 * 60 )
482- def dependencies_pdf (request , group_type , acronym ):
565+ def dependencies_pdf (request , acronym , group_type = None ):
566+ group = get_group_or_404 (acronym , group_type )
483567
484- group = get_object_or_404 (Group , acronym = acronym )
485-
486568 dothandle ,dotname = mkstemp ()
487569 os .close (dothandle )
488570 dotfile = open (dotname ,"w" )
0 commit comments