Repository: ietf-tools/datatracker

Issue:
Allow secretariat to create sessions without sending a confirmation email
### Description

Based on a conversation with @flynnliz, the current process for scheduling meeting sessions for various non-"regular" sessions results in notification emails being sent when the session request is created. For nuts and bolts agenda items (breaks, etc), these are unnecessary and cause confusion.

If the method for creating these sessions continues to involve the `secr.sreq` app, it should skip sending these emails. (If we streamline the workflow to avoid separately creating the session reqs, then we can probably close this without changing the secr app.)

### Code of Conduct

- [X] I agree to follow the [IETF's Code of Conduct](https://github.com/ietf-tools/.github/blob/main/CODE_OF_CONDUCT.md)


Discussion:

Resolving patch:
diff --git a/ietf/secr/sreq/forms.py b/ietf/secr/sreq/forms.py
--- a/ietf/secr/sreq/forms.py
+++ b/ietf/secr/sreq/forms.py
@@ -80,17 +80,18 @@ class SessionForm(forms.Form):
     timeranges    = NameModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False,
                                                  queryset=TimerangeName.objects.all())
     adjacent_with_wg = forms.ChoiceField(required=False)
+    send_notifications = forms.BooleanField(label="Send notification emails?", required=False, initial=False)
 
     def __init__(self, group, meeting, data=None, *args, **kwargs):
-        if 'hidden' in kwargs:
-            self.hidden = kwargs.pop('hidden')
-        else:
-            self.hidden = False
+        self.hidden = kwargs.pop('hidden', False)
+        self.notifications_optional = kwargs.pop('notifications_optional', False)
 
         self.group = group
         formset_class = sessiondetailsformset_factory(max_num=3 if group.features.acts_like_wg else 50)
         self.session_forms = formset_class(group=self.group, meeting=meeting, data=data)
         super(SessionForm, self).__init__(data=data, *args, **kwargs)
+        if not self.notifications_optional:
+            self.fields['send_notifications'].widget = forms.HiddenInput()
 
         # Allow additional sessions for non-wg-like groups
         if not self.group.features.acts_like_wg:
@@ -234,6 +235,9 @@ def clean_joint_with_groups(self):
     def clean_comments(self):
         return clean_text_field(self.cleaned_data['comments'])
 
+    def clean_send_notifications(self):
+        return True if not self.notifications_optional else self.cleaned_data['send_notifications']
+
     def is_valid(self):
         return super().is_valid() and self.session_forms.is_valid()
 
diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py
--- a/ietf/secr/sreq/views.py
+++ b/ietf/secr/sreq/views.py
@@ -288,7 +288,7 @@ def confirm(request, acronym):
     if len(group.features.session_purposes) == 0:
         raise Http404(f'Cannot request sessions for group "{acronym}"')
     meeting = get_meeting(days=14)
-    form = SessionForm(group, meeting, request.POST, hidden=True)
+    form = SessionForm(group, meeting, request.POST, hidden=True, notifications_optional=has_role(request.user, "Secretariat"))
     form.is_valid()
 
     login = request.user.person
@@ -366,15 +366,16 @@ def confirm(request, acronym):
         add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(current_status='notmeet').delete()
 
         # send notification
-        session_data['outbound_conflicts'] = [f"{d['name']}: {d['groups']}" for d in outbound_conflicts]
-        send_notification(
-            group,
-            meeting,
-            login,
-            session_data,
-            [sf.cleaned_data for sf in form.session_forms[:num_sessions]],
-            'new',
-        )
+        if form.cleaned_data.get("send_notifications"):
+            session_data['outbound_conflicts'] = [f"{d['name']}: {d['groups']}" for d in outbound_conflicts]
+            send_notification(
+                group,
+                meeting,
+                login,
+                session_data,
+                [sf.cleaned_data for sf in form.session_forms[:num_sessions]],
+                'new',
+            )
 
         status_text = 'IETF Agenda to be scheduled'
         messages.success(request, 'Your request has been sent to %s' % status_text)
@@ -385,7 +386,6 @@ def confirm(request, acronym):
         outbound=outbound_conflicts,  # each is a dict with name and groups as keys
         inbound=inbound_session_conflicts_as_string(group, meeting),
     )
-
     return render(request, 'sreq/confirm.html', {
         'form': form,
         'session': session_data,
@@ -449,7 +449,7 @@ def edit(request, acronym, num=None):
         if button_text == 'Cancel':
             return redirect('ietf.secr.sreq.views.view', acronym=acronym)
 
-        form = SessionForm(group, meeting, request.POST, initial=initial)
+        form = SessionForm(group, meeting, request.POST, initial=initial, notifications_optional=has_role(request.user, "Secretariat"))
         if form.is_valid():
             if form.has_changed():
                 changed_session_forms = [sf for sf in form.session_forms.forms_to_keep if sf.has_changed()]
@@ -540,17 +540,18 @@ def edit(request, acronym, num=None):
                 #add_session_activity(group,'Session Request was updated',meeting,user)
 
                 # send notification
-                outbound_conflicts = get_outbound_conflicts(form)
-                session_data = form.cleaned_data.copy()  # do not add things to the original cleaned_data
-                session_data['outbound_conflicts'] = [f"{d['name']}: {d['groups']}" for d in outbound_conflicts]
-                send_notification(
-                    group,
-                    meeting,
-                    login,
-                    session_data,
-                    [sf.cleaned_data for sf in form.session_forms.forms_to_keep],
-                    'update',
-                )
+                if form.cleaned_data.get("send_notifications"):
+                    outbound_conflicts = get_outbound_conflicts(form)
+                    session_data = form.cleaned_data.copy()  # do not add things to the original cleaned_data
+                    session_data['outbound_conflicts'] = [f"{d['name']}: {d['groups']}" for d in outbound_conflicts]
+                    send_notification(
+                        group,
+                        meeting,
+                        login,
+                        session_data,
+                        [sf.cleaned_data for sf in form.session_forms.forms_to_keep],
+                        'update',
+                    )
 
             messages.success(request, 'Session Request updated')
             return redirect('ietf.secr.sreq.views.view', acronym=acronym)
@@ -565,7 +566,7 @@ def edit(request, acronym, num=None):
 
         if not sessions:
             return redirect('ietf.secr.sreq.views.new', acronym=acronym)
-        form = SessionForm(group, meeting, initial=initial)
+        form = SessionForm(group, meeting, initial=initial, notifications_optional=has_role(request.user, "Secretariat"))
 
     return render(request, 'sreq/edit.html', {
         'is_locked': is_locked and not has_role(request.user,'Secretariat'),
@@ -665,7 +666,7 @@ def new(request, acronym):
         if button_text == 'Cancel':
             return redirect('ietf.secr.sreq.views.main')
 
-        form = SessionForm(group, meeting, request.POST)
+        form = SessionForm(group, meeting, request.POST, notifications_optional=has_role(request.user, "Secretariat"))
         if form.is_valid():
             return confirm(request, acronym)
 
@@ -689,12 +690,12 @@ def new(request, acronym):
         add_essential_people(group,initial)
         if 'resources' in initial:
             initial['resources'] = [x.pk for x in initial['resources']]
-        form = SessionForm(group, meeting, initial=initial)
+        form = SessionForm(group, meeting, initial=initial, notifications_optional=has_role(request.user, "Secretariat"))
 
     else:
         initial={}
         add_essential_people(group,initial)
-        form = SessionForm(group, meeting, initial=initial)
+        form = SessionForm(group, meeting, initial=initial, notifications_optional=has_role(request.user, "Secretariat"))
 
     return render(request, 'sreq/new.html', {
         'meeting': meeting,


Test patch: