Convert IRTF area groups into their own group type rather than attempting to overload AG. Fixes #3027. Commit ready for merge.

- Legacy-Id: 18298
This commit is contained in:
Robert Sparks 2020-07-28 18:54:44 +00:00
parent 44d19f0533
commit 2b10465aa7
27 changed files with 15029 additions and 14925 deletions

View file

@ -24,7 +24,7 @@ from ietf.group.models import Role
addresses = set()
for role in Role.objects.filter(
group__state__slug='active',
group__type__in=['ag','area','dir','iab','ietf','irtf','nomcom','rg','team','wg',]):
group__type__in=['ag','area','dir','iab','ietf','irtf','nomcom','rg','team','wg','rag']):
#sys.stderr.write(str(role)+'\n')
for e in role.person.email_set.all():
if e.active and not e.address.startswith('unknown-email-'):

View file

@ -45,7 +45,7 @@ class SearchRuleForm(forms.ModelForm):
self.fields["group"].label = "Area"
self.fields["group"].queryset = self.fields["group"].queryset.filter(Q(type="area") | Q(acronym="irtf")).order_by("acronym")
else:
self.fields["group"].queryset = self.fields["group"].queryset.filter(type__in=("wg", "rg", "ag", )).order_by("acronym")
self.fields["group"].queryset = self.fields["group"].queryset.filter(type__in=("wg", "rg", "ag", "rag", "program" )).order_by("acronym")
del self.fields["person"]
del self.fields["text"]

View file

@ -51,7 +51,7 @@ def can_manage_community_list(user, clist):
if has_role(user, 'Secretariat'):
return True
if clist.group.type_id in ['area', 'wg', 'rg', 'ag', 'program', ]:
if clist.group.type_id in ['area', 'wg', 'rg', 'ag', 'rag', 'program', ]:
return Role.objects.filter(name__slug__in=clist.group.features.groupman_roles, person__user=user, group=clist.group).exists()
return False

View file

@ -246,7 +246,7 @@ def generate_approval_mail_approved(request, doc):
# the second check catches some area working groups (like
# Transport Area Working Group)
if doc.group.type_id not in ("area", "individ", "ag", "rg") and not doc.group.name.endswith("Working Group"):
if doc.group.type_id not in ("area", "individ", "ag", "rg", "rag") and not doc.group.name.endswith("Working Group"):
doc.group.name_with_wg = doc.group.name + " Working Group"
else:
doc.group.name_with_wg = doc.group.name

View file

@ -7,7 +7,7 @@ from ietf.name.models import GroupTypeName
register = template.Library()
parents = GroupTypeName.objects.filter(slug__in=['ag','area','team','dir','program'])
parents = GroupTypeName.objects.filter(slug__in=['ag','area','rag','team','dir','program'])
others = []
for group in Group.objects.filter(acronym__in=('rsoc',), state_id='active'):

View file

@ -121,7 +121,7 @@ def can_adopt_draft(user, doc):
and doc.group.type_id == "individ")
roles = Role.objects.filter(name__in=("chair", "delegate", "secr"),
group__type__in=("wg", "rg", "ag", ),
group__type__in=("wg", "rg", "ag", "rag"),
group__state="active",
person__user=user)
role_groups = [ r.group for r in roles ]

View file

@ -22,7 +22,7 @@ class GroupFactory(factory.DjangoModelFactory):
def parent(self):
if self.type_id in ['wg','ag']:
return GroupFactory(type_id='area')
elif self.type_id in ['rg']:
elif self.type_id in ['rg','rag']:
return GroupFactory(acronym='irtf', type_id='irtf')
else:
return None

View file

@ -71,7 +71,7 @@ class GroupPagesTests(TestCase):
self.assertContains(r, group.name)
self.assertContains(r, group.ad_role().person.plain_name())
for t in ('rg','area','ag','dir','review','team','program'):
for t in ('rg','area','ag', 'rag', 'dir','review','team','program'):
g = GroupFactory.create(type_id=t,state_id='active')
if t in ['dir','review']:
g.parent = GroupFactory.create(type_id='area',state_id='active')
@ -87,7 +87,7 @@ class GroupPagesTests(TestCase):
self.assertContains(r, "Directorate")
self.assertContains(r, "AG")
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','area','dir','review','team','program','adhoc','ise']).values_list('slug',flat=True):
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','rag','area','dir','review','team','program','adhoc','ise']).values_list('slug',flat=True):
with self.assertRaises(NoReverseMatch):
url=urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type=slug))
@ -265,6 +265,7 @@ class GroupPagesTests(TestCase):
'wg' : ['secretary','ad'],
'rg' : ['secretary','irtf-chair'],
'ag' : ['secretary', 'ad' ],
'rag' : ['secretary', 'irtf-chair'],
'team' : ['secretary',], # The code currently doesn't let ads edit teams or directorates. Maybe it should.
'dir' : ['secretary',],
'review' : ['secretary',],
@ -279,7 +280,7 @@ class GroupPagesTests(TestCase):
test_groups = []
for t in ['wg','rg','ag','team']:
for t in ['wg','rg','ag','rag','team']:
g = GroupFactory(type_id=t)
setup_role(g,'chair')
test_groups.append(g)
@ -1472,7 +1473,7 @@ class StatusUpdateTests(TestCase):
self.assertEqual(response.status_code, 404)
self.client.logout()
for type_id in GroupTypeName.objects.exclude(slug__in=('wg','rg','ag','team')).values_list('slug',flat=True):
for type_id in GroupTypeName.objects.exclude(slug__in=('wg','rg','ag','rag','team')).values_list('slug',flat=True):
group = GroupFactory.create(type_id=type_id)
for user in (None,User.objects.get(username='secretary')):
ensure_updates_dont_show(group,user)

View file

@ -216,7 +216,7 @@ def construct_group_menu_context(request, group, selected, group_type, others):
entries.append(("Review requests", urlreverse(ietf.group.views.review_requests, kwargs=kwargs)))
entries.append(("Reviewers", urlreverse(ietf.group.views.reviewer_overview, kwargs=kwargs)))
if group.features.has_meetings: # type_id in ('rg','wg','ag','team'):
if group.features.has_meetings:
entries.append(("Meetings", urlreverse("ietf.group.views.meetings", kwargs=kwargs)))
entries.append(("History", urlreverse("ietf.group.views.history", kwargs=kwargs)))
entries.append(("Photos", urlreverse("ietf.group.views.group_photos", kwargs=kwargs)))

View file

@ -289,6 +289,8 @@ def active_groups(request, group_type=None):
return active_rgs(request)
elif group_type == "ag":
return active_ags(request)
elif group_type == "rag":
return active_rags(request)
elif group_type == "area":
return active_areas(request)
elif group_type == "team":
@ -303,7 +305,7 @@ def active_groups(request, group_type=None):
raise Http404
def active_group_types(request):
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','review','area','program']).filter(group__state='active').annotate(group_count=Count('group'))
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','rag','team','dir','review','area','program']).filter(group__state='active').annotate(group_count=Count('group'))
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
def active_dirs(request):
@ -378,6 +380,14 @@ def active_ags(request):
group.ads = sorted(roles(group, "ad"), key=extract_last_name)
return render(request, 'group/active_ags.html', { 'groups': groups })
def active_rags(request):
groups = Group.objects.filter(type="rag", state="active").order_by("acronym")
for group in groups:
group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
return render(request, 'group/active_rags.html', { 'groups': groups })
def bofs(request, group_type):
groups = Group.objects.filter(type=group_type, state="bof")
@ -408,6 +418,7 @@ def concluded_groups(request):
sections['RGs'] = Group.objects.filter(type='rg', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")
sections['BOFs'] = Group.objects.filter(type='wg', state="bof-conc").select_related("state", "charter").order_by("parent__name","acronym")
sections['AGs'] = Group.objects.filter(type='ag', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")
sections['RAGs'] = Group.objects.filter(type='rag', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")
sections['Directorates'] = Group.objects.filter(type='dir', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")
sections['Review teams'] = Group.objects.filter(type='review', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")
sections['Teams'] = Group.objects.filter(type='team', state="conclude").select_related("state", "charter").order_by("parent__name","acronym")

View file

@ -76,6 +76,7 @@ def has_role(user, role_names, *args, **kwargs):
"RG Chair": Q(person=person,name="chair", group__type="rg", group__state__in=["active","proposed"]),
"RG Secretary": Q(person=person,name="secr", group__type="rg", group__state__in=["active","proposed"]),
"AG Secretary": Q(person=person,name="secr", group__type="ag", group__state__in=["active"]),
"RAG Secretary": Q(person=person,name="secr", group__type="rag", group__state__in=["active"]),
"Team Chair": Q(person=person,name="chair", group__type="team", group__state="active"),
"Program Lead": Q(person=person,name="lead", group__type="program", group__state="active"),
"Program Secretary": Q(person=person,name="secr", group__type="program", group__state="active"),

View file

@ -157,7 +157,7 @@ class InterimMeetingModelForm(forms.ModelForm):
if has_role(self.user, "Area Director"):
q_objects.add(Q(type__in=["wg", "ag"], state__in=("active", "proposed", "bof")), Q.OR)
if has_role(self.user, "IRTF Chair"):
q_objects.add(Q(type="rg", state__in=("active", "proposed")), Q.OR)
q_objects.add(Q(type__in=["rg", "rag"], state__in=("active", "proposed")), Q.OR)
if has_role(self.user, "WG Chair"):
q_objects.add(Q(type="wg", state__in=("active", "proposed", "bof"), role__person=self.person, role__name="chair"), Q.OR)
if has_role(self.user, "RG Chair"):

View file

@ -338,7 +338,7 @@ def can_approve_interim_request(meeting, user):
if group.type.slug in ['wg','ag']:
if group.parent.role_set.filter(name='ad', person=person) or group.role_set.filter(name='ad', person=person):
return True
if group.type.slug == 'rg' and group.parent.role_set.filter(name='chair', person=person):
if group.type.slug in ['rg','rag'] and group.parent.role_set.filter(name='chair', person=person):
return True
if group.type.slug == 'program':
if person.role_set.filter(group__acronym='iab', name='member'):

View file

@ -550,6 +550,7 @@ class Session(object):
self.is_area_meeting = any([
session_db.group.type_id == 'area',
session_db.group.type_id == 'ag',
session_db.group.type_id == 'rag',
session_db.group.meeting_seen_as_area,
])
self.is_bof = session_db.group.state_id == 'bof'

View file

@ -996,7 +996,7 @@ class Session(models.Model):
# pages.
def all_meeting_sessions_for_group(self):
from ietf.meeting.utils import add_event_info_to_session_qs
if self.group.type_id in ['wg','rg','ag']:
if self.group.type_id in ['wg','rg','ag','rag']:
if not hasattr(self, "_all_meeting_sessions_for_group_cache"):
sessions = [s for s in add_event_info_to_session_qs(self.meeting.session_set.filter(group=self.group,type=self.type)) if s.official_timeslotassignment()]
self._all_meeting_sessions_for_group_cache = sorted(sessions, key = lambda x: x.official_timeslotassignment().timeslot.time)

View file

@ -939,7 +939,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
groups = [a.session.historic_group for a in filtered_assignments
if a.session
and a.session.historic_group
and a.session.historic_group.type_id in ('wg', 'rg', 'ag', 'iab')
and a.session.historic_group.type_id in ('wg', 'rg', 'ag', 'rag', 'iab', 'program')
and a.session.historic_group.historic_parent]
group_parents = []
for g in groups:
@ -1422,7 +1422,7 @@ def agenda_json(request, num=None ):
}
if asgn.session.historic_group.is_bof():
sessdict['is_bof'] = True
if asgn.session.historic_group.type_id in ['wg','rg', 'ag',] or asgn.session.historic_group.acronym in ['iesg',]:
if asgn.session.historic_group.type_id in ['wg','rg', 'ag', 'rag'] or asgn.session.historic_group.acronym in ['iesg',]: # TODO: should that first list be groupfeatures driven?
if asgn.session.historic_group.historic_parent:
sessdict['group']['parent'] = asgn.session.historic_group.historic_parent.acronym
parent_acronyms.add(asgn.session.historic_group.historic_parent.acronym)
@ -1542,7 +1542,7 @@ def meeting_requests(request, num=None):
s.current_status_name = status_names.get(s.current_status, s.current_status)
s.requested_by_person = session_requesters.get(s.requested_by)
groups_not_meeting = Group.objects.filter(state='Active',type__in=['wg','rg','ag','bof','program']).exclude(acronym__in = [session.group.acronym for session in sessions]).order_by("parent__acronym","acronym").prefetch_related("parent")
groups_not_meeting = Group.objects.filter(state='Active',type__in=['wg','rg','ag','rag','bof','program']).exclude(acronym__in = [session.group.acronym for session in sessions]).order_by("parent__acronym","acronym").prefetch_related("parent")
return render(request, "meeting/requests.html",
{"meeting": meeting, "sessions":sessions,
@ -3174,7 +3174,7 @@ def request_minutes(request, num=None):
Session.objects.filter(
timeslotassignments__schedule__meeting=meeting,
timeslotassignments__schedule__meeting__schedule=F('timeslotassignments__schedule'),
group__type__in=['wg','rg','ag'],
group__type__in=['wg','rg','ag','rag','program'],
)
).filter(~Q(current_status='canceled')).select_related('group', 'group__parent')
for session in session_qs:

File diff suppressed because it is too large Load diff

View file

@ -293,9 +293,8 @@ def blue_sheet_generate(request, meeting_id):
meeting = get_object_or_404(Meeting, number=meeting_id)
if request.method == "POST":
# TODO: Why aren't 'ag' in here as well?
groups = Group.objects.filter(
type__in=['wg','rg'],
type__in=['wg','rg','ag','rag','program'],
session__timeslotassignments__schedule=meeting.schedule).order_by('acronym')
create_blue_sheets(meeting, groups)

View file

@ -31,7 +31,7 @@ from ietf.secr.proceedings.proc_utils import (create_recording)
# -------------------------------------------------
# Globals
# -------------------------------------------------
AUTHORIZED_ROLES=('WG Chair','WG Secretary','RG Chair','RG Secretary', 'AG Secretary','IRTF Chair','IETF Trust Chair','IAB Group Chair','IAOC Chair','IAD','Area Director','Secretariat','Team Chair')
AUTHORIZED_ROLES=('WG Chair','WG Secretary','RG Chair','RG Secretary', 'AG Secretary', 'RAG Secretary', 'IRTF Chair','IETF Trust Chair','IAB Group Chair','IAOC Chair','IAD','Area Director','Secretariat','Team Chair')
# -------------------------------------------------
# Helper Functions
# -------------------------------------------------
@ -104,17 +104,6 @@ def get_next_slide_num(session):
next slide number to use for a newly added slide as a string.
'''
"""
slides = session.materials.filter(type='slides').order_by('-name')
if slides:
# we need this special case for non wg/rg sessions because the name format is different
# it should be changed to match the rest
# TODO - why isn't 'ag' in this list (also, is this function still used?)
if session.group.type.slug not in ('wg','rg'):
nums = [ s.name.split('-')[3] for s in slides ]
else:
nums = [ s.name.split('-')[-1] for s in slides ]
"""
if session.meeting.type_id == 'ietf':
pattern = 'slides-%s-%s' % (session.meeting.number,session.group.acronym)
elif session.meeting.type_id == 'interim':

View file

@ -27,7 +27,7 @@ JOINT_FOR_SESSION_CHOICES = (('1', 'First session'), ('2', 'Second session'), ('
# Helper Functions
# -------------------------------------------------
def allowed_conflicting_groups():
return Group.objects.filter(type__in=['wg', 'ag', 'rg'], state__in=['bof', 'proposed', 'active'])
return Group.objects.filter(type__in=['wg', 'ag', 'rg', 'rag'], state__in=['bof', 'proposed', 'active'])
def check_conflict(groups, source_group):
'''

View file

@ -626,7 +626,6 @@ MAX_WG_DELEGATES = 3
# These states aren't available in forms with drop-down choices for new
# document state:
GROUP_STATES_WITH_EXTRA_PROCESSING = ["sub-pub", "rfc-edit", ]
GROUP_TYPES_LISTED_ACTIVE = ['wg', 'rg', 'ag', 'team', 'dir', 'review', 'area', 'program', ]
# Review team releated settings
GROUP_REVIEW_MAX_ITEMS_TO_SHOW_IN_REVIEWER_LIST = 10

View file

@ -466,7 +466,7 @@ class SubmitTests(TestCase):
if change_authors:
# Since authors changed, ensure chairs are copied (and that the message says why)
self.assertTrue("chairs have been copied" in str(confirm_email))
if group_type in ['wg','rg','ag']:
if group_type in ['wg','rg','ag','rag']:
self.assertTrue("mars-chairs@" in confirm_email["To"].lower())
elif group_type == 'area':
self.assertTrue("aread@" in confirm_email["To"].lower())

View file

@ -0,0 +1,48 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin static %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "jquery.tablesorter/css/theme.bootstrap.min.css" %}">
{% endblock %}
{% block title %}Active research area groups{% endblock %}
{% block content %}
{% origin %}
<h1>Active research area groups</h1>
<table class="table table-condensed table-striped tablesorter">
<thead>
<tr>
<th>Team</th>
<th>Name</th>
<th>Secretaries</th>
<th>Chairs</th>
</tr>
</thead>
<tbody>
{% for group in groups %}
<tr>
<td><a href="{% url "ietf.group.views.group_home" acronym=group.acronym %}">{{ group.acronym }}</a></td>
<td>{{ group.name }}</td>
<td>
{% for secretary in group.secretaries %}
<a href="{% url 'ietf.person.views.profile' email_or_name=secretary.person.name %}">{{ secretary.person.plain_name }}
</a><a href="mailto:{{ secretary.email.address }}"><span class="fa fa-envelope-o tiny"></span></a> {% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for chair in group.chairs %}
<a href="{% url 'ietf.person.views.profile' email_or_name=chair.person.name %}">{{ chair.person.plain_name }}
</a><a href="mailto:{{ chair.email.address }}"><span class="fa fa-envelope-o tiny"></span></a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block js %}
<script src="{% static "jquery.tablesorter/js/jquery.tablesorter.combined.min.js" %}"></script>
{% endblock %}

View file

@ -4,6 +4,6 @@
{%comment%}
<a class="button btn-default btn-sm" href="{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}{% if gt == 'wg' or gt == 'rg' or gt == 'ag' %}{% else %}#session_{{session.pk}}{% endif %}">Edit</a>
{%endcomment%}
<a class="" href="{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}{% if gt == 'wg' or gt == 'rg' or gt == 'ag' %}{% else %}#session_{{session.pk}}{% endif %}">Edit materials</a>
<a class="" href="{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}{% if gt == 'wg' or gt == 'rg' or gt == 'ag' or gt == 'rag' %}{% else %}#session_{{session.pk}}{% endif %}">Edit materials</a>
{% endwith %}
{% endif %}

View file

@ -66,7 +66,7 @@ urlpatterns = [
url(r'^submit/', include('ietf.submit.urls')),
url(r'^sync/', include('ietf.sync.urls')),
url(r'^templates/', include('ietf.dbtemplate.urls')),
url(r'^(?P<group_type>(wg|rg|ag|team|dir|review|area|program|adhoc|ise))/', include(grouptype_urls)),
url(r'^(?P<group_type>(wg|rg|ag|rag|team|dir|review|area|program|adhoc|ise))/', include(grouptype_urls)),
# Redirects
url(r'^(?P<path>public)/', include('ietf.redirects.urls')),

View file

@ -217,6 +217,17 @@ def make_test_data():
parent=irtf,
list_email="irg-rg@ietf.org",
)
# A research area group
rag = Group.objects.create(
name="Internet Research Area Group",
acronym="irag",
description="This area group groups internet research.",
state_id="active",
type_id="rag",
parent=irtf,
list_email="irag@ietf.org",
)
#charter = Document.objects.create(
# name="charter-ietf-" + group.acronym,
# type_id="charter",
@ -254,6 +265,7 @@ def make_test_data():
frfarea.save()
create_person(irg_rg, "chair", name="Irg Chair Man", username="irgchairman")
create_person(rag, "chair", name="Rag Chair Man", username="ragchairman")
# old draft
old_draft = Document.objects.create(

View file

@ -311,10 +311,10 @@ class TestWikiGlueManagementCommand(TestCase):
set_coverage_checking(True)
def test_wiki_create_output(self):
for group_type in ['wg','rg','ag','area']:
for group_type in ['wg','rg','ag','area','rag']:
GroupFactory(type_id=group_type)
groups = Group.objects.filter(
type__slug__in=['wg','rg','ag','area'],
type__slug__in=['wg','rg','ag','area','rag'],
state__slug='active'
).order_by('acronym')
out = io.StringIO()