Skip to content

Commit 461af5a

Browse files
committed
Adds views of active areas, area groups, teams, and directorates.
Adds navigation to those views to the base menus. Unifies URL patterns shared between group/urls and group/urls_info, exposing the same view at, e.g., /group/stir and /wg/stir/. Improves testing, primarily of group/info.py Commit ready for merge. - Legacy-Id: 9924
1 parent a4c6842 commit 461af5a

17 files changed

Lines changed: 362 additions & 88 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from django import template
2+
from django.template.loader import render_to_string
3+
4+
from ietf.name.models import GroupTypeName
5+
6+
register = template.Library()
7+
8+
@register.simple_tag
9+
def active_groups_menu():
10+
parents = GroupTypeName.objects.filter(slug__in=['ag','area','team','dir'])
11+
for p in parents:
12+
p.menu_url = '/%s/'%p.slug
13+
return render_to_string('base/menu_active_groups.html', { 'parents': parents })
14+

ietf/group/edit.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,29 @@ def get_or_create_initial_charter(group, group_type):
167167
return charter
168168

169169
@login_required
170-
def submit_initial_charter(request, group_type, acronym=None):
171-
if not can_manage_group_type(request.user, group_type):
172-
return HttpResponseForbidden("You don't have permission to access this view")
170+
def submit_initial_charter(request, group_type=None, acronym=None):
171+
172+
# This needs refactoring.
173+
# The signature assumed you could have groups with the same name, but with different types, which we do not allow.
174+
# Consequently, this can be called with an existing group acronym and a type
175+
# that doesn't match the existing group type. The code below essentially ignores the group_type argument.
176+
#
177+
# If possible, the use of get_or_create_initial_charter should be moved
178+
# directly into charter_submit, and this function should go away.
179+
180+
if acronym==None:
181+
raise Http404
173182

174183
group = get_object_or_404(Group, acronym=acronym)
175184
if not group.features.has_chartering_process:
176185
raise Http404
177186

187+
# This is where we start ignoring the passed in group_type
188+
group_type = group.type_id
189+
190+
if not can_manage_group_type(request.user, group_type):
191+
return HttpResponseForbidden("You don't have permission to access this view")
192+
178193
if not group.charter:
179194
group.charter = get_or_create_initial_charter(group, group_type)
180195
group.save()

ietf/group/features.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(self, group):
2020
self.default_tab = "group_docs"
2121
elif group.type_id in ("team",):
2222
self.has_materials = True
23+
self.default_tab = "group_about"
2324

2425
if self.has_chartering_process:
2526
self.about_page = "group_charter"

ietf/group/info.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,43 @@ def wg_charters_by_acronym(request, group_type):
189189
{ 'groups': groups },
190190
content_type='text/plain; charset=UTF-8')
191191

192-
def active_groups(request, group_type):
193-
if group_type == "wg":
192+
def active_groups(request, group_type=None):
193+
194+
if not group_type:
195+
return active_group_types(request)
196+
elif group_type == "wg":
194197
return active_wgs(request)
195198
elif group_type == "rg":
196199
return active_rgs(request)
200+
elif group_type == "ag":
201+
return active_ags(request)
197202
elif group_type == "area":
198203
return active_areas(request)
204+
elif group_type == "team":
205+
return active_teams(request)
206+
elif group_type == "dir":
207+
return active_dirs(request)
199208
else:
200209
raise Http404
201210

211+
def active_group_types(request):
212+
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','area'])
213+
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
214+
215+
def active_dirs(request):
216+
dirs = Group.objects.filter(type="dir", state="active").order_by("name")
217+
for group in dirs:
218+
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
219+
group.ads = sorted(roles(group, "ad"), key=extract_last_name)
220+
group.secretaries = sorted(roles(group, "secr"), key=extract_last_name)
221+
return render(request, 'group/active_dirs.html', {'dirs' : dirs })
222+
223+
def active_teams(request):
224+
teams = Group.objects.filter(type="team", state="active").order_by("name")
225+
for group in teams:
226+
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
227+
return render(request, 'group/active_teams.html', {'teams' : teams })
228+
202229
def active_areas(request):
203230
areas = Group.objects.filter(type="area", state="active").order_by("name")
204231
return render(request, 'group/active_areas.html', {'areas': areas })
@@ -235,6 +262,15 @@ def active_rgs(request):
235262

236263
return render(request, 'group/active_rgs.html', { 'irtf': irtf, 'groups': groups })
237264

265+
def active_ags(request):
266+
267+
groups = Group.objects.filter(type="ag", state="active").order_by("acronym")
268+
for group in groups:
269+
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
270+
group.ads = sorted(roles(group, "ad"), key=extract_last_name)
271+
272+
return render(request, 'group/active_ags.html', { 'groups': groups })
273+
238274
def bofs(request, group_type):
239275
groups = Group.objects.filter(type=group_type, state="bof")
240276
return render(request, 'group/bofs.html',dict(groups=groups))

ietf/group/tests.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,25 @@ def test_group_document_dependency_dotfile(self):
6363
make_test_data()
6464
for group in Group.objects.filter(Q(type="wg") | Q(type="rg")):
6565
client = Client(Accept='application/pdf')
66-
r = client.get(urlreverse("ietf.group.info.dependencies_dot",
67-
kwargs=dict(acronym=group.acronym)))
68-
self.assertTrue(r.status_code == 200, "Failed to receive "
69-
"a dot dependency graph for group: %s"%group.acronym)
70-
self.assertGreater(len(r.content), 0, "Dot dependency graph for group "
71-
"%s has no content"%group.acronym)
66+
for url in [ urlreverse("ietf.group.info.dependencies_dot",kwargs=dict(acronym=group.acronym)),
67+
urlreverse("ietf.group.info.dependencies_dot",kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
68+
]:
69+
r = client.get(url)
70+
self.assertTrue(r.status_code == 200, "Failed to receive "
71+
"a dot dependency graph for group: %s"%group.acronym)
72+
self.assertGreater(len(r.content), 0, "Dot dependency graph for group "
73+
"%s has no content"%group.acronym)
7274

7375
def test_group_document_dependency_pdffile(self):
7476
make_test_data()
7577
for group in Group.objects.filter(Q(type="wg") | Q(type="rg")):
7678
client = Client(Accept='application/pdf')
77-
r = client.get(urlreverse("ietf.group.info.dependencies_pdf",
78-
kwargs=dict(acronym=group.acronym)))
79-
self.assertTrue(r.status_code == 200, "Failed to receive "
80-
"a pdf dependency graph for group: %s"%group.acronym)
81-
self.assertGreater(len(r.content), 0, "Pdf dependency graph for group "
82-
"%s has no content"%group.acronym)
79+
for url in [ urlreverse("ietf.group.info.dependencies_pdf",kwargs=dict(acronym=group.acronym)),
80+
urlreverse("ietf.group.info.dependencies_pdf",kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
81+
]:
82+
r = client.get(url)
83+
self.assertTrue(r.status_code == 200, "Failed to receive "
84+
"a pdf dependency graph for group: %s"%group.acronym)
85+
self.assertGreater(len(r.content), 0, "Pdf dependency graph for group "
86+
"%s has no content"%group.acronym)
8387

ietf/group/tests_info.py

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
from django.conf import settings
1212
from django.core.urlresolvers import reverse as urlreverse
13+
from django.core.urlresolvers import NoReverseMatch
1314

1415
from ietf.doc.models import Document, DocAlias, DocEvent, State
1516
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, MilestoneGroupEvent
1617
from ietf.group.utils import save_group_in_history
17-
from ietf.name.models import DocTagName, GroupStateName
18+
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
1819
from ietf.person.models import Person, Email
1920
from ietf.utils.test_utils import TestCase
2021
from ietf.utils.mail import outbox
@@ -48,11 +49,37 @@ def test_active_groups(self):
4849
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="rg"))
4950
r = self.client.get(url)
5051
self.assertEqual(r.status_code, 200)
52+
self.assertTrue('Active Research Groups' in r.content)
5153

5254
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="area"))
5355
r = self.client.get(url)
5456
self.assertEqual(r.status_code, 200)
55-
self.assertTrue("farfut" in r.content)
57+
self.assertTrue("Far Future (farfut)" in r.content)
58+
59+
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="ag"))
60+
r = self.client.get(url)
61+
self.assertEqual(r.status_code, 200)
62+
self.assertTrue("Active Area Groups" in r.content)
63+
64+
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="dir"))
65+
r = self.client.get(url)
66+
self.assertEqual(r.status_code, 200)
67+
self.assertTrue("Active Directorates" in r.content)
68+
69+
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="team"))
70+
r = self.client.get(url)
71+
self.assertEqual(r.status_code, 200)
72+
self.assertTrue("Active Teams" in r.content)
73+
74+
url = urlreverse('ietf.group.info.active_groups', kwargs=dict())
75+
r = self.client.get(url)
76+
self.assertEqual(r.status_code, 200)
77+
self.assertTrue("Directorate" in r.content)
78+
self.assertTrue("AG" in r.content)
79+
80+
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','area','dir','team']).values_list('slug',flat=True):
81+
with self.assertRaises(NoReverseMatch):
82+
url=urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type=slug))
5683

5784
def test_wg_summaries(self):
5885
draft = make_test_data()
@@ -195,14 +222,17 @@ def test_group_charter(self):
195222
due=datetime.date.today() + datetime.timedelta(days=100))
196223
milestone.docs.add(draft)
197224

198-
url = group.about_url()
199-
r = self.client.get(url)
200-
self.assertEqual(r.status_code, 200)
201-
self.assertTrue(group.name in r.content)
202-
self.assertTrue(group.acronym in r.content)
203-
self.assertTrue("This is a charter." in r.content)
204-
self.assertTrue(milestone.desc in r.content)
205-
self.assertTrue(milestone.docs.all()[0].name in r.content)
225+
for url in [group.about_url(),
226+
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym)),
227+
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
228+
]:
229+
r = self.client.get(url)
230+
self.assertEqual(r.status_code, 200)
231+
self.assertTrue(group.name in r.content)
232+
self.assertTrue(group.acronym in r.content)
233+
self.assertTrue("This is a charter." in r.content)
234+
self.assertTrue(milestone.desc in r.content)
235+
self.assertTrue(milestone.docs.all()[0].name in r.content)
206236

207237
def test_group_about(self):
208238
make_test_data()
@@ -214,12 +244,16 @@ def test_group_about(self):
214244
state_id="active",
215245
)
216246

217-
url = group.about_url()
218-
r = self.client.get(url)
219-
self.assertEqual(r.status_code, 200)
220-
self.assertTrue(group.name in r.content)
221-
self.assertTrue(group.acronym in r.content)
222-
self.assertTrue(group.description in r.content)
247+
for url in [group.about_url(),
248+
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym)),
249+
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
250+
]:
251+
url = group.about_url()
252+
r = self.client.get(url)
253+
self.assertEqual(r.status_code, 200)
254+
self.assertTrue(group.name in r.content)
255+
self.assertTrue(group.acronym in r.content)
256+
self.assertTrue(group.description in r.content)
223257

224258
def test_materials(self):
225259
make_test_data()
@@ -235,11 +269,15 @@ def test_materials(self):
235269
doc.set_state(State.objects.get(type="slides", slug="active"))
236270
DocAlias.objects.create(name=doc.name, document=doc)
237271

238-
url = urlreverse("group_materials", kwargs={ 'acronym': group.acronym })
239-
r = self.client.get(url)
240-
self.assertEqual(r.status_code, 200)
241-
self.assertTrue(doc.title in r.content)
242-
self.assertTrue(doc.name in r.content)
272+
for url in [ urlreverse("group_materials", kwargs={ 'acronym': group.acronym }),
273+
urlreverse("group_materials", kwargs={ 'acronym': group.acronym , 'group_type': group.type_id}),
274+
]:
275+
r = self.client.get(url)
276+
self.assertEqual(r.status_code, 200)
277+
self.assertTrue(doc.title in r.content)
278+
self.assertTrue(doc.name in r.content)
279+
280+
url = urlreverse("group_materials", kwargs={ 'acronym': group.acronym })
243281

244282
# try deleting the document and check it's gone
245283
doc.set_state(State.objects.get(type="slides", slug="deleted"))
@@ -475,6 +513,19 @@ def test_edit_info(self):
475513
self.assertEqual(group.groupurl_set.all()[0].name, "MARS site")
476514
self.assertTrue(os.path.exists(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))))
477515

516+
def test_initial_charter(self):
517+
make_test_data()
518+
group = Group.objects.get(acronym="mars")
519+
for url in [ urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym}),
520+
urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym,'group_type':group.type_id}),
521+
]:
522+
login_testing_unauthorized(self, "secretary", url)
523+
r = self.client.get(url,follow=True)
524+
self.assertEqual(r.status_code,200)
525+
self.assertTrue(r.redirect_chain[0][0].endswith(urlreverse('charter_submit',kwargs={'name':group.charter.name,'option':'initcharter'})))
526+
self.client.logout()
527+
528+
478529
def test_conclude(self):
479530
make_test_data()
480531

@@ -998,11 +1049,13 @@ def setUp(self):
9981049
def tearDown(self):
9991050
os.unlink(self.group_alias_file.name)
10001051

1001-
def testNothing(self):
1002-
url = urlreverse('ietf.group.info.email_aliases', kwargs=dict(acronym="mars"))
1003-
r = self.client.get(url)
1004-
self.assertTrue(all([x in r.content for x in ['mars-ads@','mars-chairs@']]))
1005-
self.assertFalse(any([x in r.content for x in ['ames-ads@','ames-chairs@']]))
1052+
def testEmailAliases(self):
1053+
1054+
for testdict in [dict(acronym="mars"),dict(acronym="mars",group_type="wg")]:
1055+
url = urlreverse('ietf.group.info.email_aliases', kwargs=testdict)
1056+
r = self.client.get(url)
1057+
self.assertTrue(all([x in r.content for x in ['mars-ads@','mars-chairs@']]))
1058+
self.assertFalse(any([x in r.content for x in ['ames-ads@','ames-chairs@']]))
10061059

10071060
url = urlreverse('ietf.group.info.email_aliases', kwargs=dict())
10081061
login_testing_unauthorized(self, "plain", url)

ietf/group/urls.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,18 @@
11
# Copyright The IETF Trust 2007, All Rights Reserved
22

3-
from django.conf.urls import patterns
3+
from django.conf.urls import patterns, include
44

55
urlpatterns = patterns('',
6+
(r'^$', 'ietf.group.info.active_groups'),
67
(r'^groupmenu.json', 'ietf.group.ajax.group_menu_data', None, "group_menu_data"),
78
(r'^(?P<acronym>[a-z0-9]+).json$', 'ietf.group.ajax.group_json'),
89
(r'^chartering/$', 'ietf.group.info.chartering_groups'),
910
(r'^chartering/create/(?P<group_type>(wg|rg))/$', 'ietf.group.edit.edit', {'action': "charter"}, "group_create"),
1011
(r'^concluded/$', 'ietf.group.info.concluded_groups'),
1112
(r'^email-aliases/$', 'ietf.group.info.email_aliases'),
12-
# FIXME: the things below are duplicated in urls_info.py while we
13-
# figure out whether to serve everything from /group/<acronym>,
14-
# need to unify these at some point
15-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
16-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', 'ietf.group.info.group_documents', None, "group_docs"),
17-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', 'ietf.group.info.group_about', None, 'group_charter'),
18-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', 'ietf.group.info.history'),
19-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', 'ietf.group.info.dependencies_dot'),
20-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
21-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/init-charter/', 'ietf.group.edit.submit_initial_charter'),
22-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/edit/$', 'ietf.group.edit.edit', {'action': "edit"}, "group_edit"),
23-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/conclude/$', 'ietf.group.edit.conclude'),
24-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "current"}, "group_edit_milestones"),
25-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "charter"}, "group_edit_charter_milestones"),
26-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/reset/$', 'ietf.group.milestones.reset_charter_milestones', None, "group_reset_charter_milestones"),
27-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/workflow/$', 'ietf.group.edit.customize_workflow'),
2813

29-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/about/(?P<group_type>.)?$', 'ietf.group.info.group_about', None, 'group_about'),
30-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"),
31-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.doc.views_material.choose_material_type'),
32-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
33-
(r'^(?P<acronym>[A-Za-z0-9._+-]+)/email-aliases/$', 'ietf.group.info.email_aliases'),
14+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
15+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/', include('ietf.group.urls_info_details')),
3416
)
3517

3618

0 commit comments

Comments
 (0)