diff --git a/ietf/doc/templatetags/active_groups_menu.py b/ietf/doc/templatetags/active_groups_menu.py index 3a566091a..6bf50f850 100644 --- a/ietf/doc/templatetags/active_groups_menu.py +++ b/ietf/doc/templatetags/active_groups_menu.py @@ -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 }) diff --git a/ietf/doc/tests_review.py b/ietf/doc/tests_review.py index d53840191..05e43d8c1 100644 --- a/ietf/doc/tests_review.py +++ b/ietf/doc/tests_review.py @@ -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)) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 91b1d2d5d..643e029cb 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -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, )) diff --git a/ietf/doc/views_material.py b/ietf/doc/views_material.py index 0dae3d4ec..dbbc98369 100644 --- a/ietf/doc/views_material.py +++ b/ietf/doc/views_material.py @@ -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 diff --git a/ietf/group/admin.py b/ietf/group/admin.py index a0b645442..96e57895b 100644 --- a/ietf/group/admin.py +++ b/ietf/group/admin.py @@ -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"] diff --git a/ietf/group/factories.py b/ietf/group/factories.py index df7f752c4..aba181949 100644 --- a/ietf/group/factories.py +++ b/ietf/group/factories.py @@ -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' diff --git a/ietf/group/features.py b/ietf/group/features.py deleted file mode 100644 index 1c0700419..000000000 --- a/ietf/group/features.py +++ /dev/null @@ -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 - diff --git a/ietf/group/forms.py b/ietf/group/forms.py index 023d2e0ab..1f04cc827 100644 --- a/ietf/group/forms.py +++ b/ietf/group/forms.py @@ -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 diff --git a/ietf/group/migrations/0002_groupfeatures_historicalgroupfeatures.py b/ietf/group/migrations/0002_groupfeatures_historicalgroupfeatures.py new file mode 100644 index 000000000..ac5bcbba0 --- /dev/null +++ b/ietf/group/migrations/0002_groupfeatures_historicalgroupfeatures.py @@ -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', + }, + ), + ] diff --git a/ietf/group/migrations/0003_groupfeatures_data.py b/ietf/group/migrations/0003_groupfeatures_data.py new file mode 100644 index 000000000..58760b96a --- /dev/null +++ b/ietf/group/migrations/0003_groupfeatures_data.py @@ -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), + ] diff --git a/ietf/group/models.py b/ietf/group/models.py index d9f8d9b71..0197f34cf 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -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) diff --git a/ietf/group/resources.py b/ietf/group/resources.py index c2210cba3..ef3c18584 100644 --- a/ietf/group/resources.py +++ b/ietf/group/resources.py @@ -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()) diff --git a/ietf/group/tests_info.py b/ietf/group/tests_info.py index ca9a10a47..26ee1cd10 100644 --- a/ietf/group/tests_info.py +++ b/ietf/group/tests_info.py @@ -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') diff --git a/ietf/group/utils.py b/ietf/group/utils.py index 3fcdff20f..5615a2280 100644 --- a/ietf/group/utils.py +++ b/ietf/group/utils.py @@ -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): diff --git a/ietf/group/views.py b/ietf/group/views.py index 75a563299..3e7343099 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -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") diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 66e49a03b..337f32811 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -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, diff --git a/ietf/name/admin.py b/ietf/name/admin.py index 1fc9f85d3..9eb0f5cc5 100644 --- a/ietf/name/admin.py +++ b/ietf/name/admin.py @@ -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) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index dbf5e4ca9..657f79825 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -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 diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py index f90abcbda..707c54aff 100644 --- a/ietf/name/generate_fixtures.py +++ b/ietf/name/generate_fixtures.py @@ -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() diff --git a/ietf/name/migrations/0002_agendatypename.py b/ietf/name/migrations/0002_agendatypename.py new file mode 100644 index 000000000..11fd824df --- /dev/null +++ b/ietf/name/migrations/0002_agendatypename.py @@ -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, + }, + ), + ] diff --git a/ietf/name/migrations/0003_agendatypename_data.py b/ietf/name/migrations/0003_agendatypename_data.py new file mode 100644 index 000000000..0c975e91b --- /dev/null +++ b/ietf/name/migrations/0003_agendatypename_data.py @@ -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), + ] diff --git a/ietf/name/models.py b/ietf/name/models.py index 42d4bb61c..ac96ab9bc 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -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): diff --git a/ietf/name/resources.py b/ietf/name/resources.py index 13249f2b3..76dca2a15 100644 --- a/ietf/name/resources.py +++ b/ietf/name/resources.py @@ -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()) diff --git a/ietf/review/factories.py b/ietf/review/factories.py index 5a6b46d1e..5d13e375c 100644 --- a/ietf/review/factories.py +++ b/ietf/review/factories.py @@ -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') diff --git a/ietf/settings.py b/ietf/settings.py index caabf0550..a2f552fe1 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -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 = [ diff --git a/ietf/templates/meeting/materials.html b/ietf/templates/meeting/materials.html index 37eff253d..9f397d124 100644 --- a/ietf/templates/meeting/materials.html +++ b/ietf/templates/meeting/materials.html @@ -212,6 +212,41 @@ {% endif %} + + {% if other %} +
Group | +Agenda | +Minutes | +Slides | +Drafts | +Updated | ++ {% else %} + | Group | +Agenda | +Minutes | +Slides | +Drafts | +Updated | + {% endif %} +
---|