Skip to content

Commit 0144ef3

Browse files
committed
Add description field on group, enable most group info page views to
work without a group type to pave the way for /group/<acronym>/ URLs, make charter page more generic, enable it to handle descriptions and walk over all personnel instead of picking out specific types of roles, add a redirect view on /group/<acronym>/ to lead to either documents/ or charter/, add simple group.features() class to be able to condition on group features rather than specific group types, adjust group pages menu accordingly - Legacy-Id: 7758
1 parent 43e472b commit 0144ef3

File tree

14 files changed

+721
-139
lines changed

14 files changed

+721
-139
lines changed

ietf/community/display.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def get_value(self, document, raw=False):
8888
if raw:
8989
return document.group.acronym
9090
else:
91-
return '<a href="%s">%s</a>' % (urlreverse('group_docs', kwargs=dict(group_type=document.group.type_id, acronym=document.group.acronym)), document.group.acronym) if (document.group and document.group.acronym != 'none') else ''
91+
return '<a href="%s">%s</a>' % (urlreverse('group_home', kwargs=dict(group_type=document.group.type_id, acronym=document.group.acronym)), document.group.acronym) if (document.group and document.group.acronym != 'none') else ''
9292

9393

9494
class ADField(DisplayField):

ietf/doc/templatetags/ietf_filters.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ def expand_comma(value):
2222
long comma-separated lists."""
2323
return value.replace(",", ", ")
2424

25-
@register.filter(name='format_charter')
26-
def format_charter(value):
27-
return value.replace("\n\n", "</p><p>").replace("\n","<br/>\n")
28-
2925
@register.filter
3026
def indent(value, numspaces=2):
3127
replacement = "\n" + " " * int(numspaces)

ietf/doc/views_doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ def document_main(request, name, rev=None):
243243
elif group.type_id in ("rg", "wg"):
244244
submission = "%s %s" % (group.acronym, group.type)
245245
if group.type_id == "wg":
246-
submission = "<a href=\"%s\">%s</a>" % (urlreverse("group_docs", kwargs=dict(group_type=doc.group.type_id, acronym=doc.group.acronym)), submission)
246+
submission = "<a href=\"%s\">%s</a>" % (urlreverse("group_home", kwargs=dict(group_type=doc.group.type_id, acronym=doc.group.acronym)), submission)
247247
if doc.stream_id and doc.get_state_slug("draft-stream-%s" % doc.stream_id) == "c-adopt":
248248
submission = "candidate for %s" % submission
249249

ietf/group/features.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class GroupFeatures(object):
2+
"""Configuration of group pages and processes to have this collected
3+
in one place rather than scattered over the group page views."""
4+
5+
has_milestones = False
6+
has_chartering_process = False
7+
has_documents = False
8+
customize_workflow = False
9+
default_tab = "group_charter"
10+
11+
def __init__(self, group):
12+
if group.type_id in ("wg", "rg"):
13+
self.has_milestones = True
14+
self.has_chartering_process = True
15+
self.has_documents = True
16+
self.customize_workflow = True
17+
self.default_tab = "group_docs"
18+
elif group.type_id in ("team",):
19+
self.has_documents = True

ietf/group/info.py

Lines changed: 133 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@
3838

3939
from django.shortcuts import get_object_or_404, render
4040
from django.template.loader import render_to_string
41-
from django.http import HttpResponse, Http404
41+
from django.http import HttpResponse, Http404, HttpResponseRedirect
4242
from django.conf import settings
4343
from django.core.urlresolvers import reverse as urlreverse
4444
from django.views.decorators.cache import cache_page
4545
from django.db.models import Q
46+
from django.utils.safestring import mark_safe
4647

4748
from ietf.doc.views_search import SearchForm, retrieve_search_results
4849
from ietf.doc.models import State, DocAlias, RelatedDocument
@@ -58,10 +59,31 @@ def roles(group, role_name):
5859

5960
def 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-
8595
def 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+
116152
def 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 &raquo;"), group.list_archive))
276+
if group.has_tools_page():
277+
entries.append((mark_safe("Tools %s Page &raquo;" % 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

Comments
 (0)