Merged in [17908] from rjsparks@nostrum.com:

Add used_roles to Group and default_used_roles to GroupFeatures. Dynamically build the group edit form to provide a field for all used_roles. Fixes #2785 and #2572. Partially addresses #2160.
 - Legacy-Id: 17960
Note: SVN reference [17908] has been migrated to Git commit 2862727d02
This commit is contained in:
Henrik Levkowetz 2020-06-10 12:58:29 +00:00
commit 567449d3d2
8 changed files with 137 additions and 36 deletions

View file

@ -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:

View file

@ -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),
),
]

View file

@ -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),
]

View file

@ -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",])

View file

@ -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")

View file

@ -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,
)

View file

@ -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\"]",

View file

@ -19,6 +19,7 @@ class Migration(migrations.Migration):
dependencies = [
('stats', '0002_add_meetingregistration_fields'),
('group', '0029_add_used_roles_and_default_used_roles'),
]
operations = [