Repository: ietf-tools/datatracker

Issue:
Capture volunteers from the registration system
Currently, the registration is not automatically telling the datatracker about people who volunteer for nomcom as part of the registration process. 

See also #5937 


Discussion:

Resolving patch:
diff --git a/ietf/api/views.py b/ietf/api/views.py
--- 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 json
 import pytz
 import re
@@ -38,6 +37,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 +140,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 +202,19 @@ 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:
+                try:
+                    nomcom = NomCom.objects.get(is_accepting_volunteers=True)
+                except (NomCom.DoesNotExist, NomCom.MultipleObjectsReturned):
+                    nomcom = None
+                if nomcom:
+                    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
--- /dev/null
+++ b/ietf/nomcom/migrations/0004_volunteer_origin_volunteer_time_volunteer_withdrawn.py
@@ -0,0 +1,27 @@
+# Generated by Django 4.2.7 on 2023-11-05 09:45
+
+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_add=True, null=True, blank=True),
+        ),
+        migrations.AddField(
+            model_name="volunteer",
+            name="withdrawn",
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+    ]
diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py
--- 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_add=True, null=True, blank=True)
+    origin = models.CharField(max_length=32, default='datatracker')
+    withdrawn = models.DateTimeField(blank=True, null=True)
+    
     def __str__(self):
         return f'{self.person} for {self.nomcom}'
     


Test patch:
diff --git a/ietf/api/tests.py b/ietf/api/tests.py
--- 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')