Skip to content

Commit e0ea8b0

Browse files
committed
Added programs to the datatracker. Added use of restructuredtext for group about pages. Generalized several places where code handles different group types. Improved testing of group about. Commit ready for merge.
- Legacy-Id: 12722
1 parent 93b1ba1 commit e0ea8b0

16 files changed

Lines changed: 900 additions & 70 deletions

File tree

ietf/community/tests.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from ietf.utils.test_data import make_test_data
1717
from ietf.utils.test_utils import login_testing_unauthorized, TestCase
1818
from ietf.utils.mail import outbox
19+
from ietf.group.factories import GroupFactory
20+
from ietf.person.factories import PersonFactory
1921

2022
class CommunityListTests(TestCase):
2123
def test_rule_matching(self):
@@ -158,6 +160,19 @@ def test_manage_group_list(self):
158160
r = self.client.get(url)
159161
self.assertEqual(r.status_code, 200)
160162

163+
# Verify GET also works with non-WG and RG groups
164+
for gtype in ['area','program']:
165+
g = GroupFactory.create(type_id=gtype)
166+
# make sure the group's features have been initialized to improve coverage
167+
_ = g.features # pyflakes:ignore
168+
p = PersonFactory()
169+
g.role_set.create(name_id={'area':'ad','program':'lead'}[gtype],person=p, email=p.email())
170+
url = urlreverse(ietf.community.views.manage_list, kwargs={ "acronym": g.acronym })
171+
setup_default_community_list_for_group(g)
172+
self.client.login(username=p.user.username,password=p.user.username+"+password")
173+
r = self.client.get(url)
174+
self.assertEqual(r.status_code, 200)
175+
161176
def test_track_untrack_document(self):
162177
draft = make_test_data()
163178

ietf/community/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ def can_manage_community_list(user, clist):
4949
return Role.objects.filter(name__slug='ad', person__user=user, group=clist.group).exists()
5050
elif clist.group.type_id in ('wg', 'rg'):
5151
return Role.objects.filter(name__slug='chair', person__user=user, group=clist.group).exists()
52+
elif clist.group.type_id in ('program'):
53+
return Role.objects.filter(name__slug='lead', person__user=user, group=clist.group).exists()
5254

5355
return False
5456

ietf/doc/templatetags/active_groups_menu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@register.simple_tag
99
def active_groups_menu():
10-
parents = GroupTypeName.objects.filter(slug__in=['ag','area','team','dir'])
10+
parents = GroupTypeName.objects.filter(slug__in=['ag','area','team','dir','program'])
1111
for p in parents:
1212
p.menu_url = '/%s/'%p.slug
1313
return render_to_string('base/menu_active_groups.html', { 'parents': parents })

ietf/group/features.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ class GroupFeatures(object):
55
has_milestones = False
66
has_chartering_process = False
77
has_documents = False # i.e. drafts/RFCs
8+
has_dependencies = False # Do dependency graphs for group documents make sense?
89
has_materials = False
910
has_reviews = False
11+
has_default_jabber = False
1012
customize_workflow = False
1113
about_page = "group_about"
1214
default_tab = about_page
@@ -19,10 +21,18 @@ def __init__(self, group):
1921
self.has_chartering_process = True
2022
self.has_documents = True
2123
self.customize_workflow = True
24+
self.has_default_jabber = True
25+
self.has_dependencies = True
2226
self.default_tab = "group_docs"
2327
elif group.type_id in ("team",):
2428
self.has_materials = True
2529
self.default_tab = "group_about"
30+
elif group.type_id in ("program",):
31+
self.has_documents = True
32+
self.has_milestones = True
33+
self.admin_roles = ["lead",]
34+
elif group.type_id == "dir":
35+
self.admin_roles = ["chair", "secr"]
2636

2737
if self.has_chartering_process:
2838
self.about_page = "group_charter"
@@ -33,5 +43,3 @@ def __init__(self, group):
3343
import ietf.group.views
3444
self.default_tab = ietf.group.views_review.review_requests
3545

36-
if group.type_id == "dir":
37-
self.admin_roles = ["chair", "secr"]

ietf/group/migrations/0010_iab_programs.py

Lines changed: 689 additions & 0 deletions
Large diffs are not rendered by default.

ietf/group/milestones.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
9898
if milestone_set == "current":
9999
needs_review = True
100100
else:
101-
return HttpResponseForbidden("You are not chair of this group.")
101+
return HttpResponseForbidden("You are not authorized to edit the milestones of this group.")
102102

103103
if milestone_set == "current":
104104
title = "Edit milestones for %s %s" % (group.acronym, group.type.name)
@@ -330,9 +330,9 @@ def reset_charter_milestones(request, group_type, acronym):
330330
raise Http404
331331

332332
can_manage = can_manage_group(request.user, group)
333-
is_chair = group.has_role(request.user, "chair")
334-
if (not can_manage) and (not is_chair):
335-
return HttpResponseForbidden("You are not chair of this group.")
333+
is_authority = group.has_role(request.user, group.features.admin_roles)
334+
if (not can_manage) and (not is_authority):
335+
return HttpResponseForbidden("You are not authorized to change the milestones for this group.")
336336

337337
current_milestones = group.groupmilestone_set.filter(state="active")
338338
charter_milestones = group.groupmilestone_set.filter(state="charter")

ietf/group/tests_info.py

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
from ietf.meeting.factories import SessionFactory
2929
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
3030
from ietf.person.models import Person, Email
31+
from ietf.person.factories import PersonFactory
3132
from ietf.utils.mail import outbox, empty_outbox
32-
from ietf.utils.test_data import make_test_data, create_person, make_review_data
33+
from ietf.utils.test_data import make_test_data, make_review_data
3334
from ietf.utils.test_utils import login_testing_unauthorized, TestCase, unicontent, reload_db_objects
3435

3536
def group_urlreverse_list(group, viewname):
@@ -62,30 +63,15 @@ def test_active_groups(self):
6263
self.assertTrue(group.name in unicontent(r))
6364
self.assertTrue(group.ad_role().person.plain_name() in unicontent(r))
6465

65-
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type="rg"))
66-
r = self.client.get(url)
67-
self.assertEqual(r.status_code, 200)
68-
self.assertTrue('Active research groups' in unicontent(r))
69-
70-
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type="area"))
71-
r = self.client.get(url)
72-
self.assertEqual(r.status_code, 200)
73-
self.assertTrue("Far Future (farfut)" in unicontent(r))
74-
75-
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type="ag"))
76-
r = self.client.get(url)
77-
self.assertEqual(r.status_code, 200)
78-
self.assertTrue("Active area groups" in unicontent(r))
79-
80-
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type="dir"))
81-
r = self.client.get(url)
82-
self.assertEqual(r.status_code, 200)
83-
self.assertTrue("Active directorates" in unicontent(r))
84-
85-
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type="team"))
86-
r = self.client.get(url)
87-
self.assertEqual(r.status_code, 200)
88-
self.assertTrue("Active teams" in unicontent(r))
66+
for t in ('rg','area','ag','dir','team','program'):
67+
g = GroupFactory.create(type_id=t,state_id='active')
68+
if t=='dir':
69+
g.parent = GroupFactory.create(type_id='area',state_id='active')
70+
g.save()
71+
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type=t))
72+
r = self.client.get(url)
73+
self.assertEqual(r.status_code, 200)
74+
self.assertTrue(g.acronym in unicontent(r))
8975

9076
url = urlreverse('ietf.group.views.active_groups', kwargs=dict())
9177
r = self.client.get(url)
@@ -269,42 +255,72 @@ def test_group_charter(self):
269255

270256
def test_group_about(self):
271257

272-
def verify_cannot_edit_group(url, username):
273-
self.client.login(username=username, password=username+"+password")
274-
r = self.client.get(url)
275-
self.assertEqual(r.status_code, 403)
258+
make_test_data()
276259

277-
def verify_can_edit_group(url, username):
278-
self.client.login(username=username, password=username+"+password")
279-
r = self.client.get(url)
280-
self.assertEqual(r.status_code, 200)
260+
p = PersonFactory(user__username='iab-member')
261+
Group.objects.get(acronym='iab').role_set.create(name_id='member',person=p,email=p.email())
281262

282-
make_test_data()
283-
group = Group.objects.create(
284-
type_id="team",
285-
acronym="testteam",
286-
name="Test Team",
287-
description="The test team is testing.",
288-
state_id="active",
289-
parent = Group.objects.get(acronym="farfut"),
290-
)
291-
create_person(group, "chair", name="Testteam Chairman", username="teamchairman")
263+
interesting_users = [ 'plain','iana','iab-chair','irtf-chair', 'marschairman', 'teamchairman','ad', 'iab-member', 'secretary', ]
292264

293-
for url in [group.about_url(),] + group_urlreverse_list(group, 'ietf.group.views.group_about'):
294-
url = group.about_url()
295-
r = self.client.get(url)
296-
self.assertEqual(r.status_code, 200)
297-
self.assertTrue(group.name in unicontent(r))
298-
self.assertTrue(group.acronym in unicontent(r))
299-
self.assertTrue(group.description in unicontent(r))
265+
can_edit = {
266+
'wg' : ['secretary','ad'],
267+
'rg' : ['secretary','irtf-chair'],
268+
'ag' : ['secretary', ],
269+
'team' : ['secretary',], # The code currently doesn't let ads edit teams or directorates. Maybe it should.
270+
'dir' : ['secretary',],
271+
'program' : ['secretary', 'iab-member'],
272+
}
300273

301-
for url in group_urlreverse_list(group, 'ietf.group.views_edit.edit'):
274+
def setup_role(group, role_id):
275+
p = PersonFactory(user__username="%s_%s"%(group.acronym,role_id))
276+
group.role_set.create(name_id=role_id,person=p,email=p.email())
277+
can_edit[group.type_id].append(p.user.username)
278+
interesting_users.append(p.user.username)
302279

303-
for username in ['plain','iana','iab chair','irtf chair','marschairman']:
304-
verify_cannot_edit_group(url, username)
280+
test_groups = []
305281

306-
for username in ['secretary','teamchairman','ad']:
307-
verify_can_edit_group(url, username)
282+
for t in ['wg','rg','ag','team']:
283+
g = GroupFactory(type_id=t)
284+
setup_role(g,'chair')
285+
test_groups.append(g)
286+
287+
g = GroupFactory(type_id='dir')
288+
setup_role(g,'secr')
289+
test_groups.append(g)
290+
291+
g = GroupFactory(type_id='program')
292+
setup_role(g, 'lead')
293+
test_groups.append(g)
294+
295+
def verify_cannot_edit_group(url, group, username):
296+
self.client.logout()
297+
self.client.login(username=username, password=username+"+password")
298+
r = self.client.get(url)
299+
self.assertTrue(r.status_code in (302,403),"%s should not be able to edit %s of type %s"%(username,group.acronym,group.type_id))
300+
301+
def verify_can_edit_group(url, group, username):
302+
self.client.logout()
303+
self.client.login(username=username, password=username+"+password")
304+
r = self.client.get(url)
305+
self.assertEqual(r.status_code, 200, "%s should be able to edit %s of type %s"%(username,group.acronym,group.type_id))
306+
307+
for group in test_groups:
308+
309+
for url in [group.about_url(),] + group_urlreverse_list(group, 'ietf.group.views.group_about'):
310+
url = group.about_url()
311+
r = self.client.get(url)
312+
self.assertEqual(r.status_code, 200)
313+
self.assertTrue(group.name in unicontent(r))
314+
self.assertTrue(group.acronym in unicontent(r))
315+
self.assertTrue(group.description in unicontent(r))
316+
317+
for url in group_urlreverse_list(group, 'ietf.group.views_edit.edit'):
318+
319+
for username in can_edit[group.type_id]:
320+
verify_can_edit_group(url, group, username)
321+
322+
for username in list(set(interesting_users)-set(can_edit[group.type_id])):
323+
verify_cannot_edit_group(url, group, username)
308324

309325
def test_materials(self):
310326
make_test_data()

ietf/group/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def can_manage_group_type(user, group_type):
9494
return has_role(user, ('IRTF Chair', 'Secretariat'))
9595
elif group_type == "wg":
9696
return has_role(user, ('Area Director', 'Secretariat'))
97+
elif group_type == "program":
98+
return has_role(user, ('IAB', 'Secretariat',))
9799

98100
return has_role(user, 'Secretariat')
99101

@@ -107,11 +109,15 @@ def can_manage_group(user, group):
107109
return has_role(user, ('Area Director', 'Secretariat'))
108110
elif group.is_decendant_of("irtf"):
109111
return has_role(user, ('IRTF Chair', 'Secretariat'))
112+
elif group.type_id == "program":
113+
return has_role(user,('IAB','Secretariat',))
110114
return has_role(user, ('Secretariat'))
111115

112116
def milestone_reviewer_for_group_type(group_type):
113117
if group_type == "rg":
114118
return "IRTF Chair"
119+
elif group_type== "program":
120+
return "IAB"
115121
else:
116122
return "Area Director"
117123

ietf/group/views.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,13 @@ def active_groups(request, group_type=None):
219219
return active_teams(request)
220220
elif group_type == "dir":
221221
return active_dirs(request)
222+
elif group_type == "program":
223+
return active_programs(request)
222224
else:
223225
raise Http404
224226

225227
def active_group_types(request):
226-
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','area'])
228+
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','area','program'])
227229
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
228230

229231
def active_dirs(request):
@@ -240,6 +242,12 @@ def active_teams(request):
240242
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
241243
return render(request, 'group/active_teams.html', {'teams' : teams })
242244

245+
def active_programs(request):
246+
programs = Group.objects.filter(type="program", state="active").order_by("name")
247+
for group in programs:
248+
group.leads = sorted(roles(group, "lead"), key=extract_last_name)
249+
return render(request, 'group/active_programs.html', {'programs' : programs })
250+
243251
def active_areas(request):
244252
areas = Group.objects.filter(type="area", state="active").order_by("name")
245253
return render(request, 'group/active_areas.html', {'areas': areas })

ietf/group/views_edit.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,11 @@ def conclude(request, acronym, group_type=None):
416416
e.desc = "Requested closing group"
417417
e.save()
418418

419-
return redirect(group.features.about_page, group_type=group_type, acronym=group.acronym)
419+
kwargs = {'acronym':group.acronym}
420+
if group_type:
421+
kwargs['group_type'] = group_type
422+
423+
return redirect(group.features.about_page, **kwargs)
420424
else:
421425
form = ConcludeForm()
422426

0 commit comments

Comments
 (0)