diff --git a/ietf/group/forms.py b/ietf/group/forms.py index 573c4a6f0..1d16f601c 100644 --- a/ietf/group/forms.py +++ b/ietf/group/forms.py @@ -11,17 +11,17 @@ import debug # pyflakes:ignore # Django imports from django import forms from django.utils.html import mark_safe # type:ignore +from django.db.models import F # IETF imports -from ietf.group.models import Group, GroupHistory, GroupStateName -from ietf.name.models import ReviewTypeName +from ietf.group.models import Group, GroupHistory, GroupStateName, GroupFeatures +from ietf.name.models import ReviewTypeName, RoleName from ietf.person.fields import SearchableEmailsField, PersonEmailChoiceField -from ietf.person.models import Person +from ietf.person.models import Person, Email from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings from ietf.review.policies import get_reviewer_queue_policy from ietf.review.utils import close_review_request_states from ietf.utils.textupload import get_cleaned_text_file_content -from ietf.utils.text import strip_suffix #from ietf.utils.ordereddict import insert_after_in_ordered_dict from ietf.utils.fields import DatepickerDateField, MultiEmailField @@ -29,14 +29,6 @@ from ietf.utils.fields import DatepickerDateField, MultiEmailField MAX_GROUP_DELEGATES = 3 -# --- Utility Functions ------------------------------------------------ - -def roles_for_group_type(group_type): - roles = ["chair", "secr", "techadv", "delegate", ] - if group_type == "review": - roles.append("reviewer") - return roles - # --- Forms ------------------------------------------------------------ class StatusUpdateForm(forms.Form): @@ -65,14 +57,7 @@ class GroupForm(forms.Form): name = forms.CharField(max_length=80, label="Name", required=True) acronym = forms.CharField(max_length=40, label="Acronym", required=True) state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True) - - # roles - chair_roles = SearchableEmailsField(label="Chairs", required=False, only_users=True) - secr_roles = SearchableEmailsField(label="Secretaries", required=False, only_users=True) - techadv_roles = SearchableEmailsField(label="Technical Advisors", required=False, only_users=True) - delegate_roles = SearchableEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES, - help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES)) - reviewer_roles = SearchableEmailsField(label="Reviewers", required=False, only_users=True) + # Note that __init__ will add role fields here ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by('name'), label="Shepherding AD", empty_label="(None)", required=False) parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False) @@ -85,16 +70,39 @@ class GroupForm(forms.Form): def __init__(self, *args, **kwargs): self.group = kwargs.pop('group', None) self.group_type = kwargs.pop('group_type', False) + if self.group: + self.used_roles = self.group.used_roles or self.group.features.default_used_roles + else: + self.used_roles = GroupFeatures.objects.get(type=self.group_type).default_used_roles if "field" in kwargs: field = kwargs["field"] del kwargs["field"] - if field in roles_for_group_type(self.group_type): + if field in self.used_roles: field = field + "_roles" else: field = None super(self.__class__, self).__init__(*args, **kwargs) + for role_slug in self.used_roles: + role_name = RoleName.objects.get(slug=role_slug) + fieldname = '%s_roles'%role_slug + field_args = { + 'label' : role_name.name, + 'required' : False, + 'only_users' : True, + } + if fieldname == 'delegate_roles': + field_args['max_entries'] = MAX_GROUP_DELEGATES + field_args['help_text'] = mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES) + self.fields[fieldname] = SearchableEmailsField(**field_args) + self.fields[fieldname].initial = Email.objects.filter(person__role__name_id=role_slug,person__role__group=self.group,person__role__email__pk=F('pk')).distinct() + + self.adjusted_field_order = ['name','acronym','state'] + for role_slug in self.used_roles: + self.adjusted_field_order.append('%s_roles'%role_slug) + self.order_fields(self.adjusted_field_order) + if self.group_type == "rg": self.fields["state"].queryset = self.fields["state"].queryset.exclude(slug__in=("bof", "bof-conc")) @@ -116,10 +124,6 @@ class GroupForm(forms.Form): self.fields['parent'].queryset = self.fields['parent'].queryset.filter(type="area") self.fields['parent'].label = "IETF Area" - role_fields_to_remove = (set(strip_suffix(attr, "_roles") for attr in self.fields if attr.endswith("_roles")) - - set(roles_for_group_type(self.group_type))) - for r in role_fields_to_remove: - del self.fields[r + "_roles"] if field: keys = list(self.fields.keys()) for f in keys: diff --git a/ietf/group/migrations/0029_add_used_roles_and_default_used_roles.py b/ietf/group/migrations/0029_add_used_roles_and_default_used_roles.py new file mode 100644 index 000000000..6f4a562c6 --- /dev/null +++ b/ietf/group/migrations/0029_add_used_roles_and_default_used_roles.py @@ -0,0 +1,34 @@ +# Generated by Django 2.0.13 on 2020-05-22 12:00 + +from django.db import migrations +import jsonfield.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0028_add_robots'), + ] + + operations = [ + migrations.AddField( + model_name='group', + name='used_roles', + field=jsonfield.fields.JSONField(default=[], help_text="Leave an empty list to get the group_type's default used roles", max_length=128), + ), + migrations.AddField( + model_name='groupfeatures', + name='default_used_roles', + field=jsonfield.fields.JSONField(default=[], max_length=128), + ), + migrations.AddField( + model_name='grouphistory', + name='used_roles', + field=jsonfield.fields.JSONField(default=[], help_text="Leave an empty list to get the group_type's default used roles", max_length=128), + ), + migrations.AddField( + model_name='historicalgroupfeatures', + name='default_used_roles', + field=jsonfield.fields.JSONField(default=[], max_length=128), + ), + ] diff --git a/ietf/group/migrations/0030_populate_default_used_roles.py b/ietf/group/migrations/0030_populate_default_used_roles.py new file mode 100644 index 000000000..e6ba3a0fc --- /dev/null +++ b/ietf/group/migrations/0030_populate_default_used_roles.py @@ -0,0 +1,46 @@ +# Generated by Django 2.0.13 on 2020-05-22 11:41 + +from django.db import migrations + +grouptype_defaults = { + 'adhoc': ['matman', 'ad', 'chair', 'lead'], + 'admin': ['member', 'chair'], + 'ag': ['ad', 'chair', 'secr'], + 'area': ['ad'], + 'dir': ['ad', 'chair', 'reviewer', 'secr'], + 'review': ['ad', 'chair', 'reviewer', 'secr'], + 'iab': ['chair'], + 'iana': ['auth'], + 'iesg': [], + 'ietf': ['ad', 'member', 'comdir', 'delegate', 'execdir', 'recman', 'secr', 'trac-editor', 'trac-admin', 'chair'], + 'individ': ['ad'], + 'irtf': ['member', 'atlarge', 'chair'], + 'ise': ['chair'], + 'isoc': ['chair', 'ceo'], + 'nomcom': ['member', 'advisor', 'liaison', 'chair', 'techadv'], + 'program': ['member', 'chair', 'lead'], + 'rfcedtyp': ['auth', 'chair'], + 'rg': ['chair', 'techadv', 'secr', 'delegate'], + 'sdo': ['liaiman', 'ceo', 'coord', 'auth', 'chair'], + 'team': ['ad', 'member', 'delegate', 'secr', 'liaison', 'atlarge', 'chair', 'matman', 'techadv'], + 'wg': ['ad', 'editor', 'delegate', 'secr', 'chair', 'matman', 'techadv'], +} + +def forward(apps, schema_editor): + GroupFeatures = apps.get_model('group','GroupFeatures') + for type_id, roles in grouptype_defaults.items(): + GroupFeatures.objects.filter(type_id=type_id).update(default_used_roles=roles) + +def reverse(apps, schema_editor): + pass # intentional + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0029_add_used_roles_and_default_used_roles'), + ('stats', '0003_meetingregistration_attended'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/group/models.py b/ietf/group/models.py index 5710af219..d5f5089ba 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -43,6 +43,8 @@ class GroupInfo(models.Model): unused_states = models.ManyToManyField('doc.State', help_text="Document states that have been disabled for the group.", blank=True) unused_tags = models.ManyToManyField(DocTagName, help_text="Document tags that have been disabled for the group.", blank=True) + used_roles = jsonfield.JSONField(max_length=128, blank=False, default=[], help_text="Leave an empty list to get the group_type's default used roles") + uses_milestone_dates = models.BooleanField(default=True) def __str__(self): @@ -232,6 +234,7 @@ class GroupFeatures(models.Model): 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 = jsonfield.JSONField(max_length=64, blank=False, default=["slides"]) + default_used_roles = jsonfield.JSONField(max_length=128, blank=False, default=[]) admin_roles = jsonfield.JSONField(max_length=64, blank=False, default=["chair"]) # Trac Admin docman_roles = jsonfield.JSONField(max_length=128, blank=False, default=["ad","chair","delegate","secr"]) groupman_roles = jsonfield.JSONField(max_length=128, blank=False, default=["ad","chair",]) diff --git a/ietf/group/tests_info.py b/ietf/group/tests_info.py index 84ca13a1c..c9281d1e6 100644 --- a/ietf/group/tests_info.py +++ b/ietf/group/tests_info.py @@ -567,6 +567,8 @@ class GroupEditTests(TestCase): q = PyQuery(r.content) self.assertEqual(len(q('form select[name=parent]')), 1) self.assertEqual(len(q('form input[name=acronym]')), 1) + for role_slug in group.used_roles or group.features.default_used_roles: + self.assertEqual(len(q('form input[name=%s_roles]'%role_slug)),1) # faulty post Group.objects.create(name="Collision Test Group", acronym="collide") diff --git a/ietf/group/views.py b/ietf/group/views.py index 9237109b8..d81be7bea 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -128,14 +128,6 @@ from ietf.utils.text import strip_suffix def roles(group, role_name): return Role.objects.filter(group=group, name=role_name).select_related("email", "person") -def roles_for_group_type(group_type): - roles = ["chair", "secr", "techadv", "delegate", ] - if group_type == "review": - roles.append("reviewer") - return roles - - - def fill_in_charter_info(group, include_drafts=False): group.areadirector = getattr(group.ad_role(),'email',None) @@ -546,7 +538,7 @@ def group_about(request, acronym, group_type=None): "can_provide_status_update": can_provide_update, "status_update": status_update, "charter_submit_url": charter_submit_url, - "editable_roles": roles_for_group_type(group_type), + "editable_roles": group.used_roles or group.features.default_used_roles, "closing_note": e, })) @@ -1076,8 +1068,6 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None): closing_note = closing_note, ) - for slug in roles_for_group_type(group_type): - init[slug + "_roles"] = Email.objects.filter(role__group=group, role__name=slug).order_by('role__person__name') else: init = dict(ad=request.user.person.id if group_type == "wg" and has_role(request.user, "Area Director") else None, ) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index d3face474..ab2196f2c 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -2483,6 +2483,7 @@ "custom_group_roles": false, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"matman\",\"ad\",\"chair\",\"lead\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"lead\",\"delegate\"]", @@ -2514,6 +2515,7 @@ "custom_group_roles": false, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"member\",\"chair\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\"]", @@ -2545,6 +2547,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\",\"chair\",\"secr\"]", "docman_roles": "[\"chair\",\"delegate\",\"secr\"]", "groupman_authroles": "[\"Secretariat\",\"Area Director\"]", "groupman_roles": "[\"ad\",\"chair\",\"delegate\"]", @@ -2576,6 +2579,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\"]", "docman_roles": "[\"ad\",\"delegate\",\"secr\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"ad\"]", @@ -2607,6 +2611,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\",\"chair\",\"reviewer\",\"secr\"]", "docman_roles": "[]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"ad\",\"secr\"]", @@ -2638,6 +2643,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"chair\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[]", @@ -2669,6 +2675,7 @@ "custom_group_roles": false, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"auth\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\"]", @@ -2700,6 +2707,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"delegate\"]", @@ -2731,6 +2739,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\",\"member\",\"comdir\",\"delegate\",\"execdir\",\"recman\",\"secr\",\"trac-editor\",\"trac-admin\",\"chair\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"delegate\"]", @@ -2762,6 +2771,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\"]", "docman_roles": "[\"auth\"]", "groupman_authroles": "[]", "groupman_roles": "[]", @@ -2793,6 +2803,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"member\",\"atlarge\",\"chair\"]", "docman_roles": "[]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"delegate\"]", @@ -2824,6 +2835,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"chair\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"delegate\"]", @@ -2855,6 +2867,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"chair\",\"ceo\"]", "docman_roles": "[]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\"]", @@ -2886,6 +2899,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"member\",\"advisor\",\"liaison\",\"chair\",\"techadv\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\",\"advisor\"]", @@ -2917,6 +2931,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"member\",\"chair\",\"lead\"]", "docman_roles": "[\"lead\",\"chair\",\"secr\"]", "groupman_authroles": "[\"Secretariat\",\"IAB\"]", "groupman_roles": "[\"lead\",\"chair\",\"secr\"]", @@ -2948,6 +2963,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.review_requests", + "default_used_roles": "[\"ad\",\"chair\",\"reviewer\",\"secr\"]", "docman_roles": "[\"secr\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"ad\",\"secr\"]", @@ -2979,6 +2995,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"auth\",\"chair\"]", "docman_roles": "[]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[]", @@ -3010,6 +3027,7 @@ "custom_group_roles": false, "customize_workflow": true, "default_tab": "ietf.group.views.group_documents", + "default_used_roles": "[\"chair\",\"techadv\",\"secr\",\"delegate\"]", "docman_roles": "[\"chair\",\"delegate\",\"secr\"]", "groupman_authroles": "[\"Secretariat\",\"IRTF Chair\"]", "groupman_roles": "[\"chair\",\"delegate\"]", @@ -3041,6 +3059,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"liaiman\",\"ceo\",\"coord\",\"auth\",\"chair\"]", "docman_roles": "[\"liaiman\",\"matman\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[]", @@ -3072,6 +3091,7 @@ "custom_group_roles": true, "customize_workflow": false, "default_tab": "ietf.group.views.group_about", + "default_used_roles": "[\"ad\",\"member\",\"delegate\",\"secr\",\"liaison\",\"atlarge\",\"chair\",\"matman\",\"techadv\"]", "docman_roles": "[\"chair\"]", "groupman_authroles": "[\"Secretariat\"]", "groupman_roles": "[\"chair\"]", @@ -3103,6 +3123,7 @@ "custom_group_roles": false, "customize_workflow": true, "default_tab": "ietf.group.views.group_documents", + "default_used_roles": "[\"ad\",\"editor\",\"delegate\",\"secr\",\"chair\",\"matman\",\"techadv\"]", "docman_roles": "[\"chair\",\"delegate\",\"secr\"]", "groupman_authroles": "[\"Secretariat\",\"Area Director\"]", "groupman_roles": "[\"ad\",\"chair\",\"delegate\",\"secr\"]", diff --git a/ietf/stats/migrations/0003_meetingregistration_attended.py b/ietf/stats/migrations/0003_meetingregistration_attended.py index 848e0a421..6500f7f5a 100644 --- a/ietf/stats/migrations/0003_meetingregistration_attended.py +++ b/ietf/stats/migrations/0003_meetingregistration_attended.py @@ -19,6 +19,7 @@ class Migration(migrations.Migration): dependencies = [ ('stats', '0002_add_meetingregistration_fields'), + ('group', '0029_add_used_roles_and_default_used_roles'), ] operations = [