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
This commit is contained in:
Robert Sparks 2015-07-30 21:51:38 +00:00
parent a4c6842e16
commit 461af5af19
17 changed files with 362 additions and 88 deletions

View file

@ -0,0 +1,14 @@
from django import template
from django.template.loader import render_to_string
from ietf.name.models import GroupTypeName
register = template.Library()
@register.simple_tag
def active_groups_menu():
parents = GroupTypeName.objects.filter(slug__in=['ag','area','team','dir'])
for p in parents:
p.menu_url = '/%s/'%p.slug
return render_to_string('base/menu_active_groups.html', { 'parents': parents })

View file

@ -167,14 +167,29 @@ def get_or_create_initial_charter(group, group_type):
return charter
@login_required
def submit_initial_charter(request, group_type, acronym=None):
if not can_manage_group_type(request.user, group_type):
return HttpResponseForbidden("You don't have permission to access this view")
def submit_initial_charter(request, group_type=None, acronym=None):
# This needs refactoring.
# The signature assumed you could have groups with the same name, but with different types, which we do not allow.
# Consequently, this can be called with an existing group acronym and a type
# that doesn't match the existing group type. The code below essentially ignores the group_type argument.
#
# If possible, the use of get_or_create_initial_charter should be moved
# directly into charter_submit, and this function should go away.
if acronym==None:
raise Http404
group = get_object_or_404(Group, acronym=acronym)
if not group.features.has_chartering_process:
raise Http404
# This is where we start ignoring the passed in group_type
group_type = group.type_id
if not can_manage_group_type(request.user, group_type):
return HttpResponseForbidden("You don't have permission to access this view")
if not group.charter:
group.charter = get_or_create_initial_charter(group, group_type)
group.save()

View file

@ -20,6 +20,7 @@ class GroupFeatures(object):
self.default_tab = "group_docs"
elif group.type_id in ("team",):
self.has_materials = True
self.default_tab = "group_about"
if self.has_chartering_process:
self.about_page = "group_charter"

View file

@ -189,16 +189,43 @@ def wg_charters_by_acronym(request, group_type):
{ 'groups': groups },
content_type='text/plain; charset=UTF-8')
def active_groups(request, group_type):
if group_type == "wg":
def active_groups(request, group_type=None):
if not group_type:
return active_group_types(request)
elif group_type == "wg":
return active_wgs(request)
elif group_type == "rg":
return active_rgs(request)
elif group_type == "ag":
return active_ags(request)
elif group_type == "area":
return active_areas(request)
elif group_type == "team":
return active_teams(request)
elif group_type == "dir":
return active_dirs(request)
else:
raise Http404
def active_group_types(request):
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','area'])
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
def active_dirs(request):
dirs = Group.objects.filter(type="dir", state="active").order_by("name")
for group in dirs:
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
group.ads = sorted(roles(group, "ad"), key=extract_last_name)
group.secretaries = sorted(roles(group, "secr"), key=extract_last_name)
return render(request, 'group/active_dirs.html', {'dirs' : dirs })
def active_teams(request):
teams = Group.objects.filter(type="team", state="active").order_by("name")
for group in teams:
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
return render(request, 'group/active_teams.html', {'teams' : teams })
def active_areas(request):
areas = Group.objects.filter(type="area", state="active").order_by("name")
return render(request, 'group/active_areas.html', {'areas': areas })
@ -235,6 +262,15 @@ def active_rgs(request):
return render(request, 'group/active_rgs.html', { 'irtf': irtf, 'groups': groups })
def active_ags(request):
groups = Group.objects.filter(type="ag", state="active").order_by("acronym")
for group in groups:
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
group.ads = sorted(roles(group, "ad"), key=extract_last_name)
return render(request, 'group/active_ags.html', { 'groups': groups })
def bofs(request, group_type):
groups = Group.objects.filter(type=group_type, state="bof")
return render(request, 'group/bofs.html',dict(groups=groups))

View file

@ -63,21 +63,25 @@ class GroupTests(TestCase):
make_test_data()
for group in Group.objects.filter(Q(type="wg") | Q(type="rg")):
client = Client(Accept='application/pdf')
r = client.get(urlreverse("ietf.group.info.dependencies_dot",
kwargs=dict(acronym=group.acronym)))
self.assertTrue(r.status_code == 200, "Failed to receive "
"a dot dependency graph for group: %s"%group.acronym)
self.assertGreater(len(r.content), 0, "Dot dependency graph for group "
"%s has no content"%group.acronym)
for url in [ urlreverse("ietf.group.info.dependencies_dot",kwargs=dict(acronym=group.acronym)),
urlreverse("ietf.group.info.dependencies_dot",kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
]:
r = client.get(url)
self.assertTrue(r.status_code == 200, "Failed to receive "
"a dot dependency graph for group: %s"%group.acronym)
self.assertGreater(len(r.content), 0, "Dot dependency graph for group "
"%s has no content"%group.acronym)
def test_group_document_dependency_pdffile(self):
make_test_data()
for group in Group.objects.filter(Q(type="wg") | Q(type="rg")):
client = Client(Accept='application/pdf')
r = client.get(urlreverse("ietf.group.info.dependencies_pdf",
kwargs=dict(acronym=group.acronym)))
self.assertTrue(r.status_code == 200, "Failed to receive "
"a pdf dependency graph for group: %s"%group.acronym)
self.assertGreater(len(r.content), 0, "Pdf dependency graph for group "
"%s has no content"%group.acronym)
for url in [ urlreverse("ietf.group.info.dependencies_pdf",kwargs=dict(acronym=group.acronym)),
urlreverse("ietf.group.info.dependencies_pdf",kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
]:
r = client.get(url)
self.assertTrue(r.status_code == 200, "Failed to receive "
"a pdf dependency graph for group: %s"%group.acronym)
self.assertGreater(len(r.content), 0, "Pdf dependency graph for group "
"%s has no content"%group.acronym)

View file

@ -10,11 +10,12 @@ import debug # pyflakes:ignore
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
from django.core.urlresolvers import NoReverseMatch
from ietf.doc.models import Document, DocAlias, DocEvent, State
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, MilestoneGroupEvent
from ietf.group.utils import save_group_in_history
from ietf.name.models import DocTagName, GroupStateName
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
from ietf.person.models import Person, Email
from ietf.utils.test_utils import TestCase
from ietf.utils.mail import outbox
@ -48,11 +49,37 @@ class GroupPagesTests(TestCase):
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="rg"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue('Active Research Groups' in r.content)
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="area"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue("farfut" in r.content)
self.assertTrue("Far Future (farfut)" in r.content)
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="ag"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue("Active Area Groups" in r.content)
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="dir"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue("Active Directorates" in r.content)
url = urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type="team"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue("Active Teams" in r.content)
url = urlreverse('ietf.group.info.active_groups', kwargs=dict())
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue("Directorate" in r.content)
self.assertTrue("AG" in r.content)
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','area','dir','team']).values_list('slug',flat=True):
with self.assertRaises(NoReverseMatch):
url=urlreverse('ietf.group.info.active_groups', kwargs=dict(group_type=slug))
def test_wg_summaries(self):
draft = make_test_data()
@ -195,14 +222,17 @@ class GroupPagesTests(TestCase):
due=datetime.date.today() + datetime.timedelta(days=100))
milestone.docs.add(draft)
url = group.about_url()
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(group.name in r.content)
self.assertTrue(group.acronym in r.content)
self.assertTrue("This is a charter." in r.content)
self.assertTrue(milestone.desc in r.content)
self.assertTrue(milestone.docs.all()[0].name in r.content)
for url in [group.about_url(),
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym)),
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
]:
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(group.name in r.content)
self.assertTrue(group.acronym in r.content)
self.assertTrue("This is a charter." in r.content)
self.assertTrue(milestone.desc in r.content)
self.assertTrue(milestone.docs.all()[0].name in r.content)
def test_group_about(self):
make_test_data()
@ -214,12 +244,16 @@ class GroupPagesTests(TestCase):
state_id="active",
)
url = group.about_url()
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(group.name in r.content)
self.assertTrue(group.acronym in r.content)
self.assertTrue(group.description in r.content)
for url in [group.about_url(),
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym)),
urlreverse('ietf.group.info.group_about',kwargs=dict(acronym=group.acronym,group_type=group.type_id)),
]:
url = group.about_url()
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(group.name in r.content)
self.assertTrue(group.acronym in r.content)
self.assertTrue(group.description in r.content)
def test_materials(self):
make_test_data()
@ -235,11 +269,15 @@ class GroupPagesTests(TestCase):
doc.set_state(State.objects.get(type="slides", slug="active"))
DocAlias.objects.create(name=doc.name, document=doc)
url = urlreverse("group_materials", kwargs={ 'acronym': group.acronym })
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(doc.title in r.content)
self.assertTrue(doc.name in r.content)
for url in [ urlreverse("group_materials", kwargs={ 'acronym': group.acronym }),
urlreverse("group_materials", kwargs={ 'acronym': group.acronym , 'group_type': group.type_id}),
]:
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(doc.title in r.content)
self.assertTrue(doc.name in r.content)
url = urlreverse("group_materials", kwargs={ 'acronym': group.acronym })
# try deleting the document and check it's gone
doc.set_state(State.objects.get(type="slides", slug="deleted"))
@ -475,6 +513,19 @@ class GroupEditTests(TestCase):
self.assertEqual(group.groupurl_set.all()[0].name, "MARS site")
self.assertTrue(os.path.exists(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))))
def test_initial_charter(self):
make_test_data()
group = Group.objects.get(acronym="mars")
for url in [ urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym}),
urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym,'group_type':group.type_id}),
]:
login_testing_unauthorized(self, "secretary", url)
r = self.client.get(url,follow=True)
self.assertEqual(r.status_code,200)
self.assertTrue(r.redirect_chain[0][0].endswith(urlreverse('charter_submit',kwargs={'name':group.charter.name,'option':'initcharter'})))
self.client.logout()
def test_conclude(self):
make_test_data()
@ -998,11 +1049,13 @@ expand-ames-chairs@virtual.ietf.org mars_chair@ietf
def tearDown(self):
os.unlink(self.group_alias_file.name)
def testNothing(self):
url = urlreverse('ietf.group.info.email_aliases', kwargs=dict(acronym="mars"))
r = self.client.get(url)
self.assertTrue(all([x in r.content for x in ['mars-ads@','mars-chairs@']]))
self.assertFalse(any([x in r.content for x in ['ames-ads@','ames-chairs@']]))
def testEmailAliases(self):
for testdict in [dict(acronym="mars"),dict(acronym="mars",group_type="wg")]:
url = urlreverse('ietf.group.info.email_aliases', kwargs=testdict)
r = self.client.get(url)
self.assertTrue(all([x in r.content for x in ['mars-ads@','mars-chairs@']]))
self.assertFalse(any([x in r.content for x in ['ames-ads@','ames-chairs@']]))
url = urlreverse('ietf.group.info.email_aliases', kwargs=dict())
login_testing_unauthorized(self, "plain", url)

View file

@ -1,36 +1,18 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf.urls import patterns
from django.conf.urls import patterns, include
urlpatterns = patterns('',
(r'^$', 'ietf.group.info.active_groups'),
(r'^groupmenu.json', 'ietf.group.ajax.group_menu_data', None, "group_menu_data"),
(r'^(?P<acronym>[a-z0-9]+).json$', 'ietf.group.ajax.group_json'),
(r'^chartering/$', 'ietf.group.info.chartering_groups'),
(r'^chartering/create/(?P<group_type>(wg|rg))/$', 'ietf.group.edit.edit', {'action': "charter"}, "group_create"),
(r'^concluded/$', 'ietf.group.info.concluded_groups'),
(r'^email-aliases/$', 'ietf.group.info.email_aliases'),
# FIXME: the things below are duplicated in urls_info.py while we
# figure out whether to serve everything from /group/<acronym>,
# need to unify these at some point
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', 'ietf.group.info.group_documents', None, "group_docs"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', 'ietf.group.info.group_about', None, 'group_charter'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', 'ietf.group.info.history'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', 'ietf.group.info.dependencies_dot'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/init-charter/', 'ietf.group.edit.submit_initial_charter'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/edit/$', 'ietf.group.edit.edit', {'action': "edit"}, "group_edit"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/conclude/$', 'ietf.group.edit.conclude'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "current"}, "group_edit_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "charter"}, "group_edit_charter_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/reset/$', 'ietf.group.milestones.reset_charter_milestones', None, "group_reset_charter_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/workflow/$', 'ietf.group.edit.customize_workflow'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/about/(?P<group_type>.)?$', 'ietf.group.info.group_about', None, 'group_about'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.doc.views_material.choose_material_type'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
(r'^(?P<acronym>[A-Za-z0-9._+-]+)/email-aliases/$', 'ietf.group.info.email_aliases'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/', include('ietf.group.urls_info_details')),
)

View file

@ -1,12 +1,12 @@
# Copyright The IETF Trust 2008, All Rights Reserved
from django.conf.urls import patterns
from django.conf.urls import patterns, include
from django.views.generic import RedirectView
from ietf.group import info, edit, milestones
from ietf.group import info, edit
urlpatterns = patterns('',
(r'^$', info.active_groups),
(r'^$', info.active_groups),
(r'^summary.txt', RedirectView.as_view(url='/wg/1wg-summary.txt')),
(r'^summary-by-area.txt', RedirectView.as_view(url='/wg/1wg-summary.txt')),
(r'^summary-by-acronym.txt', RedirectView.as_view(url='/wg/1wg-summary-by-acronym.txt')),
@ -19,19 +19,5 @@ urlpatterns = patterns('',
(r'^bofs/$', info.bofs),
(r'^email-aliases/$', 'ietf.group.info.email_aliases'),
(r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/txt/$', info.group_documents_txt),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', info.group_home, None, "group_home"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', info.group_documents, None, "group_docs"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', info.group_about, None, 'group_charter'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', info.history),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', info.dependencies_dot),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/pdf/$', info.dependencies_pdf),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/init-charter/', edit.submit_initial_charter),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/edit/$', edit.edit, {'action': "edit"}, "group_edit"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/conclude/$', edit.conclude),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/$', milestones.edit_milestones, {'milestone_set': "current"}, "group_edit_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "group_edit_charter_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "group_reset_charter_milestones"),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/workflow/$', edit.customize_workflow),
(r'^(?P<acronym>[A-Za-z0-9._+-]+)/email-aliases/$', 'ietf.group.info.email_aliases'),
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/', include('ietf.group.urls_info_details')),
)

View file

@ -0,0 +1,23 @@
from django.conf.urls import patterns
urlpatterns = patterns('',
(r'^$', 'ietf.group.info.group_home', None, "group_home"),
(r'^documents/txt/$', 'ietf.group.info.group_documents_txt'),
(r'^documents/$', 'ietf.group.info.group_documents', None, "group_docs"),
(r'^charter/$', 'ietf.group.info.group_about', None, 'group_charter'),
(r'^about/$', 'ietf.group.info.group_about', None, 'group_about'),
(r'^history/$','ietf.group.info.history'),
(r'^deps/dot/$', 'ietf.group.info.dependencies_dot'),
(r'^deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
(r'^init-charter/', 'ietf.group.edit.submit_initial_charter'),
(r'^edit/$', 'ietf.group.edit.edit', {'action': "edit"}, "group_edit"),
(r'^conclude/$', 'ietf.group.edit.conclude'),
(r'^milestones/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "current"}, "group_edit_milestones"),
(r'^milestones/charter/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "charter"}, "group_edit_charter_milestones"),
(r'^milestones/charter/reset/$', 'ietf.group.milestones.reset_charter_milestones', None, "group_reset_charter_milestones"),
(r'^workflow/$', 'ietf.group.edit.customize_workflow'),
(r'^materials/$', 'ietf.group.info.materials', None, "group_materials"),
(r'^materials/new/$', 'ietf.doc.views_material.choose_material_type'),
(r'^materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
(r'^/email-aliases/$', 'ietf.group.info.email_aliases'),
)

View file

@ -1,5 +1,5 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
{% load ietf_filters community_tags wg_menu streams_menu %}
{% load ietf_filters community_tags wg_menu streams_menu active_groups_menu %}
{% if flavor != "top" %}
{% include "base/menu_user.html" %}
@ -14,9 +14,9 @@
<ul class="dropdown-menu" role="menu">
{% endif %}
<li><a href="{% url "ietf.group.info.active_groups" group_type="area" %}">Active areas</a></li>
<li><a href="{% url "ietf.group.info.active_groups" group_type="wg" %}">Active WGs</a></li>
<li><a href="{% url "ietf.group.info.active_groups" group_type="rg" %}">Active RGs</a></li>
<li class="dropdown-submenu group-menu"><a href="{% url "ietf.group.info.active_groups" %}">Other</a>{% active_groups_menu %} </li>
{% if flavor == "top" %}<li class="divider visible-lg-block"></li>{% endif %}
<li {%if flavor == "top" %}class="dropdown-header visible-lg-block"{% else %}class="nav-header hidden-nojs"{% endif %}>By area/parent</li>

View file

@ -0,0 +1,8 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
<ul class="dropdown-menu" role="menu">
{% for p in parents %}
<li>
<a href="{{ p.menu_url }}">Active {{ p.name }}s</a>
</li>
{% endfor %}
</ul>

View file

@ -0,0 +1,45 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% block title %}Active Area Groups{% endblock %}
{% block content %}
{% origin %}
<h1>Active Area Groups</h1>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Team</th>
<th>Name</th>
<th>AD</th>
<th>Secretaries</th>
<th>Chairs</th>
</tr>
</thead>
<tbody>
{% for group in groups %}
<tr>
<td><a href="{% url "ietf.group.info.group_home" acronym=group.acronym %}">{{ group.acronym }}</a></td>
<td>{{ group.name }}</td>
<td>
{% for ad in group.ads %}
<a href="mailto:{{ ad.email.address }}">{{ ad.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for secretary in group.secretaries %}
<a href="mailto:{{ secretary.email.address }}">{{ secretary.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for chair in group.chairs %}
<a href="mailto:{{ chair.email.address }}">{{ chair.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -12,11 +12,11 @@
<p>When changing the area structure, the IESG can decide which members are responsible for new and changed areas, including making one sitting AD responsible for multiple areas, but the IESG can only add new members through the <a href="https://www.ietf.org/nomcom/index.html">nomcom process</a>.</p>
<p>The primary task of area management is handled by one or two Area Directors per area. An AD may be advised by one or more directorates, which are created, selected, chaired and if necessary disbanded by the AD. Directorates may be specific to an area, specific to a technology, or chartered in some other fashion.</p>
<p>The ADs for an area are jointly responsible for making sure the WGs in the area are well coordinated, that there is coverage for the technologies needed in the area, and that the challenges most important to the Internet in that area are indeed being worked on.</p>
<ul><li>A full list of active working groups, sorted by area, may be found at <a href="https://datatracker.ietf.org/wg/">https://datatracker.ietf.org/wg/</a>.</li></ul>
<ul><li>A full list of active working groups, sorted by area, may be found at <a href="{% url 'ietf.group.info.active_groups' group_type='wg'%}">{% url 'ietf.group.info.active_groups' group_type='wg' %}</a>.</li></ul>
<p>The IESG decides which areas working groups belong to. The charter of each area is listed below.</p>
{% for area in areas %}
<h2>{{area.name}} ({{area.acronym}})</h2>
<h2><a href="{%url 'ietf.group.info.active_groups' group_type='wg'%}#{{area.acronym}}">{{area.name}} ({{area.acronym}})</a></h2>
<pre class="pasted">{{area.description}}</pre>
{% endfor %}
<p>For more information about the role of the IESG in areas and working groups, please see <a href="https://www.ietf.org/rfc/rfc3710.txt">RFC 3710 ("An IESG charter")</a>, section 6 and <a href="https://www.ietf.org/rfc/rfc2418.txt">RFC 2418 ("IETF Working Group Guidelines and Procedures")</a>.</p>

View file

@ -0,0 +1,46 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% block title %}Active Directorates{% endblock %}
{% block content %}
{% origin %}
<h1>Active Directorates</h1>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Team</th>
<th>Name</th>
<th>AD</th>
<th>Secretaries</th>
<th>Chairs</th>
</tr>
</thead>
<tbody>
{% for group in dirs %}
<tr>
<td><a href="{% url "ietf.group.info.group_home" acronym=group.acronym %}">{{ group.acronym }}</a></td>
<td>{{ group.name }}</td>
<td>
{% for ad in group.ads %}
<a href="mailto:{{ ad.email.address }}">{{ ad.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for secretary in group.secretaries %}
<a href="mailto:{{ secretary.email.address }}">{{ secretary.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for chair in group.chairs %}
<a href="mailto:{{ chair.email.address }}">{{ chair.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% block title %}Active Groups{% endblock %}
{% block content %}
{% origin %}
<h1>Active Groups</h1>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for typename in grouptypes %}
<tr>
<td><a href="{% url "ietf.group.info.active_groups" group_type=typename.slug%}">{{ typename.name }}</a></td>
<td>{{ typename.desc }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% block title %}Active Teams{% endblock %}
{% block content %}
{% origin %}
<h1>Active Teams</h1>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Team</th>
<th>Name</th>
<th>Chairs</th>
</tr>
</thead>
<tbody>
{% for group in teams %}
<tr>
<td><a href="{% url "ietf.group.info.group_home" acronym=group.acronym %}">{{ group.acronym }}</a></td>
<td>{{ group.name }}</td>
<td>
{% for chair in group.chairs %}
<a href="mailto:{{ chair.email.address }}">{{ chair.person.plain_name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -54,7 +54,7 @@ urlpatterns = patterns('',
(r'^sync/', include('ietf.sync.urls')),
(r'^stream/', include('ietf.group.urls_stream')),
(r'^templates/', include('ietf.dbtemplate.urls')),
(r'^(?P<group_type>(wg|rg|area))/', include('ietf.group.urls_info')),
(r'^(?P<group_type>(wg|rg|ag|team|dir|area))/', include('ietf.group.urls_info')),
# Redirects
(r'^(?P<path>public)/', include('ietf.redirects.urls')),