diff --git a/ietf/bin/set_admin_permissions b/ietf/bin/set_admin_permissions index 0ecc0737e..701181423 100755 --- a/ietf/bin/set_admin_permissions +++ b/ietf/bin/set_admin_permissions @@ -39,7 +39,7 @@ def permission_names_to_objects(names): result.append(Permission.objects.get(content_type__app_label=app_label, codename=codename)) except Permission.DoesNotExist: - print "NO SUCH PERMISSION: %s, %s" % (app_label, codename) + print ("NO SUCH PERMISSION: %s, %s" % (app_label, codename)) raise return result @@ -61,6 +61,7 @@ def main(): 'group.add_groupevent','group.change_groupevent','group.delete_groupevent', 'iesg.add_telechatagendaitem','iesg.change_telechatagendaitem','iesg.delete_telechatagendaitem', 'iesg.add_telechatdate','iesg.change_telechatdate','iesg.delete_telechatdate', + 'liaisons.add_liaisonstatementgroupcontacts', 'liaisons.change_liaisonstatementgroupcontacts', 'liaisons.delete_liaisonstatementgroupcontacts', 'mailinglists.add_list','mailinglists.change_list','mailinglists.delete_list', 'mailtrigger.add_mailtrigger','mailtrigger.change_mailtrigger','mailtrigger.delete_mailtrigger', 'mailtrigger.add_recipient','mailtrigger.change_recipient','mailtrigger.delete_recipient', @@ -71,6 +72,7 @@ def main(): 'meeting.add_urlresource','meeting.change_urlresource','meeting.delete_urlresource', 'message.add_announcementfrom','message.change_announcementfrom','message.delete_announcementfrom', 'nomcom.add_nomcom','nomcom.change_nomcom','nomcom.delete_nomcom', + 'nomcom.add_volunteer','nomcom.change_volunteer','nomcom.delete_volunteer', 'person.add_person','person.change_person','person.delete_person', 'person.add_alias','person.change_alias','person.delete_alias', 'person.add_email','person.change_email','person.delete_email', diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py index cfcaf89ff..cb4951858 100644 --- a/ietf/doc/tests_ballot.py +++ b/ietf/doc/tests_ballot.py @@ -1097,7 +1097,7 @@ class RegenerateLastCallTestCase(TestCase): lc_text = draft.latest_event(WriteupDocEvent, type="changed_last_call_text").text self.assertTrue("contains these normative down" in lc_text) self.assertTrue("rfc6666" in lc_text) - self.assertTrue("Independent Submission Editor stream" in lc_text) + self.assertTrue("Independent Submission" in lc_text) draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'),relationship_id='downref-approval') diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py index 11363f1ba..6813679b1 100644 --- a/ietf/ietfauth/tests.py +++ b/ietf/ietfauth/tests.py @@ -35,6 +35,7 @@ from ietf.group.models import Group, Role, RoleName from ietf.ietfauth.htpasswd import update_htpasswd_file from ietf.mailinglists.models import Subscribed from ietf.meeting.factories import MeetingFactory +from ietf.nomcom.factories import NomComFactory from ietf.person.factories import PersonFactory, EmailFactory from ietf.person.models import Person, Email, PersonalApiKey from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory @@ -333,6 +334,33 @@ class IetfAuthTests(TestCase): self.assertEqual(updated_roles[0].email_id, new_email_address) + def test_nomcom_dressing_on_profile(self): + url = urlreverse('ietf.ietfauth.views.profile') + + nobody = PersonFactory() + login_testing_unauthorized(self, nobody.user.username, url) + r = self.client.get(url) + self.assertEqual(r.status_code,200) + q = PyQuery(r.content) + self.assertFalse(q('#volunteer-button')) + self.assertFalse(q('#volunteered')) + + year = datetime.date.today().year + nomcom = NomComFactory(group__acronym=f'nomcom{year}',is_accepting_volunteers=True) + r = self.client.get(url) + self.assertEqual(r.status_code,200) + q = PyQuery(r.content) + self.assertTrue(q('#volunteer-button')) + self.assertFalse(q('#volunteered')) + + nomcom.volunteer_set.create(person=nobody) + r = self.client.get(url) + self.assertEqual(r.status_code,200) + q = PyQuery(r.content) + self.assertFalse(q('#volunteer-button')) + self.assertTrue(q('#volunteered')) + + def test_reset_password(self): url = urlreverse(ietf.ietfauth.views.password_reset) diff --git a/ietf/ietfauth/views.py b/ietf/ietfauth/views.py index a85b5b864..3cfa9f8bc 100644 --- a/ietf/ietfauth/views.py +++ b/ietf/ietfauth/views.py @@ -67,6 +67,7 @@ from ietf.ietfauth.htpasswd import update_htpasswd_file from ietf.ietfauth.utils import role_required, has_role from ietf.mailinglists.models import Subscribed, Whitelisted from ietf.name.models import ExtResourceName +from ietf.nomcom.models import NomCom from ietf.person.models import Person, Email, Alias, PersonalApiKey, PERSON_API_KEY_VALUES from ietf.review.models import ReviewerSettings, ReviewWish, ReviewAssignment from ietf.review.utils import unavailable_periods_to_list, get_default_filter_re @@ -210,6 +211,14 @@ def profile(request): emails = Email.objects.filter(person=person).exclude(address__startswith='unknown-email-').order_by('-active','-time') new_email_forms = [] + nc = NomCom.objects.filter(group__acronym__icontains=Date.today().year).first() + if nc and nc.volunteer_set.filter(person=person).exists(): + volunteer_status = 'volunteered' + elif nc and nc.is_accepting_volunteers: + volunteer_status = 'allow' + else: + volunteer_status = 'deny' + if request.method == 'POST': person_form = get_person_form(request.POST, instance=person) for r in roles: @@ -287,6 +296,8 @@ def profile(request): 'roles': roles, 'emails': emails, 'new_email_forms': new_email_forms, + 'nomcom': nc, + 'volunteer_status': volunteer_status, 'settings':settings, }) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index ce101e8ae..d7b5272eb 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -2499,10 +2499,10 @@ "is_schedulable": true, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"lead\",\n \"delegate\",\n \"matman\"\n]", + "need_parent": false, "parent_types": [ "ietf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"lead\",\n \"delegate\",\n \"matman\"\n]", "show_on_agenda": true @@ -2536,8 +2536,8 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\"\n]", - "parent_types": [], "need_parent": false, + "parent_types": [], "req_subm_approval": false, "role_order": "[\n \"chair\"\n]", "show_on_agenda": false @@ -2571,11 +2571,11 @@ "is_schedulable": true, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "area", "ietf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": true @@ -2609,10 +2609,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": true, "parent_types": [ "ietf" ], - "need_parent": true, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2646,10 +2646,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": true, "parent_types": [ "area" ], - "need_parent": true, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2683,10 +2683,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\"\n]", + "need_parent": false, "parent_types": [ "ietf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": true @@ -2720,8 +2720,8 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\"\n]", - "parent_types": [], "need_parent": false, + "parent_types": [], "req_subm_approval": false, "role_order": "[\n \"chair\"\n]", "show_on_agenda": false @@ -2755,8 +2755,8 @@ "is_schedulable": false, "material_types": "\"[]\"", "matman_roles": "[\n \"chair\",\n \"delegate\",\n \"member\"\n]", - "parent_types": [], "need_parent": false, + "parent_types": [], "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"delegate\",\n \"member\"\n]", "show_on_agenda": false @@ -2790,10 +2790,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\"\n]", + "need_parent": false, "parent_types": [ "ietf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2827,10 +2827,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[]", + "need_parent": true, "parent_types": [ "area" ], - "need_parent": true, "req_subm_approval": false, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2864,10 +2864,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "irtf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2901,8 +2901,8 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\"\n]", - "parent_types": [], "need_parent": false, + "parent_types": [], "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"delegate\"\n]", "show_on_agenda": false @@ -2936,10 +2936,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "isoc" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -2973,10 +2973,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\"\n]", + "need_parent": false, "parent_types": [ "area" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"member\",\n \"advisor\"\n]", "show_on_agenda": false @@ -3010,10 +3010,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"lead\",\n \"chair\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "ietf" ], - "need_parent": false, "req_subm_approval": false, "role_order": "[\n \"lead\",\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -3047,10 +3047,10 @@ "is_schedulable": true, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "irtf" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": true @@ -3063,7 +3063,7 @@ "about_page": "ietf.group.views.group_about", "acts_like_wg": false, "admin_roles": "[\n \"chair\",\n \"secr\"\n]", - "agenda_type": null, + "agenda_type": "ietf", "create_wiki": true, "custom_group_roles": true, "customize_workflow": false, @@ -3076,7 +3076,7 @@ "has_chartering_process": false, "has_default_jabber": false, "has_documents": false, - "has_meetings": false, + "has_meetings": true, "has_milestones": false, "has_nonsession_materials": false, "has_reviews": true, @@ -3084,10 +3084,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"ad\",\n \"secr\"\n]", + "need_parent": true, "parent_types": [ "area" ], - "need_parent": true, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -3121,8 +3121,8 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[]", - "parent_types": [], "need_parent": false, + "parent_types": [], "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\"\n]", "show_on_agenda": false @@ -3156,10 +3156,10 @@ "is_schedulable": true, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": true, "parent_types": [ "irtf" ], - "need_parent": true, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]", "show_on_agenda": true @@ -3193,11 +3193,11 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[]", + "need_parent": false, "parent_types": [ "area", "sdo" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"liaiman\"\n]", "show_on_agenda": false @@ -3231,10 +3231,10 @@ "is_schedulable": false, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"chair\",\n \"matman\"\n]", + "need_parent": false, "parent_types": [ "area" ], - "need_parent": false, "req_subm_approval": false, "role_order": "[\n \"chair\",\n \"member\",\n \"matman\"\n]", "show_on_agenda": false @@ -3268,10 +3268,10 @@ "is_schedulable": true, "material_types": "[\n \"slides\"\n]", "matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]", + "need_parent": false, "parent_types": [ "area" ], - "need_parent": false, "req_subm_approval": true, "role_order": "[\n \"chair\",\n \"secr\",\n \"delegate\"\n]", "show_on_agenda": true @@ -4165,6 +4165,7 @@ "fields": { "cc": [ "liaison_cc", + "liaison_coordinators", "liaison_response_contacts", "liaison_technical_contacts" ], @@ -5503,6 +5504,14 @@ "model": "mailtrigger.recipient", "pk": "liaison_cc" }, + { + "fields": { + "desc": "The IAB liaison coordination team members", + "template": "" + }, + "model": "mailtrigger.recipient", + "pk": "liaison_coordinators" + }, { "fields": { "desc": "The assigned liaison manager for an external group ", @@ -10003,6 +10012,7 @@ "auth", "aut-appr", "grp-appr", + "ad-appr", "manual", "cancel" ], @@ -12050,7 +12060,7 @@ "fields": { "desc": "", "name": "Liaison CC Contact", - "order": 0, + "order": 9, "used": true }, "model": "name.rolename", @@ -12060,7 +12070,7 @@ "fields": { "desc": "", "name": "Liaison Contact", - "order": 0, + "order": 8, "used": true }, "model": "name.rolename", @@ -12488,7 +12498,7 @@ }, { "fields": { - "desc": "IAB stream", + "desc": "Internet Architecture Board (IAB)", "name": "IAB", "order": 4, "used": true @@ -12498,7 +12508,7 @@ }, { "fields": { - "desc": "IETF stream", + "desc": "Internet Engineering Task Force (IETF)", "name": "IETF", "order": 1, "used": true @@ -12508,7 +12518,7 @@ }, { "fields": { - "desc": "IRTF Stream", + "desc": "Internet Research Task Force (IRTF)", "name": "IRTF", "order": 3, "used": true @@ -12518,7 +12528,7 @@ }, { "fields": { - "desc": "Independent Submission Editor stream", + "desc": "Independent Submission", "name": "ISE", "order": 2, "used": true @@ -15243,9 +15253,9 @@ "fields": { "command": "xym", "switch": "--version", - "time": "2020-11-14T00:10:15.888", + "time": "2021-06-08T00:12:46.324", "used": true, - "version": "xym 0.4.8" + "version": "xym 0.5" }, "model": "utils.versioninfo", "pk": 1 @@ -15254,7 +15264,7 @@ "fields": { "command": "pyang", "switch": "--version", - "time": "2020-11-14T00:10:17.069", + "time": "2021-06-08T00:12:48.137", "used": true, "version": "pyang 2.4.0" }, @@ -15265,7 +15275,7 @@ "fields": { "command": "yanglint", "switch": "--version", - "time": "2020-11-14T00:10:17.405", + "time": "2021-06-08T00:12:48.465", "used": true, "version": "yanglint SO 1.6.7" }, @@ -15276,9 +15286,9 @@ "fields": { "command": "xml2rfc", "switch": "--version", - "time": "2020-11-14T00:10:19.405", + "time": "2021-06-08T00:12:51.318", "used": true, - "version": "xml2rfc 3.4.0" + "version": "xml2rfc 3.8.0" }, "model": "utils.versioninfo", "pk": 4 diff --git a/ietf/nomcom/admin.py b/ietf/nomcom/admin.py index 252a5f845..eef0eb3a8 100644 --- a/ietf/nomcom/admin.py +++ b/ietf/nomcom/admin.py @@ -1,11 +1,12 @@ -# Copyright The IETF Trust 2012-2020, All Rights Reserved +# Copyright The IETF Trust 2012-2021, All Rights Reserved # -*- coding: utf-8 -*- from django.contrib import admin from ietf.nomcom.models import ( ReminderDates, NomCom, Nomination, Nominee, NomineePosition, - Position, Feedback, FeedbackLastSeen, TopicFeedbackLastSeen) + Position, Feedback, FeedbackLastSeen, TopicFeedbackLastSeen, + Volunteer, ) class ReminderDatesAdmin(admin.ModelAdmin): @@ -68,4 +69,10 @@ class TopicFeedbackLastSeenAdmin(admin.ModelAdmin): raw_id_fields = ['reviewer'] admin.site.register(TopicFeedbackLastSeen, TopicFeedbackLastSeenAdmin) +class VolunteerAdmin(admin.ModelAdmin): + list_display = ['nomcom','person','affiliation'] + list_filter = ['nomcom'] + raw_id_fields = ['person'] +admin.site.register(Volunteer, VolunteerAdmin) + diff --git a/ietf/nomcom/forms.py b/ietf/nomcom/forms.py index 52780d271..642ec5d5f 100644 --- a/ietf/nomcom/forms.py +++ b/ietf/nomcom/forms.py @@ -13,7 +13,7 @@ from django.forms.widgets import FileInput from ietf.dbtemplate.forms import DBTemplateForm from ietf.name.models import FeedbackTypeName, NomineePositionStateName from ietf.nomcom.models import ( NomCom, Nomination, Nominee, NomineePosition, - Position, Feedback, ReminderDates, Topic ) + Position, Feedback, ReminderDates, Topic, Volunteer ) from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE, get_user_email, validate_private_key, validate_public_key, make_nomineeposition, make_nomineeposition_for_newperson, @@ -833,3 +833,22 @@ class EditNomineeForm(forms.ModelForm): class NominationResponseCommentForm(forms.Form): comments = forms.CharField(widget=forms.Textarea,required=False,help_text="Any comments provided will be encrypted and will only be visible to the NomCom.", strip=False) +class NomcomVolunteerMultipleChoiceField(forms.ModelMultipleChoiceField): + def label_from_instance(self, obj): + year = obj.year() + return f'Volunteer for the {year}/{year+1} Nominating Committee' + +class VolunteerForm(forms.ModelForm): + class Meta: + model = Volunteer + fields = ('affiliation',) + + nomcoms = NomcomVolunteerMultipleChoiceField(queryset=NomCom.objects.none(),widget=forms.CheckboxSelectMultiple) + field_order = ('nomcoms','affiliation') + + def __init__(self, person, *args, **kargs): + super().__init__(*args, **kargs) + self.fields['nomcoms'].queryset = NomCom.objects.filter(is_accepting_volunteers=True).exclude(volunteer__person=person) + self.fields['nomcoms'].help_text = 'You may volunteer even if the datatracker does not currently calculate that you are eligible. Eligibility will be assessed when the selection process is performed.' + self.fields['affiliation'].help_text = 'Affiliation to show in the volunteer list' + self.fields['affiliation'].required = True diff --git a/ietf/nomcom/migrations/0011_volunteers.py b/ietf/nomcom/migrations/0011_volunteers.py new file mode 100644 index 000000000..651e1dc54 --- /dev/null +++ b/ietf/nomcom/migrations/0011_volunteers.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.24 on 2021-06-09 12:15 + +from django.db import migrations, models +import django.db.models.deletion +import ietf.utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('person', '0019_auto_20210604_1443'), + ('nomcom', '0010_nomcom_first_call_for_volunteers'), + ] + + operations = [ + migrations.AddField( + model_name='nomcom', + name='is_accepting_volunteers', + field=models.BooleanField(default=False, help_text='Is this nomcom is currently accepting volunteers?', verbose_name='Accepting volunteers'), + ), + migrations.CreateModel( + name='Volunteer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('affiliation', models.CharField(blank=True, max_length=255)), + ('nomcom', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='nomcom.NomCom')), + ('person', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='person.Person')), + ], + ), + ] diff --git a/ietf/nomcom/migrations/0012_populate_volunteers.py b/ietf/nomcom/migrations/0012_populate_volunteers.py new file mode 100644 index 000000000..4132299d1 --- /dev/null +++ b/ietf/nomcom/migrations/0012_populate_volunteers.py @@ -0,0 +1,102 @@ +# Copyright The IETF Trust 2021 All Rights Reserved +# Generated by Django 2.2.24 on 2021-06-10 12:50 + +from django.db import migrations + +def forward(apps, schema_editor): + Volunteer = apps.get_model('nomcom','Volunteer') + NomCom = apps.get_model('nomcom','NomCom') + nc = NomCom.objects.get(group__acronym='nomcom2021') + + nc.is_accepting_volunteers = True + nc.save() + + volunteers = [ + (21684, 'Futurewei Technologies'), # Barry Leiba + (117988, 'Google LLC'), # Benjamin M. Schwartz + (122671, 'Fastmail Pty Ltd'), # Bron Gondwana + (124329, 'Huawei'), # Cheng Li + (113580, 'China Mobile'), # Weiqiang Cheng + (22933, 'LabN Consulting'), # Christian Hopps + (102391, 'Futurewei Technologies, Inc'), # Donald E. Eastlake 3rd + (111477, 'Huawei'), # Dhruv Dhody + (12695, 'Mozilla'), # Eric Rescorla + (17141, 'APNIC'), # George G. Michaelson + (108833, 'Huawei'), # Luigi Iannone + (118908, 'Huawei Technologies'), # Giuseppe Fioccola + (5376, 'Vigil Security, LLC'), # Russ Housley + (118100, 'Equinix'), # Ignas Bagdonas + (107287, 'rtfm llp'), # Jim Reid + (123344, 'Netflix'), # Theresa Enghardt + (109226, 'Huawei'), # Italo Busi + (113152, 'Ericsson'), # Jaime Jimenez + (111354, 'Juniper Networks'), # John Drake + (112342, 'Cisco Systems, Inc.'), # Jakob Heitz + (109207, 'Huawei Technologies Co., Ltd.'), # 江元龙 + (110737, 'Huawei Technologies'), # Jie Dong + (109330, 'Ericsson'), # John Preuß Mattsson + (123589, 'Nokia'), # Julien Maisonneuve + (124655, 'UK National Cyber Security Centre (NCSC)'), # Kirsty Paine + (119463, 'Akamai Technologies, Inc.'), # Kyle Rose + (109983, 'Huawei Technologies Co.,Ltd.'), # Bo Wu + (2097, 'Cisco Systems'), # Eliot Lear + (567, 'UCLA'), # Lixia Zhang + (107762, 'Huawei'), # Mach Chen + (125198, 'Juniper Networks'), # Melchior Aelmans + (104294, 'Ericsson'), # Magnus Westerlund + (104495, 'Impedance Mismatch LLC'), # Marc Petit-Huguenin + (119947, 'Painless Security, LLC'), # Margaret Cullen + (102830, 'Independent'), # Mary Barnes + (116593, 'Fastly'), # Patrick McManus + (102254, 'Sandelman Software Works'), # Michael Richardson + (20356, 'Apple'), # Ted Lemon + (103881, 'Fastly'), # Mark Nottingham + (106741, 'NthPermutation Security'), # Michael StJohns + (116323, 'Moulay Ismail University of Meknes, Morocco'), # Nabil Benamar + (20106, 'W3C/MIT'), # Samuel Weiler + (105691, 'cisco'), # Ole Trøan + (121160, 'Independent, Amazon'), # Padma Pillay-Esnault + (115824, 'Cisco Systems'), # Pascal Thubert + (122637, 'Huawei Technologies Co.,Ltd.'), # Shuping Peng + (112952, 'Huawei'), # Haibo Wang + (5234, 'IIJ Research Lab & Arrcus Inc'), # Randy Bush + (101568, 'Juniper Networks'), # Ron Bonica + (123443, 'Huawei Technologies Co.,Ltd.'), # Bing Liu (Remy) + (18321, 'Episteme Technology Consulting LLC'), # Pete Resnick + (18427, 'Akamai'), # Rich Salz + (126259, 'Huawei Technologies Co.,Ltd.'), # Fan Yang + (115724, 'Juniper Networks'), # Shraddha Hegde + (125509, 'Tencent'), # Shuai Zhao + (110614, 'Huawei'), # Tal Mizrahi + (123395, 'APNIC'), # Tom Harrison + (116516, 'Juniper Networks'), # Tarek Saad + (11834, 'Futurewei USA'), # Toerless Eckert + (123962, 'Open-Xchange'), # Vittorio Bertola + (126530, 'Huawei'), # Yali Wang + (106199, 'Ericsson'), # Wassim Haddad + (125173, 'Huawei'), # Wei Pan + (111299, 'ZTE Corporation'), # Xiao Min + (113285, 'Huawei Technologies'), # XiPeng Xiao + (116337, 'Futurewei Technologies Inc.'), # Yingzhen Qu + (123974, 'ZTE'), # Zheng Zhang + (117500, 'Huawei'), # Guangying Zheng + (115934, 'Huawei Technologies'), # Haomian Zheng + (110966, 'Juniper'), # Zhaohui (Jeffrey) Zhang + ] + + for pk, affiliation in volunteers: + nc.volunteer_set.create(person_id=pk, affiliation=affiliation) + +def reverse(apps, schema_editor): + Volunteer = apps.get_model('nomcom','Volunteer') + Volunteer.objects.all().delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('nomcom', '0011_volunteers'), + ] + + operations = [ + migrations.RunPython(forward,reverse) + ] diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index ebb8c7abf..8ae1762c0 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -59,6 +59,8 @@ class NomCom(models.Model): help_text='Display pictures of each nominee (if available) on the feedback pages') show_accepted_nominees = models.BooleanField(verbose_name='Show accepted nominees', default=True, help_text='Show accepted nominees on the public nomination page') + is_accepting_volunteers = models.BooleanField(verbose_name="Accepting volunteers", default=False, + help_text='Is this nomcom is currently accepting volunteers?') first_call_for_volunteers = models.DateField(verbose_name='Date of the first call for volunteers', blank=True, null=True) class Meta: @@ -310,4 +312,12 @@ class TopicFeedbackLastSeen(models.Model): reviewer = ForeignKey(Person) topic = ForeignKey(Topic) time = models.DateTimeField(auto_now=True) + +class Volunteer(models.Model): + nomcom = ForeignKey('NomCom') + person = ForeignKey(Person) + affiliation = models.CharField(blank=True, max_length=255) + + def __str__(self): + return f'{self.person} for {self.nomcom}' diff --git a/ietf/nomcom/resources.py b/ietf/nomcom/resources.py index 61d101052..c87e72eae 100644 --- a/ietf/nomcom/resources.py +++ b/ietf/nomcom/resources.py @@ -12,7 +12,7 @@ from tastypie.cache import SimpleCache from ietf import api from ietf.nomcom.models import (NomCom, Position, Nominee, ReminderDates, NomineePosition, - Feedback, Nomination, FeedbackLastSeen, Topic, TopicFeedbackLastSeen, ) + Feedback, Nomination, FeedbackLastSeen, Topic, TopicFeedbackLastSeen, Volunteer, ) from ietf.group.resources import GroupResource class NomComResource(ModelResource): @@ -226,3 +226,22 @@ class TopicFeedbackLastSeenResource(ModelResource): "topic": ALL_WITH_RELATIONS, } api.nomcom.register(TopicFeedbackLastSeenResource()) + + +from ietf.person.resources import PersonResource +class VolunteerResource(ModelResource): + nomcom = ToOneField(NomComResource, 'nomcom') + person = ToOneField(PersonResource, 'person') + class Meta: + queryset = Volunteer.objects.all() + serializer = api.Serializer() + cache = SimpleCache() + #resource_name = 'volunteer' + ordering = ['id', ] + filtering = { + "id": ALL, + "affiliation": ALL, + "nomcom": ALL_WITH_RELATIONS, + "person": ALL_WITH_RELATIONS, + } +api.nomcom.register(VolunteerResource()) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 8c24c67da..acf6036e7 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -23,7 +23,8 @@ import debug # pyflakes:ignore from ietf.dbtemplate.factories import DBTemplateFactory from ietf.dbtemplate.models import DBTemplate -from ietf.doc.factories import DocEventFactory, WgDocumentAuthorFactory +from ietf.doc.factories import DocEventFactory, WgDocumentAuthorFactory, \ + NewRevisionDocEventFactory, DocumentAuthorFactory from ietf.group.factories import GroupFactory, GroupHistoryFactory, RoleFactory, RoleHistoryFactory from ietf.group.models import Group, Role from ietf.meeting.factories import MeetingFactory @@ -40,7 +41,7 @@ from ietf.nomcom.factories import NomComFactory, FeedbackFactory, TopicFactory, key from ietf.nomcom.utils import get_nomcom_by_year, make_nomineeposition, \ get_hash_nominee_position, is_eligible, list_eligible, \ - get_eligibility_date + get_eligibility_date, suggest_affiliation from ietf.person.factories import PersonFactory, EmailFactory from ietf.person.models import Email, Person from ietf.stats.models import MeetingRegistration @@ -1833,11 +1834,42 @@ Junk body for testing continue first_name, last_name = ascii.rsplit(None, 1) MeetingRegistration.objects.create(meeting=meeting, first_name=first_name, last_name=last_name, person=person, country_code='WO', email=email, attended=True) - # test the page - url = reverse('ietf.nomcom.views.eligible',kwargs={'year':self.nc.year()}) - login_testing_unauthorized(self,self.chair.user.username,url) - response = self.client.get(url) - self.assertEqual(response.status_code, 200) + for view in ('public_eligible','private_eligible'): + url = reverse(f'ietf.nomcom.views.{view}',kwargs={'year':self.nc.year()}) + for username in (self.chair.user.username,'secretary'): + login_testing_unauthorized(self,username,url) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.client.logout() + self.client.login(username='plain',password='plain+password') + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + + def test_volunteers(self): + year = self.nc.year() + def first_meeting_of_year(year): + assert isinstance(year, int) + assert year >= 1991 + return (year-1985)*3+2 + people = PersonFactory.create_batch(10) + meeting_start = first_meeting_of_year(year-2) + for number in range(meeting_start, meeting_start+8): + m = MeetingFactory.create(type_id='ietf', number=number) + for p in people: + m.meetingregistration_set.create(person=p) + for p in people: + self.nc.volunteer_set.create(person=p,affiliation='something') + for view in ('public_volunteers','private_volunteers'): + url = reverse(f'ietf.nomcom.views.{view}', kwargs=dict(year=self.nc.year())) + for username in (self.chair.user.username,'secretary'): + login_testing_unauthorized(self,username,url) + response = self.client.get(url) + self.assertContains(response,people[-1].email(),status_code=200) + self.client.logout() + self.client.login(username='plain',password='plain+password') + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + class NomComIndexTests(TestCase): @@ -2424,3 +2456,76 @@ class rfc8989EligibilityTests(TestCase): self.assertFalse(is_eligible(person,self.nomcom)) self.assertEqual(set(list_eligible(nomcom=self.nomcom)),set(eligible)) + +class VolunteerTests(TestCase): + + def test_volunteer(self): + url = reverse('ietf.nomcom.views.volunteer') + + person = PersonFactory() + login_testing_unauthorized(self, person.user.username, url) + r = self.client.get(url) + self.assertContains(r, 'NomCom is not accepting volunteers at this time', status_code=200) + + year = datetime.date.today().year + nomcom = NomComFactory(group__acronym=f'nomcom{year}', is_accepting_volunteers=False) + r = self.client.get(url) + self.assertContains(r, 'NomCom is not accepting volunteers at this time', status_code=200) + nomcom.is_accepting_volunteers = True + nomcom.save() + MeetingRegistrationFactory(person=person, affiliation='mtg_affiliation') + r = self.client.get(url) + self.assertContains(r, 'Volunteer for NomCom', status_code=200) + self.assertContains(r, 'mtg_affiliation') + r=self.client.post(url, dict(nomcoms=[nomcom.pk], affiliation='')) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(q('form div.has-error #id_affiliation')) + r=self.client.post(url, dict(nomcoms=[], affiliation='something')) + q = PyQuery(r.content) + self.assertTrue(q('form div.has-error #id_nomcoms')) + r=self.client.post(url, dict(nomcoms=[nomcom.pk], affiliation='something')) + self.assertRedirects(r, reverse('ietf.ietfauth.views.profile')) + self.assertEqual(person.volunteer_set.get(nomcom=nomcom).affiliation, 'something') + r=self.client.get(url) + self.assertContains(r, 'already volunteered', status_code=200) + + person.volunteer_set.all().delete() + nomcom2 = NomComFactory(group__acronym=f'nomcom{year-1}', is_accepting_volunteers=True) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#id_nomcoms div.checkbox')), 2) + r = self.client.post(url, dict(nomcoms=[nomcom.pk, nomcom2.pk], affiliation='something')) + self.assertRedirects(r, reverse('ietf.ietfauth.views.profile')) + self.assertEqual(person.volunteer_set.count(), 2) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertFalse(q('form div#id_nomcoms')) + self.assertIn(f'{nomcom.year()}/', q('#already-volunteered').text()) + self.assertIn(f'{nomcom2.year()}/', q('#already-volunteered').text()) + + person.volunteer_set.all().delete() + r=self.client.post(url, dict(nomcoms=[nomcom2.pk], affiliation='something')) + self.assertRedirects(r, reverse('ietf.ietfauth.views.profile')) + self.assertEqual(person.volunteer_set.count(), 1) + self.assertEqual(person.volunteer_set.first().nomcom, nomcom2) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#id_nomcoms div.checkbox')), 1) + self.assertNotIn(f'{nomcom.year()}/', q('#already-volunteered').text()) + self.assertIn(f'{nomcom2.year()}/', q('#already-volunteered').text()) + + def test_suggest_affiliation(self): + person = PersonFactory() + self.assertEqual(suggest_affiliation(person), '') + da = DocumentAuthorFactory(person=person,affiliation='auth_affil') + NewRevisionDocEventFactory(doc=da.document) + self.assertEqual(suggest_affiliation(person), 'auth_affil') + nc = NomComFactory() + nc.volunteer_set.create(person=person,affiliation='volunteer_affil') + self.assertEqual(suggest_affiliation(person), 'volunteer_affil') + MeetingRegistrationFactory(person=person, affiliation='meeting_affil') + self.assertEqual(suggest_affiliation(person), 'meeting_affil') diff --git a/ietf/nomcom/urls.py b/ietf/nomcom/urls.py index 0b66d7695..e5290d034 100644 --- a/ietf/nomcom/urls.py +++ b/ietf/nomcom/urls.py @@ -7,6 +7,7 @@ urlpatterns = [ url(r'^$', views.index), url(r'^ann/$', views.announcements), url(r'^history/$', views.history), + url(r'^volunteer/$', views.volunteer), url(r'^(?P\d{4})/private/$', views.private_index), url(r'^(?P\d{4})/private/key/$', views.private_key), url(r'^(?P\d{4})/private/help/$', views.configuration_help), @@ -37,7 +38,8 @@ urlpatterns = [ url(r'^(?P\d{4})/private/chair/topic/add/$', views.edit_topic), url(r'^(?P\d{4})/private/chair/topic/(?P\d+)/$', views.edit_topic), url(r'^(?P\d{4})/private/chair/topic/(?P\d+)/remove/$', views.remove_topic), - url(r'^(?P\d{4})/private/chair/eligible/$', views.eligible ), + url(r'^(?P\d{4})/private/chair/eligible/$', views.private_eligible), + url(r'^(?P\d{4})/private/chair/volunteers/$', views.private_volunteers), url(r'^(?P\d{4})/$', views.year_index), url(r'^(?P\d{4})/requirements/$', views.requirements), @@ -47,6 +49,8 @@ urlpatterns = [ url(r'^(?P\d{4})/nominate/$', views.public_nominate), url(r'^(?P\d{4})/nominate/newperson$', views.public_nominate_newperson), url(r'^(?P\d{4})/process-nomination-status/(?P\d+)/(?P[\w]+)/(?P[\d]+)/(?P[a-f0-9]+)/$', views.process_nomination_status), + url(r'^(?P\d{4})/eligible/$', views.public_eligible), + url(r'^(?P\d{4})/volunteers/$', views.public_volunteers), # use the generic view from message url(r'^ann/(?P\d+)/$', message_views.message, {'group_type': "nomcom" }), ] diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index 6a8787d52..99ff39321 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -22,7 +22,7 @@ from django.template.loader import render_to_string from django.shortcuts import get_object_or_404 from ietf.dbtemplate.models import DBTemplate -from ietf.doc.models import DocEvent +from ietf.doc.models import DocEvent, NewRevisionDocEvent from ietf.group.models import Group, Role from ietf.person.models import Email, Person from ietf.mailtrigger.utils import gather_address_lists @@ -589,4 +589,16 @@ def three_of_five_eligible(previous_five, queryset=None): queryset = Person.objects.all() return queryset.filter(meetingregistration__meeting__in=list(previous_five),meetingregistration__attended=True).annotate(mtg_count=Count('meetingregistration')).filter(mtg_count__gte=3) +def suggest_affiliation(person): + recent_meeting = person.meetingregistration_set.order_by('-meeting__date').first() + affiliation = recent_meeting.affiliation if recent_meeting else '' + if not affiliation: + recent_volunteer = person.volunteer_set.order_by('-nomcom__group__acronym').first() + if recent_volunteer: + affiliation = recent_volunteer.affiliation + if not affiliation: + recent_draft_revision = NewRevisionDocEvent.objects.filter(doc__type_id='draft',doc__documentauthor__person=person).order_by('-time').first() + if recent_draft_revision: + affiliation = recent_draft_revision.doc.documentauthor_set.filter(person=person).first().affiliation + return affiliation diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index ca1c8eff3..a2dfcbc37 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -11,7 +11,7 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.models import AnonymousUser from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.forms.models import modelformset_factory, inlineformset_factory +from django.forms.models import modelformset_factory, inlineformset_factory from django.http import Http404, HttpResponseRedirect from django.shortcuts import render, get_object_or_404, redirect from django.template.loader import render_to_string @@ -31,10 +31,10 @@ from ietf.nomcom.forms import (NominateForm, NominateNewPersonForm, FeedbackForm PrivateKeyForm, EditNomcomForm, EditNomineeForm, PendingFeedbackForm, ReminderDatesForm, FullFeedbackFormSet, FeedbackEmailForm, NominationResponseCommentForm, TopicForm, - NewEditMembersForm,) + NewEditMembersForm, VolunteerForm, ) from ietf.nomcom.models import (Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates, FeedbackLastSeen, Topic, TopicFeedbackLastSeen, ) -from ietf.nomcom.utils import (get_nomcom_by_year, store_nomcom_private_key, +from ietf.nomcom.utils import (get_nomcom_by_year, store_nomcom_private_key, suggest_affiliation, get_hash_nominee_position, send_reminder_to_nominees, list_eligible, HOME_TEMPLATE, NOMINEE_ACCEPT_REMINDER_TEMPLATE,NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE, ) @@ -1269,8 +1269,33 @@ def extract_email_lists(request, year): 'bypos': bypos, }) +@login_required +def volunteer(request): + nomcoms = NomCom.objects.filter(is_accepting_volunteers=True) + if not nomcoms.exists(): + return render(request, 'nomcom/volunteers_not_accepted.html') + person = request.user.person + already_volunteered = nomcoms.filter(volunteer__person=person) + can_volunteer = nomcoms.exclude(volunteer__person=person) + + if request.method=='POST': + form = VolunteerForm(person=person, data=request.POST) + if form.is_valid(): + for nc in form.cleaned_data['nomcoms']: + nc.volunteer_set.create(person=person, affiliation=form.cleaned_data['affiliation']) + return redirect('ietf.ietfauth.views.profile') + else: + form = VolunteerForm(person=person,initial=dict(nomcoms=can_volunteer, affiliation=suggest_affiliation(person))) + return render(request, 'nomcom/volunteer.html', {'form':form, 'can_volunteer': can_volunteer, 'already_volunteered': already_volunteered}) + +def public_eligible(request, year): + return eligible(request=request, year=year, public=True) + +def private_eligible(request, year): + return eligible(request=request, year=year, public=False) + @role_required("Nomcom Chair", "Nomcom Advisor", "Secretariat") -def eligible(request, year): +def eligible(request, year, public=False): nomcom = get_nomcom_by_year(year) eligible_persons = list(list_eligible(nomcom=nomcom)) @@ -1281,3 +1306,23 @@ def eligible(request, year): 'year':year, 'eligible_persons':eligible_persons, }) + +def public_volunteers(request, year): + return volunteers(request=request, year=year, public=True) + +def private_volunteers(request, year): + return volunteers(request=request, year=year, public=False) + +@role_required("Nomcom Chair", "Nomcom Advisor", "Secretariat") +def volunteers(request, year, public=False): + nomcom = get_nomcom_by_year(year) + # pull list of volunteers + # get queryset of all eligible (from utils) + # decorate members of the list with eligibility + volunteers = nomcom.volunteer_set.all() + eligible = list_eligible(nomcom) + for v in volunteers: + v.eligible = v.person in eligible + volunteers = sorted(volunteers,key=lambda v:(not v.eligible,v.person.last_name())) + return render(request, 'nomcom/volunteers.html', dict(year=year, nomcom=nomcom, volunteers=volunteers, public=public)) + diff --git a/ietf/templates/nomcom/eligible.html b/ietf/templates/nomcom/eligible.html index 8c82e1f44..c44cad5bb 100644 --- a/ietf/templates/nomcom/eligible.html +++ b/ietf/templates/nomcom/eligible.html @@ -1,4 +1,4 @@ -{% extends "nomcom/nomcom_private_base.html" %} +{% extends public|yesno:"nomcom/nomcom_public_base.html,nomcom/nomcom_private_base.html" %} {# Copyright The IETF Trust 2015, All Rights Reserved #} {% load origin %} {% load bootstrap3 %} diff --git a/ietf/templates/nomcom/nomcom_private_base.html b/ietf/templates/nomcom/nomcom_private_base.html index 83fc000fc..2e1fe6e2f 100644 --- a/ietf/templates/nomcom/nomcom_private_base.html +++ b/ietf/templates/nomcom/nomcom_private_base.html @@ -3,6 +3,7 @@ {% load origin %} {% load nomcom_tags %} +{% load ietf_filters %} {% block title %}NomCom {{ year }} Private{% block subtitle %}{% endblock %}{% endblock %} @@ -46,7 +47,6 @@ {% if nomcom.group.state_id == 'active' %}
  • Edit Members
  • {% endif %} -
  • View Eligible
  • Configuration Help
  • Announcement Tool
  • @@ -55,6 +55,15 @@ {% endif %} + {% if user|is_chair_or_advisor:year or user|has_role:"Secretariat" %} + + {% endif %} {% block nomcom_content %} diff --git a/ietf/templates/nomcom/nomcom_public_base.html b/ietf/templates/nomcom/nomcom_public_base.html index 355c7d9f8..9f85268bf 100644 --- a/ietf/templates/nomcom/nomcom_public_base.html +++ b/ietf/templates/nomcom/nomcom_public_base.html @@ -3,6 +3,7 @@ {% load origin %} {% load nomcom_tags %} +{% load ietf_filters %} {% block title %}NomCom {{ year }}{% block subtitle %}{% endblock %}{% endblock %} @@ -19,6 +20,15 @@ {% endif %}
  • Desired expertise
  • Questionnaires
  • + {% if user|is_chair_or_advisor:year or user|has_role:"Secretariat" %} + + {% endif %} {% block nomcom_content %} diff --git a/ietf/templates/nomcom/volunteer.html b/ietf/templates/nomcom/volunteer.html new file mode 100644 index 000000000..074b5641d --- /dev/null +++ b/ietf/templates/nomcom/volunteer.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2021, All Rights Reserved #} +{% load origin %} +{% load bootstrap3 %} + +{% block title %}Volunteer for NomCom{% endblock %} + +{% block content %} + {% origin %} +

    Volunteer for NomCom

    + + {% if can_volunteer.exists %} +
    + {% csrf_token %} + + {% bootstrap_form form %} + + {% buttons %} + + {% endbuttons %} +
    + {% endif %} + + {% if already_volunteered.exists %} +
    + You have already volunteered for the {% for nc in already_volunteered %}{{nc.year}}/{{nc.year|add:1}}{% if not forloop.last %}, {% endif %}{% endfor %} Nominating Committee{{already_volunteered|pluralize}}. + To modify your volunteer status, contact the NomCom chair. +
    + + {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/ietf/templates/nomcom/volunteers.html b/ietf/templates/nomcom/volunteers.html new file mode 100644 index 000000000..b64036c11 --- /dev/null +++ b/ietf/templates/nomcom/volunteers.html @@ -0,0 +1,40 @@ +{% extends public|yesno:"nomcom/nomcom_public_base.html,nomcom/nomcom_private_base.html" %} +{# Copyright The IETF Trust 2021, All Rights Reserved #} +{% load origin %} +{% load bootstrap3 %} +{% load static %} + +{% block subtitle %} - Volunteers{% endblock %} + +{% block pagehead %} + +{% endblock %} + +{% block nomcom_content %} + {% origin %} +

    Volunteers for {{ nomcom.group }}

    + + + + + + + + + + + {% for v in volunteers %} + + + + + + + {% endfor %} +
    EligibleLast NameFirst NameAffiliationEmail Addresses
    {{v.eligible|yesno}} + {{v.person.last_name}}{{v.person.first_name}}{{v.affiliation}}{% for e in v.person.email_set.all %}{{e.address}}{% if not forloop.last %}, {% endif %}{% endfor %}
    +{% endblock nomcom_content %} + +{% block js %} + +{% endblock %} diff --git a/ietf/templates/nomcom/volunteers_not_accepted.html b/ietf/templates/nomcom/volunteers_not_accepted.html new file mode 100644 index 000000000..95321812c --- /dev/null +++ b/ietf/templates/nomcom/volunteers_not_accepted.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2021, All Rights Reserved #} +{% load origin %} + +{% block title %}Not Accepting Volunteers{% endblock %} + +{% block content %} + {% origin %} +

    NomCom is not accepting volunteers at this time

    +{% endblock %} \ No newline at end of file diff --git a/ietf/templates/registration/edit_profile.html b/ietf/templates/registration/edit_profile.html index 09fe7d1f8..a1abe3713 100644 --- a/ietf/templates/registration/edit_profile.html +++ b/ietf/templates/registration/edit_profile.html @@ -66,7 +66,14 @@
    -
    {{person|is_nomcom_eligible|yesno:'Yes,No,No'}}
    +
    + {{person|is_nomcom_eligible|yesno:'Yes,No,No'}} + {% if volunteer_status == 'allow' %} +
    + Volunteer + {% endif %} +
    +

    If you believe this calculation is incorrect, make sure you've added all the @@ -80,6 +87,9 @@ for eligibility requirements. For the 2021 nomcom, see also RFC 8989. + {% if volunteer_status == 'volunteered' %} +
    You have volunteered for the {{nomcom.group.name}}. To modify your volunteer status, contact the NomCom chair. + {% endif %}

    @@ -89,7 +99,9 @@
    {% for extres in person.personextresource_set.all %}
    -
    {% firstof extres.display_name extres.name.name %}
    +
    + {% firstof extres.display_name extres.name.name %} +
    {{extres.value}} {% if forloop.first %} {% endif %}