From f3bd5ce4568af1cc4f5396856c156b6a5d306ea4 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Sun, 5 Nov 2023 09:37:02 +0100 Subject: [PATCH 1/6] feat: Capture volunteers from the registration system. Fixes #5938 --- ietf/api/tests.py | 47 +++++++++++++++++++ ietf/api/views.py | 24 +++++++++- ...igin_volunteer_time_volunteer_withdrawn.py | 33 +++++++++++++ ietf/nomcom/models.py | 5 +- 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py diff --git a/ietf/api/tests.py b/ietf/api/tests.py index 4c1440882d..3ed8986523 100644 --- a/ietf/api/tests.py +++ b/ietf/api/tests.py @@ -28,6 +28,8 @@ from ietf.group.factories import RoleFactory from ietf.meeting.factories import MeetingFactory, SessionFactory from ietf.meeting.models import Session +from ietf.nomcom.models import Volunteer, NomCom +from ietf.nomcom.factories import NomComFactory, nomcom_kwargs_for_year from ietf.person.factories import PersonFactory, random_faker from ietf.person.models import User from ietf.person.models import PersonalApiKey @@ -630,6 +632,7 @@ def test_api_new_meeting_registration(self): 'reg_type': 'hackathon', 'ticket_type': '', 'checkedin': 'False', + 'is_nomcom_volunteer': 'False', } url = urlreverse('ietf.api.views.api_new_meeting_registration') r = self.client.post(url, reg) @@ -691,6 +694,50 @@ def test_api_new_meeting_registration(self): missing_fields = [f.strip() for f in fields.split(',')] self.assertEqual(set(missing_fields), set(drop_fields)) + def test_api_new_meeting_registration_nomcom_volunteer(self): + '''Test that Volunteer is created if is_nomcom_volunteer=True + is submitted to API + ''' + meeting = MeetingFactory(type_id='ietf') + reg = { + 'apikey': 'invalid', + 'affiliation': "Alguma Corporação", + 'country_code': 'PT', + 'meeting': meeting.number, + 'reg_type': 'onsite', + 'ticket_type': '', + 'checkedin': 'False', + 'is_nomcom_volunteer': 'True', + } + person = PersonFactory() + reg['email'] = person.email().address + reg['first_name'] = person.first_name() + reg['last_name'] = person.last_name() + now = datetime.datetime.now() + if now.month > 10: + year = now.year + 1 + else: + year = now.year + # create appropriate group and nomcom objects + nomcom = NomComFactory.create(is_accepting_volunteers=True, **nomcom_kwargs_for_year(year)) + url = urlreverse('ietf.api.views.api_new_meeting_registration') + r = self.client.post(url, reg) + self.assertContains(r, 'Invalid apikey', status_code=403) + oidcp = PersonFactory(user__is_staff=True) + # Make sure 'oidcp' has an acceptable role + RoleFactory(name_id='robot', person=oidcp, email=oidcp.email(), group__acronym='secretariat') + key = PersonalApiKey.objects.create(person=oidcp, endpoint=url) + reg['apikey'] = key.hash() + r = self.client.post(url, reg) + nomcom = NomCom.objects.last() + self.assertContains(r, "Accepted, New registration", status_code=202) + # assert Volunteer exists + self.assertEqual(Volunteer.objects.count(), 1) + volunteer = Volunteer.objects.last() + self.assertEqual(volunteer.person, person) + self.assertEqual(volunteer.nomcom, nomcom) + self.assertEqual(volunteer.origin, 'registration') + def test_api_version(self): DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=datetime.timezone.utc), host='testapi.example.com',tz='UTC') url = urlreverse('ietf.api.views.version') diff --git a/ietf/api/views.py b/ietf/api/views.py index df7fdb2c8a..6423b335d9 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -1,7 +1,7 @@ # Copyright The IETF Trust 2017-2020, All Rights Reserved # -*- coding: utf-8 -*- - +import datetime import json import pytz import re @@ -38,6 +38,7 @@ from ietf.ietfauth.views import send_account_creation_email from ietf.ietfauth.utils import role_required from ietf.meeting.models import Meeting +from ietf.nomcom.models import Volunteer, NomCom from ietf.stats.models import MeetingRegistration from ietf.utils import log from ietf.utils.decorators import require_api_key @@ -140,7 +141,7 @@ def api_new_meeting_registration(request): def err(code, text): return HttpResponse(text, status=code, content_type='text/plain') required_fields = [ 'meeting', 'first_name', 'last_name', 'affiliation', 'country_code', - 'email', 'reg_type', 'ticket_type', 'checkedin'] + 'email', 'reg_type', 'ticket_type', 'checkedin', 'is_nomcom_volunteer'] fields = required_fields + [] if request.method == 'POST': # parameters: @@ -202,6 +203,25 @@ def err(code, text): else: send_account_creation_email(request, email) response += ", Email sent" + + # handle nomcom volunteer + if data['is_nomcom_volunteer'] and object.person: + now = datetime.datetime.now() + month = now.month + year = now.year + if month > 7: + year = year + 1 + try: + nomcom = NomCom.objects.get(group__acronym__icontains=year) + except NomCom.DoesNotExist: + nomcom = None + # log warning + if nomcom and nomcom.is_accepting_volunteers: + Volunteer.objects.create( + nomcom=nomcom, + person=object.person, + affiliation=data['affiliation'], + origin='registration') return HttpResponse(response, status=202, content_type='text/plain') else: return HttpResponse(status=405) diff --git a/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py new file mode 100644 index 0000000000..6d18014eca --- /dev/null +++ b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.7 on 2023-11-05 08:20 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("nomcom", "0003_alter_nomination_share_nominator"), + ] + + operations = [ + migrations.AddField( + model_name="volunteer", + name="origin", + field=models.CharField(default="datatracker", max_length=32), + ), + migrations.AddField( + model_name="volunteer", + name="time", + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name="volunteer", + name="withdrawn", + field=models.DateTimeField( + default=datetime.datetime( + 2023, 11, 5, 0, 0, tzinfo=datetime.timezone.utc + ) + ), + preserve_default=False, + ), + ] diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index ee2eea2cca..d74ba0c780 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -327,7 +327,10 @@ class Volunteer(models.Model): nomcom = ForeignKey('NomCom') person = ForeignKey(Person) affiliation = models.CharField(blank=True, max_length=255) - + time = models.DateTimeField(auto_now=True) + origin = models.CharField(max_length=32, default='datatracker') + withdrawn = models.DateTimeField() + def __str__(self): return f'{self.person} for {self.nomcom}' From 15d286d78244238318dccea20521ea9f07b1e649 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Sun, 5 Nov 2023 10:47:02 +0100 Subject: [PATCH 2/6] fix: allow withdrawn field to be null --- ...unteer_origin_volunteer_time_volunteer_withdrawn.py | 10 ++-------- ietf/nomcom/models.py | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py index 6d18014eca..2e180a9d9f 100644 --- a/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py +++ b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py @@ -1,6 +1,5 @@ -# Generated by Django 4.2.7 on 2023-11-05 08:20 +# Generated by Django 4.2.7 on 2023-11-05 09:45 -import datetime from django.db import migrations, models @@ -23,11 +22,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name="volunteer", name="withdrawn", - field=models.DateTimeField( - default=datetime.datetime( - 2023, 11, 5, 0, 0, tzinfo=datetime.timezone.utc - ) - ), - preserve_default=False, + field=models.DateTimeField(blank=True, null=True), ), ] diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index d74ba0c780..55743cd43d 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -329,7 +329,7 @@ class Volunteer(models.Model): affiliation = models.CharField(blank=True, max_length=255) time = models.DateTimeField(auto_now=True) origin = models.CharField(max_length=32, default='datatracker') - withdrawn = models.DateTimeField() + withdrawn = models.DateTimeField(blank=True, null=True) def __str__(self): return f'{self.person} for {self.nomcom}' From d2d7fe4e40b19543d1b2a34a8cabf15eb09e07c3 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Mon, 6 Nov 2023 10:45:38 +0100 Subject: [PATCH 3/6] docs: add explanatory code comments --- ietf/api/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/api/views.py b/ietf/api/views.py index 6423b335d9..52d4181d73 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -206,6 +206,8 @@ def err(code, text): # handle nomcom volunteer if data['is_nomcom_volunteer'] and object.person: + # registrants for the fall meeting will be volunteering for the + # following year's nomcom now = datetime.datetime.now() month = now.month year = now.year From 7f06d7009d54fc968ff51dd2bbc5dd96ececf471 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Wed, 22 Nov 2023 13:22:47 -0800 Subject: [PATCH 4/6] fix: identify current nomcom by is_accepting_volunteers --- ietf/api/views.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ietf/api/views.py b/ietf/api/views.py index 52d4181d73..31919887da 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -206,19 +206,11 @@ def err(code, text): # handle nomcom volunteer if data['is_nomcom_volunteer'] and object.person: - # registrants for the fall meeting will be volunteering for the - # following year's nomcom - now = datetime.datetime.now() - month = now.month - year = now.year - if month > 7: - year = year + 1 try: - nomcom = NomCom.objects.get(group__acronym__icontains=year) - except NomCom.DoesNotExist: + nomcom = NomCom.objects.get(is_accepting_volunteers=True) + except (NomCom.DoesNotExist, NomCom.MultipleObjectsReturned): nomcom = None - # log warning - if nomcom and nomcom.is_accepting_volunteers: + if nomcom: Volunteer.objects.create( nomcom=nomcom, person=object.person, From e68af4b2ca94d8884f3a6b29035889cb5d26828f Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Tue, 28 Nov 2023 08:40:36 -0800 Subject: [PATCH 5/6] fix: use auto_now_add instead of auto_now --- ietf/api/views.py | 1 - ietf/nomcom/models.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ietf/api/views.py b/ietf/api/views.py index 31919887da..2bd7f350a1 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -1,7 +1,6 @@ # Copyright The IETF Trust 2017-2020, All Rights Reserved # -*- coding: utf-8 -*- -import datetime import json import pytz import re diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index 55743cd43d..51006a227e 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -327,7 +327,7 @@ class Volunteer(models.Model): nomcom = ForeignKey('NomCom') person = ForeignKey(Person) affiliation = models.CharField(blank=True, max_length=255) - time = models.DateTimeField(auto_now=True) + time = models.DateTimeField(auto_now_add=True, null=True, blank=True) origin = models.CharField(max_length=32, default='datatracker') withdrawn = models.DateTimeField(blank=True, null=True) From 7b9843561e7b68c39473677a11a6edc06551aff3 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Mon, 11 Dec 2023 10:22:39 -0800 Subject: [PATCH 6/6] fix: update migration --- .../0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py index 2e180a9d9f..9eaebf2069 100644 --- a/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py +++ b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py @@ -17,7 +17,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="volunteer", name="time", - field=models.DateTimeField(auto_now=True), + field=models.DateTimeField(auto_now_add=True, null=True, blank=True), ), migrations.AddField( model_name="volunteer",