This commit replaces the code defined group features with features held

in a database table:

- Added a GroupFeatures model to the group models, and removed the old 
  features.py
- Added a agenda type for future use in showing different group types on
  different agendas.
- Renamed the group feature has_materials to has_nonsession_materials.
- Added API resources and admin support for the new tables.
- Added a Directorate (with reviews) group type as complement to
  Directorate, to distinguish between directorates with and without reviews.
- Adjusted tests as needed.
- Updated the fixtures, and fixed the generate_fixtures script to include
  the new AgendaTypeName objects.

There still exists about 70 instances of code comparing the group type
with a list of types; most of these should probably be replaced with new
features, instead, to make it possible to add new group types through the
database table, rather than having to edit the code.  That was the purpose
of this refactoring from the start, but the presence of this large number
of comparisons of group type against lists of types defeats the goal until
we add appropriate features and replace the group type list comparisons.
 - Legacy-Id: 15316
This commit is contained in:
Henrik Levkowetz 2018-07-12 10:51:48 +00:00
parent 21f38a364e
commit 2daef52bea
28 changed files with 1093 additions and 106 deletions

View file

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

View file

@ -52,8 +52,8 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01')
NewRevisionDocEventFactory(doc=doc,rev='01')
RoleFactory(name_id='chair',person__user__username='marschairman',group=doc.group)
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team3 = ReviewTeamFactory(acronym="reviewteam3", name="Review Team3", type_id="dir", list_email="reviewteam3@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team3 = ReviewTeamFactory(acronym="reviewteam3", name="Review Team3", type_id="review", list_email="reviewteam3@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team3,person=rev_role.person,name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
@ -111,7 +111,7 @@ class ReviewTests(TestCase):
def test_doc_page(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -132,7 +132,7 @@ class ReviewTests(TestCase):
def test_review_request(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -145,7 +145,7 @@ class ReviewTests(TestCase):
def test_close_request(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -268,7 +268,7 @@ class ReviewTests(TestCase):
def test_assign_reviewer(self):
doc = WgDraftFactory(pages=2)
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',person__name=u'Some Reviewer',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='marschairman',person__name=u'WG Cháir Man',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
@ -382,7 +382,7 @@ class ReviewTests(TestCase):
def test_accept_reviewer_assignment(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='requested',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -403,7 +403,7 @@ class ReviewTests(TestCase):
def test_reject_reviewer_assignment(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -476,7 +476,7 @@ class ReviewTests(TestCase):
def test_search_mail_archive(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -523,7 +523,7 @@ class ReviewTests(TestCase):
def setup_complete_review_test(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
NewRevisionDocEventFactory(doc=doc,rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -804,7 +804,7 @@ class ReviewTests(TestCase):
def test_edit_comment(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
@ -825,7 +825,7 @@ class ReviewTests(TestCase):
def test_edit_deadline(self):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))

View file

@ -572,7 +572,7 @@ def document_main(request, name, rev=None):
latest_rev=latest_rev,
snapshot=snapshot,
can_manage_material=can_manage_material,
in_group_materials_types = doc.group and doc.group.features.has_materials and doc.type_id in doc.group.features.material_types,
in_group_materials_types = doc.group and doc.group.features.has_nonsession_materials and doc.type_id in doc.group.features.material_types,
other_types=other_types,
presentations=presentations,
))

View file

@ -20,7 +20,7 @@ from ietf.group.utils import can_manage_materials
@login_required
def choose_material_type(request, acronym):
group = get_object_or_404(Group, acronym=acronym)
if not group.features.has_materials:
if not group.features.has_nonsession_materials:
raise Http404
return render(request, 'doc/material/choose_material_type.html', {
@ -85,7 +85,7 @@ def edit_material(request, name=None, acronym=None, action=None, doc_type=None):
if action == "new":
group = get_object_or_404(Group, acronym=acronym)
if not group.features.has_materials:
if not group.features.has_nonsession_materials:
raise Http404
doc = None
@ -95,7 +95,8 @@ def edit_material(request, name=None, acronym=None, action=None, doc_type=None):
group = doc.group
document_type = doc.type
if document_type not in DocTypeName.objects.filter(slug__in=group.features.material_types) and document_type.slug not in ['minutes','agenda','bluesheets',]:
if (document_type not in DocTypeName.objects.filter(slug__in=group.features.material_types)
and document_type.slug not in ['minutes','agenda','bluesheets',]):
raise Http404

View file

@ -10,7 +10,7 @@ from django.utils.encoding import force_unicode
from django.utils.html import escape
from django.utils.translation import ugettext as _
from ietf.group.models import (Group, GroupHistory, GroupEvent, GroupURL, GroupMilestone,
from ietf.group.models import (Group, GroupFeatures, GroupHistory, GroupEvent, GroupURL, GroupMilestone,
GroupMilestoneHistory, GroupStateTransitions, Role, RoleHistory, ChangeStateGroupEvent,
MilestoneGroupEvent, )
@ -100,6 +100,25 @@ class GroupAdmin(admin.ModelAdmin):
admin.site.register(Group, GroupAdmin)
class GroupFeaturesAdmin(admin.ModelAdmin):
list_display = [
'type',
'customize_workflow',
'has_chartering_process',
'has_default_jabber',
'has_dependencies',
'has_documents',
'has_nonsession_materials',
'has_milestones',
'has_reviews',
'material_types',
'agenda_type',
'admin_roles',
'about_page',
'default_tab',
]
admin.site.register(GroupFeatures, GroupFeaturesAdmin)
class GroupHistoryAdmin(admin.ModelAdmin):
list_display = ["time", "acronym", "name", "type"]
list_display_links = ["acronym", "name"]

View file

@ -19,7 +19,7 @@ class ReviewTeamFactory(factory.DjangoModelFactory):
class Meta:
model = Group
type_id = 'dir'
type_id = 'review'
name = factory.Faker('sentence',nb_words=6)
acronym = factory.Sequence(lambda n: 'acronym%d' %n)
state_id = 'active'

View file

@ -1,46 +0,0 @@
class GroupFeatures(object):
"""Configuration of group pages and processes to have this collected
in one place rather than scattered over the group page views."""
has_milestones = False
has_chartering_process = False
has_documents = False # i.e. drafts/RFCs
has_dependencies = False # Do dependency graphs for group documents make sense?
has_materials = False
has_reviews = False
has_default_jabber = False
customize_workflow = False
about_page = "ietf.group.views.group_about"
default_tab = about_page
material_types = ["slides"]
admin_roles = ["chair"]
def __init__(self, group):
# TODO: should 'ag' be in this list
if group.type_id in ("wg", "rg"):
self.has_milestones = True
self.has_chartering_process = True
self.has_documents = True
self.customize_workflow = True
self.has_default_jabber = True
self.has_dependencies = True
self.default_tab = "ietf.group.views.group_documents"
elif group.type_id in ("team",):
self.has_materials = True
self.default_tab = "ietf.group.views.group_about"
elif group.type_id in ("program",):
self.has_documents = True
self.has_milestones = True
self.admin_roles = ["lead",]
elif group.type_id == "dir":
self.admin_roles = ["chair", "secr"]
if self.has_chartering_process:
self.about_page = "ietf.group.views.group_about"
from ietf.review.utils import active_review_teams
if group in active_review_teams():
self.has_reviews = True
import ietf.group.views
self.default_tab = ietf.group.views.review_requests

View file

@ -27,7 +27,7 @@ MAX_GROUP_DELEGATES = 3
def roles_for_group_type(group_type):
roles = ["chair", "secr", "techadv", "delegate", ]
if group_type == "dir":
if group_type == "review":
roles.append("reviewer")
return roles

View file

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-07-10 15:58
from __future__ import unicode_literals
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import debug # pyflakes:ignore
import ietf.utils.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('name', '0002_agendatypename'),
('group', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='GroupFeatures',
fields=[
('type', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='name.GroupTypeName')),
('has_milestones', models.BooleanField(default=False, verbose_name=b'Milestones')),
('has_chartering_process', models.BooleanField(default=False, verbose_name=b'Chartering')),
('has_documents', models.BooleanField(default=False, verbose_name=b'Documents')),
('has_dependencies', models.BooleanField(default=False, verbose_name=b'Dependencies')),
('has_nonsession_materials', models.BooleanField(default=False, verbose_name=b'Materials')),
('has_meetings', models.BooleanField(default=False, verbose_name=b'Meetings')),
('has_reviews', models.BooleanField(default=False, verbose_name=b'Reviews')),
('has_default_jabber', models.BooleanField(default=False, verbose_name=b'Jabber')),
('customize_workflow', models.BooleanField(default=False, verbose_name=b'Workflow')),
('about_page', models.CharField(default=b'ietf.group.views.group_about', max_length=64)),
('default_tab', models.CharField(default=b'ietf.group.views.group_about', max_length=64)),
('material_types', models.CharField(default=b'slides', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of material types', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')])),
('admin_roles', models.CharField(default=b'chair', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')])),
('agenda_type', models.ForeignKey(default=b'ietf', null=True, on_delete=django.db.models.deletion.CASCADE, to='name.AgendaTypeName')),
],
),
migrations.CreateModel(
name='HistoricalGroupFeatures',
fields=[
('has_milestones', models.BooleanField(default=False, verbose_name=b'Milestones')),
('has_chartering_process', models.BooleanField(default=False, verbose_name=b'Chartering')),
('has_documents', models.BooleanField(default=False, verbose_name=b'Documents')),
('has_dependencies', models.BooleanField(default=False, verbose_name=b'Dependencies')),
('has_nonsession_materials', models.BooleanField(default=False, verbose_name=b'Materials')),
('has_meetings', models.BooleanField(default=False, verbose_name=b'Meetings')),
('has_reviews', models.BooleanField(default=False, verbose_name=b'Reviews')),
('has_default_jabber', models.BooleanField(default=False, verbose_name=b'Jabber')),
('customize_workflow', models.BooleanField(default=False, verbose_name=b'Workflow')),
('about_page', models.CharField(default=b'ietf.group.views.group_about', max_length=64)),
('default_tab', models.CharField(default=b'ietf.group.views.group_about', max_length=64)),
('material_types', models.CharField(default=b'slides', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of material types', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')])),
('admin_roles', models.CharField(default=b'chair', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')])),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('agenda_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.AgendaTypeName')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('type', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.GroupTypeName')),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical group features',
},
),
]

View file

@ -0,0 +1,299 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-07-10 15:58
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations
import debug # pyflakes:ignore
from ietf.review.utils import active_review_teams
group_type_features = {
u'ag': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': True,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'area': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'dir': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair,secr',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'review': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair,secr',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.review_requests',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': True,
'material_types': 'slides'},
u'iab': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': True,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'ietf': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': True,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'individ': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'irtf': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'isoc': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'nomcom': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'side',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'program': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'lead',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': True,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': True,
'has_reviews': False,
'material_types': 'slides'},
u'rfcedtyp': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'side',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'rg': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': True,
'default_tab': 'ietf.group.views.group_documents',
'has_chartering_process': True,
'has_default_jabber': True,
'has_dependencies': True,
'has_documents': True,
'has_meetings': True,
'has_nonsession_materials': False,
'has_milestones': True,
'has_reviews': False,
'material_types': 'slides'},
u'sdo': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': None,
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': False,
'has_nonsession_materials': False,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'team': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': False,
'default_tab': 'ietf.group.views.group_about',
'has_chartering_process': False,
'has_default_jabber': False,
'has_dependencies': False,
'has_documents': False,
'has_meetings': True,
'has_nonsession_materials': True,
'has_milestones': False,
'has_reviews': False,
'material_types': 'slides'},
u'wg': {
'about_page': 'ietf.group.views.group_about',
'admin_roles': 'chair',
'agenda_type': 'ietf',
'customize_workflow': True,
'default_tab': 'ietf.group.views.group_documents',
'has_chartering_process': True,
'has_default_jabber': True,
'has_dependencies': True,
'has_documents': True,
'has_meetings': True,
'has_nonsession_materials': False,
'has_milestones': True,
'has_reviews': False,
'material_types': 'slides'},
}
def forward(apps, schema_editor):
Group = apps.get_model('group', 'Group')
GroupTypeName = apps.get_model('name', 'GroupTypeName')
GroupFeatures = apps.get_model('group', 'GroupFeatures')
AgendaTypeName = apps.get_model('name', 'AgendaTypeName')
for type in group_type_features:
features = group_type_features[type]
features['type_id'] = type
if features['agenda_type']:
features['agenda_type'] = AgendaTypeName.objects.get(slug=features['agenda_type'])
GroupFeatures.objects.create(**features)
dir = GroupTypeName.objects.get(slug='dir')
review = GroupTypeName.objects.create(slug='review', name='Directorate (with reviews)', desc='', used=True, order=0)
review_teams = [ g.acronym for g in active_review_teams() ]
for group in Group.objects.filter(type=dir):
if group.acronym in review_teams:
group.type = review
group.save()
def reverse(apps, schema_editor):
Group = apps.get_model('group', 'Group')
GroupFeatures = apps.get_model('group', 'GroupFeatures')
GroupTypeName = apps.get_model('name', 'GroupTypeName')
dir = GroupTypeName.objects.get(slug='dir')
review = GroupTypeName.objects.get(slug='review')
for group in Group.objects.filter(type=review):
debug.show('group.acronym')
group.type = dir
group.save()
for entry in GroupFeatures.objects.all():
entry.delete()
review.delete()
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('name', '0002_agendatypename'),
('group', '0002_groupfeatures_historicalgroupfeatures'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -6,12 +6,16 @@ import os
import re
from urlparse import urljoin
from django.core.validators import RegexValidator
from django.db import models
from django.db.models.deletion import CASCADE
from simple_history.models import HistoricalRecords
import debug # pyflakes:ignore
from ietf.group.colors import fg_group_colors, bg_group_colors
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName, AgendaTypeName
from ietf.person.models import Email, Person
from ietf.utils.mail import formataddr
from ietf.utils import log
@ -48,8 +52,12 @@ class GroupInfo(models.Model):
@property
def features(self):
if not hasattr(self, "features_cache"):
from ietf.group.features import GroupFeatures
self.features_cache = GroupFeatures(self)
features = GroupFeatures.objects.get(type=self.type)
# convert textual lists to python lists:
for a in ['material_types', 'admin_roles', ]:
v = getattr(features, a)
setattr(features, a, v.split(','))
self.features_cache = features
return self.features_cache
def about_url(self):
@ -193,6 +201,40 @@ class Group(GroupInfo):
desc = [ p for p in re.split('\r?\n\s*\r?\n\s*', text) if p.strip() ][0]
return desc
validate_comma_separated_materials = RegexValidator(
regex=r"[a-z0-9_-]+(,[a-z0-9_-]+)*",
message="Enter a comma-separated list of material types",
code='invalid',
)
validate_comma_separated_roles = RegexValidator(
regex=r"[a-z0-9_-]+(,[a-z0-9_-]+)*",
message="Enter a comma-separated list of role slugs",
code='invalid',
)
class GroupFeatures(models.Model):
type = ForeignKey(GroupTypeName, primary_key=True, null=False, related_name='features')
history = HistoricalRecords()
#
has_milestones = models.BooleanField("Milestones", default=False)
has_chartering_process = models.BooleanField("Chartering", default=False)
has_documents = models.BooleanField("Documents", default=False) # i.e. drafts/RFCs
has_dependencies = models.BooleanField("Dependencies",default=False) # Do dependency graphs for group documents make sense?
has_nonsession_materials= models.BooleanField("Materials", default=False)
has_meetings = models.BooleanField("Meetings", default=False)
has_reviews = models.BooleanField("Reviews", default=False)
has_default_jabber = models.BooleanField("Jabber", default=False)
customize_workflow = models.BooleanField("Workflow", default=False)
agenda_type = models.ForeignKey(AgendaTypeName, null=True, default="ietf", on_delete=CASCADE)
about_page = models.CharField(max_length=64, blank=False, default="ietf.group.views.group_about" )
default_tab = models.CharField(max_length=64, blank=False, default="ietf.group.views.group_about" )
material_types = models.CharField(max_length=64, blank=False, default="slides",
validators=[validate_comma_separated_materials])
admin_roles = models.CharField(max_length=64, blank=False, default="chair",
validators=[validate_comma_separated_roles])
class GroupHistory(GroupInfo):
group = ForeignKey(Group, related_name='history_set')
acronym = models.CharField(max_length=40)

View file

@ -9,7 +9,7 @@ from ietf import api
from ietf.group.models import (Group, GroupStateTransitions, GroupMilestone, GroupHistory,
GroupURL, Role, GroupEvent, RoleHistory, GroupMilestoneHistory, MilestoneGroupEvent,
ChangeStateGroupEvent)
ChangeStateGroupEvent, GroupFeatures, HistoricalGroupFeatures)
from ietf.person.resources import PersonResource
@ -269,3 +269,65 @@ class ChangeStateGroupEventResource(ModelResource):
}
api.group.register(ChangeStateGroupEventResource())
from ietf.name.resources import GroupTypeNameResource, AgendaTypeNameResource
class GroupFeaturesResource(ModelResource):
type = ToOneField(GroupTypeNameResource, 'type')
agenda_type = ToOneField(AgendaTypeNameResource, 'agenda_type', null=True)
class Meta:
queryset = GroupFeatures.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'groupfeatures'
filtering = {
"has_milestones": ALL,
"has_chartering_process": ALL,
"has_documents": ALL,
"has_dependencies": ALL,
"has_nonsession_materials": ALL,
"has_meetings": ALL,
"has_reviews": ALL,
"has_default_jabber": ALL,
"customize_workflow": ALL,
"about_page": ALL,
"default_tab": ALL,
"material_types": ALL,
"admin_roles": ALL,
"type": ALL_WITH_RELATIONS,
"agenda_type": ALL_WITH_RELATIONS,
}
api.group.register(GroupFeaturesResource())
from ietf.name.resources import GroupTypeNameResource, AgendaTypeNameResource
from ietf.utils.resources import UserResource
class HistoricalGroupFeaturesResource(ModelResource):
type = ToOneField(GroupTypeNameResource, 'type', null=True)
agenda_type = ToOneField(AgendaTypeNameResource, 'agenda_type', null=True)
history_user = ToOneField(UserResource, 'history_user', null=True)
class Meta:
queryset = HistoricalGroupFeatures.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'historicalgroupfeatures'
filtering = {
"has_milestones": ALL,
"has_chartering_process": ALL,
"has_documents": ALL,
"has_dependencies": ALL,
"has_nonsession_materials": ALL,
"has_meetings": ALL,
"has_reviews": ALL,
"has_default_jabber": ALL,
"customize_workflow": ALL,
"about_page": ALL,
"default_tab": ALL,
"material_types": ALL,
"admin_roles": ALL,
"history_id": ALL,
"history_change_reason": ALL,
"history_date": ALL,
"history_type": ALL,
"type": ALL_WITH_RELATIONS,
"agenda_type": ALL_WITH_RELATIONS,
"history_user": ALL_WITH_RELATIONS,
}
api.group.register(HistoricalGroupFeaturesResource())

View file

@ -65,9 +65,9 @@ class GroupPagesTests(TestCase):
self.assertTrue(group.name in unicontent(r))
self.assertTrue(group.ad_role().person.plain_name() in unicontent(r))
for t in ('rg','area','ag','dir','team','program'):
for t in ('rg','area','ag','dir','review','team','program'):
g = GroupFactory.create(type_id=t,state_id='active')
if t=='dir':
if t in ['dir','review']:
g.parent = GroupFactory.create(type_id='area',state_id='active')
g.save()
url = urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type=t))
@ -81,7 +81,7 @@ class GroupPagesTests(TestCase):
self.assertTrue("Directorate" in unicontent(r))
self.assertTrue("AG" in unicontent(r))
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','area','dir','team', 'program']).values_list('slug',flat=True):
for slug in GroupTypeName.objects.exclude(slug__in=['wg','rg','ag','area','dir','review','team', 'program']).values_list('slug',flat=True):
with self.assertRaises(NoReverseMatch):
url=urlreverse('ietf.group.views.active_groups', kwargs=dict(group_type=slug))
@ -286,6 +286,7 @@ class GroupPagesTests(TestCase):
'ag' : ['secretary', ],
'team' : ['secretary',], # The code currently doesn't let ads edit teams or directorates. Maybe it should.
'dir' : ['secretary',],
'review' : ['secretary',],
'program' : ['secretary', 'iab-member'],
}
@ -302,9 +303,10 @@ class GroupPagesTests(TestCase):
setup_role(g,'chair')
test_groups.append(g)
g = GroupFactory(type_id='dir')
setup_role(g,'secr')
test_groups.append(g)
for t in ['dir','review',]:
g = GroupFactory(type_id=t)
setup_role(g,'secr')
test_groups.append(g)
g = GroupFactory(type_id='program')
setup_role(g, 'lead')

View file

@ -175,13 +175,14 @@ def construct_group_menu_context(request, group, selected, group_type, others):
entries.append(("About", urlreverse("ietf.group.views.group_about", kwargs=kwargs)))
if group.features.has_documents:
entries.append(("Documents", urlreverse("ietf.group.views.group_documents", kwargs=kwargs)))
if group.features.has_materials and get_group_materials(group).exists():
if group.features.has_nonsession_materials and get_group_materials(group).exists():
entries.append(("Materials", urlreverse("ietf.group.views.materials", kwargs=kwargs)))
if group.features.has_reviews:
import ietf.group.views
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.type_id in ('rg','wg','ag','team'):
if group.features.has_meetings: # type_id in ('rg','wg','ag','team'):
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)))
@ -211,7 +212,7 @@ def construct_group_menu_context(request, group, selected, group_type, others):
import ietf.community.views
actions.append((u'Manage document list', urlreverse(ietf.community.views.manage_list, kwargs=kwargs)))
if group.features.has_materials and can_manage_materials(request.user, group):
if group.features.has_nonsession_materials and can_manage_materials(request.user, group):
actions.append((u"Upload material", urlreverse("ietf.doc.views_material.choose_material_type", kwargs=kwargs)))
if group.features.has_reviews and can_manage_review_requests_for_team(request.user, group):

View file

@ -124,7 +124,7 @@ def roles(group, role_name):
def roles_for_group_type(group_type):
roles = ["chair", "secr", "techadv", "delegate", ]
if group_type == "dir":
if group_type == "review":
roles.append("reviewer")
return roles
@ -293,13 +293,15 @@ def active_groups(request, group_type=None):
return active_teams(request)
elif group_type == "dir":
return active_dirs(request)
elif group_type == "review":
return active_review_dirs(request)
elif group_type == "program":
return active_programs(request)
else:
raise Http404
def active_group_types(request):
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','area','program'])
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','team','dir','review','area','program'])
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
def active_dirs(request):
@ -310,6 +312,14 @@ def active_dirs(request):
group.secretaries = sorted(roles(group, "secr"), key=extract_last_name)
return render(request, 'group/active_dirs.html', {'dirs' : dirs })
def active_review_dirs(request):
dirs = Group.objects.filter(type="review", 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_review_dirs.html', {'dirs' : dirs })
def active_teams(request):
teams = Group.objects.filter(type="team", state="active").order_by("name")
for group in teams:
@ -646,7 +656,7 @@ def history(request, acronym, group_type=None):
def materials(request, acronym, group_type=None):
group = get_group_or_404(acronym, group_type)
if not group.features.has_materials:
if not group.features.has_nonsession_materials:
raise Http404
docs = get_group_materials(group).order_by("type__order", "-time").select_related("type")

View file

@ -130,6 +130,9 @@ def materials(request, num=None):
irtf = sessions.filter(group__parent__acronym = 'irtf')
training = sessions.filter(group__acronym__in=['edu','iaoc'], type_id__in=['session', 'other', ])
iab = sessions.filter(group__parent__acronym = 'iab')
other = sessions.filter(type_id__in=['session'], group__type__features__has_meetings = True)
for ss in [plenaries, ietf, irtf, training, iab]:
other = other.exclude(pk__in=[s.pk for s in ss])
for topic in [plenaries, ietf, training, irtf, iab]:
for event in topic:
@ -145,6 +148,7 @@ def materials(request, num=None):
'training': training,
'irtf': irtf,
'iab': iab,
'other': other,
'cut_off_date': cut_off_date,
'cor_cut_off_date': cor_cut_off_date,
'submission_started': now > begin_date,

View file

@ -1,7 +1,7 @@
from django.contrib import admin
from ietf.name.models import (
BallotPositionName, ConstraintName, ContinentName, CountryName, DBTemplateTypeName,
AgendaTypeName, BallotPositionName, ConstraintName, ContinentName, CountryName, DBTemplateTypeName,
DocRelationshipName, DocReminderTypeName, DocTagName, DocTypeName, DraftSubmissionStateName,
FeedbackTypeName, FormalLanguageName, GroupMilestoneStateName, GroupStateName, GroupTypeName,
ImportantDateName, IntendedStdLevelName, IprDisclosureStateName, IprEventTypeName,
@ -45,6 +45,7 @@ class ImportantDateNameAdmin(NameAdmin):
ordering = ('-used','default_offset_days',)
admin.site.register(ImportantDateName,ImportantDateNameAdmin)
admin.site.register(AgendaTypeName, NameAdmin)
admin.site.register(BallotPositionName, NameAdmin)
admin.site.register(ConstraintName, NameAdmin)
admin.site.register(ContinentName, NameAdmin)

View file

@ -2328,6 +2328,326 @@
"model": "doc.statetype",
"pk": "statchg"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": true,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "ag"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "area"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair,secr",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.review_requests",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "dir"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": true,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "iab"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": true,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "ietf"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "individ"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "irtf"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "isoc"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "side",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "nomcom"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "lead",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": true,
"has_meetings": false,
"has_milestones": true,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "program"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair,secr",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.review_requests",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": true,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "review"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "side",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "rfcedtyp"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": true,
"default_tab": "ietf.group.views.group_documents",
"has_chartering_process": true,
"has_default_jabber": true,
"has_dependencies": true,
"has_documents": true,
"has_meetings": true,
"has_milestones": true,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "rg"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": null,
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": false,
"has_milestones": false,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "sdo"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": false,
"default_tab": "ietf.group.views.group_about",
"has_chartering_process": false,
"has_default_jabber": false,
"has_dependencies": false,
"has_documents": false,
"has_meetings": true,
"has_milestones": false,
"has_nonsession_materials": true,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "team"
},
{
"fields": {
"about_page": "ietf.group.views.group_about",
"admin_roles": "chair",
"agenda_type": "ietf",
"customize_workflow": true,
"default_tab": "ietf.group.views.group_documents",
"has_chartering_process": true,
"has_default_jabber": true,
"has_dependencies": true,
"has_documents": true,
"has_meetings": true,
"has_milestones": true,
"has_nonsession_materials": false,
"has_reviews": false,
"material_types": "slides"
},
"model": "group.groupfeatures",
"pk": "wg"
},
{
"fields": {
"cc": [
@ -2544,6 +2864,8 @@
{
"fields": {
"cc": [
"conflict_review_steering_group",
"conflict_review_stream_manager",
"doc_affecteddoc_authors",
"doc_affecteddoc_group_chairs",
"doc_affecteddoc_notify",
@ -2775,6 +3097,8 @@
"cc": [],
"desc": "Recipients when a document's telechat date or other telechat specific details are changed",
"to": [
"conflict_review_steering_group",
"conflict_review_stream_manager",
"doc_affecteddoc_authors",
"doc_affecteddoc_group_chairs",
"doc_affecteddoc_notify",
@ -3969,6 +4293,46 @@
"model": "mailtrigger.recipient",
"pk": "submission_submitter"
},
{
"fields": {
"desc": "",
"name": "AD Office Hours",
"order": 0,
"used": true
},
"model": "name.agendatypename",
"pk": "ad"
},
{
"fields": {
"desc": "",
"name": "IETF Agenda",
"order": 0,
"used": true
},
"model": "name.agendatypename",
"pk": "ietf"
},
{
"fields": {
"desc": "",
"name": "Side Meetings",
"order": 0,
"used": true
},
"model": "name.agendatypename",
"pk": "side"
},
{
"fields": {
"desc": "",
"name": "Workshops",
"order": 0,
"used": true
},
"model": "name.agendatypename",
"pk": "workshop"
},
{
"fields": {
"blocking": false,
@ -8352,6 +8716,17 @@
"model": "name.grouptypename",
"pk": "program"
},
{
"fields": {
"desc": "",
"name": "Directorate (with reviews)",
"order": 0,
"used": true,
"verbose_name": ""
},
"model": "name.grouptypename",
"pk": "review"
},
{
"fields": {
"desc": "",
@ -8468,7 +8843,7 @@
"desc": "Final Pre-Registration and Pre-Payment cut-off at 17:00 local meeting time",
"name": "Pre-Registration Cutoff",
"order": 0,
"used": true
"used": false
},
"model": "name.importantdatename",
"pk": "cutoffpre"
@ -10090,7 +10465,7 @@
"fields": {
"command": "xym",
"switch": "--version",
"time": "2018-05-21T00:08:52.105",
"time": "2018-07-11T00:07:49.096",
"used": true,
"version": "xym 0.4"
},
@ -10101,7 +10476,7 @@
"fields": {
"command": "pyang",
"switch": "--version",
"time": "2018-05-21T00:08:53.585",
"time": "2018-07-11T00:07:49.823",
"used": true,
"version": "pyang 1.7.5"
},
@ -10112,7 +10487,7 @@
"fields": {
"command": "yanglint",
"switch": "--version",
"time": "2018-05-21T00:08:53.827",
"time": "2018-07-11T00:07:50.005",
"used": true,
"version": "yanglint 0.14.80"
},
@ -10123,9 +10498,9 @@
"fields": {
"command": "xml2rfc",
"switch": "--version",
"time": "2018-05-21T00:08:54.971",
"time": "2018-07-11T00:07:51.102",
"used": true,
"version": "xml2rfc 2.9.6"
"version": "xml2rfc 2.9.8"
},
"model": "utils.versioninfo",
"pk": 4

View file

@ -43,6 +43,9 @@ objects += ietf.doc.models.StateType.objects.all()
objects += ietf.doc.models.State.objects.all()
objects += ietf.doc.models.BallotType.objects.all()
import ietf.group.models
objects += ietf.group.models.GroupFeatures.objects.all()
import ietf.mailtrigger.models
objects += ietf.mailtrigger.models.Recipient.objects.all()
objects += ietf.mailtrigger.models.MailTrigger.objects.all()

View file

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-07-10 13:47
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('name', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AgendaTypeName',
fields=[
('slug', models.CharField(max_length=32, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('desc', models.TextField(blank=True)),
('used', models.BooleanField(default=True)),
('order', models.IntegerField(default=0)),
],
options={
'ordering': ['order', 'name'],
'abstract': False,
},
),
]

View file

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-07-10 13:47
from __future__ import unicode_literals
from django.db import migrations
agenda_type_names = (
{
'slug': 'ietf',
'name': 'IETF Agenda',
'desc': '',
'used': True,
'order': 0,
},
{
'slug': 'ad',
'name': 'AD Office Hours',
'desc': '',
'used': True,
'order': 0,
},
{
'slug': 'side',
'name': 'Side Meetings',
'desc': '',
'used': True,
'order': 0,
},
{
'slug': 'workshop',
'name': 'Workshops',
'desc': '',
'used': True,
'order': 0,
},
)
def forward(apps, schema_editor):
AgendaTypeName = apps.get_model('name', 'AgendaTypeName')
for entry in agenda_type_names:
AgendaTypeName.objects.create(**entry)
def reverse(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('name', '0001_initial'),
('group', '0002_groupfeatures_historicalgroupfeatures'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -57,6 +57,8 @@ class BallotPositionName(NameModel):
blocking = models.BooleanField(default=False)
class MeetingTypeName(NameModel):
"""IETF, Interim"""
class AgendaTypeName(NameModel):
"""ietf, ad, side, workshop, ..."""
class SessionStatusName(NameModel):
"""Waiting for Approval, Approved, Waiting for Scheduling, Scheduled, Cancelled, Disapproved"""
class TimeSlotTypeName(NameModel):

View file

@ -7,16 +7,15 @@ from tastypie.cache import SimpleCache
from ietf import api
from ietf.name.models import (TimeSlotTypeName, GroupStateName, DocTagName, IntendedStdLevelName,
LiaisonStatementPurposeName, DraftSubmissionStateName, DocTypeName, RoleName,
IprDisclosureStateName, StdLevelName, LiaisonStatementEventTypeName, GroupTypeName,
IprEventTypeName, GroupMilestoneStateName, SessionStatusName, DocReminderTypeName,
ConstraintName, MeetingTypeName, DocRelationshipName, RoomResourceName, IprLicenseTypeName,
LiaisonStatementTagName, FeedbackTypeName, LiaisonStatementState, StreamName,
BallotPositionName, DBTemplateTypeName, NomineePositionStateName, ReviewRequestStateName,
ReviewTypeName, ReviewResultName, TopicAudienceName, FormalLanguageName, ContinentName,
CountryName, ImportantDateName, DocUrlTagName)
from ietf.name.models import ( AgendaTypeName, BallotPositionName, ConstraintName,
ContinentName, CountryName, DBTemplateTypeName, DocRelationshipName, DocReminderTypeName,
DocTagName, DocTypeName, DocUrlTagName, DraftSubmissionStateName, FeedbackTypeName,
FormalLanguageName, GroupMilestoneStateName, GroupStateName, GroupTypeName,
ImportantDateName, IntendedStdLevelName, IprDisclosureStateName, IprEventTypeName,
IprLicenseTypeName, LiaisonStatementEventTypeName, LiaisonStatementPurposeName,
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName, )
class TimeSlotTypeNameResource(ModelResource):
class Meta:
@ -552,3 +551,19 @@ class DocUrlTagNameResource(ModelResource):
"order": ALL,
}
api.name.register(DocUrlTagNameResource())
class AgendaTypeNameResource(ModelResource):
class Meta:
queryset = AgendaTypeName.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'agendatypename'
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
}
api.name.register(AgendaTypeNameResource())

View file

@ -8,7 +8,7 @@ class ReviewTeamSettingsFactory(factory.DjangoModelFactory):
class Meta:
model = ReviewTeamSettings
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='dir')
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='review')
@factory.post_generation
def review_types(obj, create, extracted, **kwargs):
@ -35,7 +35,7 @@ class ReviewRequestFactory(factory.DjangoModelFactory):
state_id = 'requested'
type_id = 'lc'
doc = factory.SubFactory('ietf.doc.factories.DocumentFactory',type_id='draft')
team = factory.SubFactory('ietf.group.factories.ReviewTeamFactory',type_id='dir')
team = factory.SubFactory('ietf.group.factories.ReviewTeamFactory',type_id='review')
deadline = datetime.datetime.today()+datetime.timedelta(days=14)
requested_by = factory.SubFactory('ietf.person.factories.PersonFactory')

View file

@ -883,7 +883,7 @@ TRAC_ISSUE_URL_PATTERN = "https://trac.ietf.org/trac/%s/report/1"
TRAC_SVN_DIR_PATTERN = "/a/svn/group/%s"
#TRAC_SVN_URL_PATTERN = "https://svn.ietf.org/svn/group/%s/"
TRAC_CREATE_GROUP_TYPES = ['wg', 'rg', 'area', 'team', 'dir', 'ag', ]
TRAC_CREATE_GROUP_TYPES = ['wg', 'rg', 'area', 'team', 'dir', 'review', 'ag', ]
TRAC_CREATE_GROUP_STATES = ['bof', 'active', ]
TRAC_CREATE_GROUP_ACRONYMS = ['iesg', 'iaoc', 'ietf', ]
TRAC_CREATE_ADHOC_WIKIS = [

View file

@ -212,6 +212,41 @@
</tbody>
</table>
{% endif %}
{% if other %}
<h2 class="anchor-target" id="other">Other <small>Miscellaneous other sessions</small></h2>
<table class="table table-condensed table-striped tablesorter">
<thead>
<tr>
{% if user|has_role:"Secretariat" or user|managed_groups %}
<th class="col-md-1">Group</th>
<th class="col-md-1">Agenda</th>
<th class="col-md-1">Minutes</th>
<th class="col-md-3">Slides</th>
<th class="col-md-4">Drafts</th>
<th class="col-md-1">Updated</th>
<th class="col-md-1">&nbsp;</th>
{% else %}
<th class="col-md-1">Group</th>
<th class="col-md-1">Agenda</th>
<th class="col-md-1">Minutes</th>
<th class="col-md-4">Slides</th>
<th class="col-md-4">Drafts</th>
<th class="col-md-1">Updated</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for session in other|dictsort:"group.acronym" %}
{% ifchanged %}
{% include "meeting/group_materials.html" %}
{% endifchanged %}
{% endfor %}
</tbody>
</table>
{% endif %}
{% endwith %}
</div>
@ -235,6 +270,10 @@
{% if irtf %}
<li><a href="#irtf">IRTF</a></li>
{% endif %}
{% if other %}
<li><a href="#other">Other</a></li>
{% endif %}
</ul>
</div>
</div>

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|area|program))/', include(grouptype_urls)),
url(r'^(?P<group_type>(wg|rg|ag|team|dir|review|area|program))/', include(grouptype_urls)),
# Redirects
url(r'^(?P<path>public)/', include('ietf.redirects.urls')),

View file

@ -416,9 +416,9 @@ def make_test_data():
return draft
def make_review_data(doc):
team1 = create_group(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
team2 = create_group(acronym="reviewteam2", name="Review Team 2", type_id="dir", list_email="reviewteam2@ietf.org", parent=Group.objects.get(acronym="farfut"))
team3 = create_group(acronym="reviewteam3", name="Review Team 3", type_id="dir", list_email="reviewteam2@ietf.org", parent=Group.objects.get(acronym="farfut"))
team1 = create_group(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
team2 = create_group(acronym="reviewteam2", name="Review Team 2", type_id="review", list_email="reviewteam2@ietf.org", parent=Group.objects.get(acronym="farfut"))
team3 = create_group(acronym="reviewteam3", name="Review Team 3", type_id="review", list_email="reviewteam2@ietf.org", parent=Group.objects.get(acronym="farfut"))
for team in (team1, team2, team3):
ReviewTeamSettings.objects.create(group=team)
for r in ReviewResultName.objects.filter(slug__in=["issues", "ready-issues", "ready", "not-ready"]):