Skip to content

Commit 5318081

Browse files
Allow non-WG-like groups to request additional sessions/durations and bypass approval
- Legacy-Id: 19424
1 parent 1054f90 commit 5318081

6 files changed

Lines changed: 83 additions & 100 deletions

File tree

ietf/meeting/forms.py

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -531,27 +531,49 @@ def _day_choices(days):
531531

532532

533533
class DurationChoiceField(forms.ChoiceField):
534-
duration_choices = (('3600', '60 minutes'), ('7200', '120 minutes'))
535-
536-
# todo expand range of choices and make configurable
537-
def __init__(self, *args, **kwargs):
534+
def __init__(self, durations=None, *args, **kwargs):
535+
if durations is None:
536+
durations = (3600, 7200)
538537
super().__init__(
539-
choices=(('','--Please select'), *self.duration_choices),
538+
choices=self._make_choices(durations),
540539
*args, **kwargs,
541540
)
542541

543542
def prepare_value(self, value):
544543
"""Converts incoming value into string used for the option value"""
545544
if value:
546-
return str(int(value.total_seconds())) if hasattr(value, 'total_seconds') else str(value)
545+
return str(int(value.total_seconds())) if isinstance(value, datetime.timedelta) else str(value)
547546
return ''
548547

549-
def clean(self, value):
550-
"""Convert option value string back to a timedelta"""
551-
val = super().clean(value)
552-
if val == '':
553-
return None
554-
return datetime.timedelta(seconds=int(val))
548+
def to_python(self, value):
549+
return datetime.timedelta(seconds=round(float(value))) if value not in self.empty_values else None
550+
551+
def valid_value(self, value):
552+
return super().valid_value(self.prepare_value(value))
553+
554+
def _format_duration_choice(self, dur):
555+
seconds = int(dur.total_seconds()) if isinstance(dur, datetime.timedelta) else int(dur)
556+
hours = int(seconds / 3600)
557+
minutes = round((seconds - 3600 * hours) / 60)
558+
hr_str = '{} hour{}'.format(hours, '' if hours == 1 else 's')
559+
min_str = '{} minute{}'.format(minutes, '' if minutes == 1 else 's')
560+
if hours > 0 and minutes > 0:
561+
time_str = ' '.join((hr_str, min_str))
562+
elif hours > 0:
563+
time_str = hr_str
564+
else:
565+
time_str = min_str
566+
return (str(seconds), time_str)
567+
568+
def _make_choices(self, durations):
569+
return (
570+
('','--Please select'),
571+
*[self._format_duration_choice(dur) for dur in durations])
572+
573+
def _set_durations(self, durations):
574+
self.choices = self._make_choices(durations)
575+
576+
durations = property(None, _set_durations)
555577

556578

557579
class SessionDetailsForm(forms.ModelForm):
@@ -573,6 +595,8 @@ def __init__(self, group, *args, **kwargs):
573595
}),
574596
})
575597
self.fields['purpose'].queryset = SessionPurposeName.objects.filter(pk__in=session_purposes)
598+
if not group.features.acts_like_wg:
599+
self.fields['requested_duration'].durations = [datetime.timedelta(minutes=m) for m in range(30, 241, 30)]
576600

577601
class Meta:
578602
model = Session
@@ -606,7 +630,7 @@ def __init__(self, group, meeting, queryset=None, *args, **kwargs):
606630

607631
def save_new(self, form, commit=True):
608632
form.instance.meeting = self._meeting
609-
super().save_new(form, commit)
633+
return super().save_new(form, commit)
610634

611635
def save(self, commit=True):
612636
existing_instances = set(form.instance for form in self.forms if form.instance.pk)
@@ -619,14 +643,15 @@ def forms_to_keep(self):
619643
"""Get the not-deleted forms"""
620644
return [f for f in self.forms if f not in self.deleted_forms]
621645

622-
SessionDetailsFormSet = forms.inlineformset_factory(
623-
Group,
624-
Session,
625-
formset=SessionDetailsInlineFormset,
626-
form=SessionDetailsForm,
627-
can_delete=True,
628-
can_order=False,
629-
min_num=1,
630-
max_num=3,
631-
extra=3,
632-
)
646+
def sessiondetailsformset_factory(min_num=1, max_num=3):
647+
return forms.inlineformset_factory(
648+
Group,
649+
Session,
650+
formset=SessionDetailsInlineFormset,
651+
form=SessionDetailsForm,
652+
can_delete=True,
653+
can_order=False,
654+
min_num=min_num,
655+
max_num=max_num,
656+
extra=max_num, # only creates up to max_num total
657+
)

ietf/secr/sreq/forms.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from ietf.name.models import TimerangeName, ConstraintName
1010
from ietf.group.models import Group
11-
from ietf.meeting.forms import SessionDetailsFormSet
11+
from ietf.meeting.forms import sessiondetailsformset_factory
1212
from ietf.meeting.models import ResourceAssociation, Constraint
1313
from ietf.person.fields import SearchablePersonsField
1414
from ietf.utils.html import clean_text_field
@@ -90,9 +90,12 @@ def __init__(self, group, meeting, data=None, *args, **kwargs):
9090
self.hidden = False
9191

9292
self.group = group
93-
self.session_forms = SessionDetailsFormSet(group=self.group, meeting=meeting, data=data)
93+
formset_class = sessiondetailsformset_factory(max_num=3 if group.features.acts_like_wg else 12)
94+
self.session_forms = formset_class(group=self.group, meeting=meeting, data=data)
9495
super(SessionForm, self).__init__(data=data, *args, **kwargs)
9596

97+
if not self.group.features.acts_like_wg:
98+
self.fields['num_session'].choices = ((n, str(n)) for n in range(1, 13))
9699
self.fields['comments'].widget = forms.Textarea(attrs={'rows':'3','cols':'65'})
97100

98101
other_groups = list(allowed_conflicting_groups().exclude(pk=group.pk).values_list('acronym', 'acronym').order_by('acronym'))

ietf/secr/sreq/views.py

Lines changed: 22 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def get_initial_session(sessions, prune_conflicts=False):
6161
conflicts = constraints.filter(name__is_group_conflict=True) # only the group conflict constraints
6262

6363
# even if there are three sessions requested, the old form has 2 in this field
64-
initial['num_session'] = min(sessions.count(), 2)
64+
initial['num_session'] = min(sessions.count(), 2) if group.features.acts_like_wg else sessions.count()
6565
initial['attendees'] = sessions[0].attendees
6666

6767
def valid_conflict(conflict):
@@ -259,6 +259,13 @@ def cancel(request, acronym):
259259
messages.success(request, 'The %s Session Request has been cancelled' % group.acronym)
260260
return redirect('ietf.secr.sreq.views.main')
261261

262+
263+
def status_slug_for_new_session(session, session_number):
264+
if session.group.features.acts_like_wg and session_number == 2:
265+
return 'apprw'
266+
return 'schedw'
267+
268+
262269
@role_required(*AUTHORIZED_ROLES)
263270
def confirm(request, acronym):
264271
'''
@@ -311,7 +318,6 @@ def confirm(request, acronym):
311318
# Create new session records
312319
# Should really use sess_form.save(), but needs data from the main form as well. Need to sort that out properly.
313320
for count, sess_form in enumerate(form.session_forms[:num_sessions]):
314-
slug = 'apprw' if count == 3 else 'schedw'
315321
new_session = Session.objects.create(
316322
meeting=meeting,
317323
group=group,
@@ -324,7 +330,7 @@ def confirm(request, acronym):
324330
)
325331
SchedulingEvent.objects.create(
326332
session=new_session,
327-
status=SessionStatusName.objects.get(slug=slug),
333+
status=SessionStatusName.objects.get(slug=status_slug_for_new_session(new_session, count)),
328334
by=login,
329335
)
330336
if 'resources' in form.data:
@@ -412,7 +418,6 @@ def edit(request, acronym, num=None):
412418
).filter(
413419
Q(current_status__isnull=True) | ~Q(current_status__in=['canceled', 'notmeet', 'deleted'])
414420
).order_by('id')
415-
sessions_count = sessions.count()
416421
initial = get_initial_session(sessions)
417422
FormClass = get_session_form_class()
418423

@@ -442,68 +447,16 @@ def edit(request, acronym, num=None):
442447
form = FormClass(group, meeting, request.POST, initial=initial)
443448
if form.is_valid():
444449
if form.has_changed():
445-
form.session_forms.save() # todo maintain event creation in commented-out old code!!
446-
# might be cleaner to simply delete and rewrite all records (but maintain submitter?)
447-
# adjust duration or add sessions
448-
# session 1
449-
# if 'length_session1' in form.changed_data:
450-
# session = sessions[0]
451-
# session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1']))
452-
# session.save()
453-
# session_changed(session)
454-
#
455-
# # session 2
456-
# if 'length_session2' in form.changed_data:
457-
# length_session2 = form.cleaned_data['length_session2']
458-
# if length_session2 == '':
459-
# sessions[1].delete()
460-
# elif sessions_count < 2:
461-
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session2']))
462-
# new_session = Session.objects.create(
463-
# meeting=meeting,
464-
# group=group,
465-
# attendees=form.cleaned_data['attendees'],
466-
# requested_duration=duration,
467-
# comments=form.cleaned_data['comments'],
468-
# type_id='regular',
469-
# )
470-
# SchedulingEvent.objects.create(
471-
# session=new_session,
472-
# status=SessionStatusName.objects.get(slug='schedw'),
473-
# by=request.user.person,
474-
# )
475-
# else:
476-
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session2']))
477-
# session = sessions[1]
478-
# session.requested_duration = duration
479-
# session.save()
480-
#
481-
# # session 3
482-
# if 'length_session3' in form.changed_data:
483-
# length_session3 = form.cleaned_data['length_session3']
484-
# if length_session3 == '':
485-
# sessions[2].delete()
486-
# elif sessions_count < 3:
487-
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session3']))
488-
# new_session = Session.objects.create(
489-
# meeting=meeting,
490-
# group=group,
491-
# attendees=form.cleaned_data['attendees'],
492-
# requested_duration=duration,
493-
# comments=form.cleaned_data['comments'],
494-
# type_id='regular',
495-
# )
496-
# SchedulingEvent.objects.create(
497-
# session=new_session,
498-
# status=SessionStatusName.objects.get(slug='apprw'),
499-
# by=request.user.person,
500-
# )
501-
# else:
502-
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session3']))
503-
# session = sessions[2]
504-
# session.requested_duration = duration
505-
# session.save()
506-
# session_changed(session)
450+
changed_session_forms = [sf for sf in form.session_forms.forms_to_keep if sf.has_changed()]
451+
form.session_forms.save()
452+
for n, new_session in enumerate(form.session_forms.created_instances):
453+
SchedulingEvent.objects.create(
454+
session=new_session,
455+
status_id=status_slug_for_new_session(new_session, n),
456+
by=request.user.person,
457+
)
458+
for sf in changed_session_forms:
459+
session_changed(sf.instance)
507460

508461
# New sessions may have been created, refresh the sessions list
509462
sessions = add_event_info_to_session_qs(
@@ -528,7 +481,8 @@ def edit(request, acronym, num=None):
528481
session_changed(sessions[current_joint_for_session_idx])
529482
sessions[new_joint_for_session_idx].joint_with_groups.set(new_joint_with_groups)
530483
session_changed(sessions[new_joint_for_session_idx])
531-
484+
485+
# Update sessions to match changes to shared form fields
532486
if 'attendees' in form.changed_data:
533487
sessions.update(attendees=form.cleaned_data['attendees'])
534488
if 'comments' in form.changed_data:
@@ -660,7 +614,7 @@ def main(request):
660614

661615
# add session status messages for use in template
662616
for group in scheduled_groups:
663-
if len(group.meeting_sessions) < 3:
617+
if not group.features.acts_like_wg or (len(group.meeting_sessions) < 3):
664618
group.status_message = group.meeting_sessions[0].current_status
665619
else:
666620
group.status_message = 'First two sessions: %s, Third session: %s' % (group.meeting_sessions[0].current_status, group.meeting_sessions[2].current_status)

ietf/secr/templates/includes/sessions_request_form.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@
77
<tr class="bg1"><td>Working Group Name:</td><td>{{ group.name }} ({{ group.acronym }})</td></tr>
88
<tr class="bg2"><td>Area Name:</td><td>{% if group.parent %}{{ group.parent.name }} ({{ group.parent.acronym }}){% endif %}</td></tr>
99
<tr class="bg1"><td>Number of Sessions:<span class="required">*</span></td><td>{{ form.num_session.errors }}{{ form.num_session }}</td></tr>
10-
<tr class="bg2" id="session_row_0"><td>Session 1:<span class="required">*</span></td><td>{% include 'meeting/session_details_form.html' with form=form.session_forms.0 only %}</td></tr>
10+
{% if group.features.acts_like_wg %}<tr class="bg2" id="session_row_0"><td>Session 1:<span class="required">*</span></td><td>{% include 'meeting/session_details_form.html' with form=form.session_forms.0 only %}</td></tr>
1111
<tr class="bg2" id="session_row_1"><td>Session 2:<span class="required">*</span></td><td>{% include 'meeting/session_details_form.html' with form=form.session_forms.1 only %}</td></tr>
1212
{% if not is_virtual %}
1313
<tr class="bg2"><td>Time between two sessions:</td><td>{{ form.session_time_relation.errors }}{{ form.session_time_relation }}</td></tr>
1414
{% endif %}
15-
{% if group.type.slug == "wg" %}
1615
<tr class="bg2" id="third_session_row"><td>Additional Session Request:</td><td>{{ form.third_session }} Check this box to request an additional session.<br>
1716
Additional slot may be available after agenda scheduling has closed and with the approval of an Area Director.<br>
1817
<div id="session_row_2">
1918
Third Session:
2019
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 only %}
2120
</div>
2221
</td></tr>
23-
{% else %}{# else group.type.slug != "wg" #}
24-
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 hidden=True only %}
22+
{% else %}{# else not group.features.acts_like_wg #}
23+
{% for session_form in form.session_forms %}
24+
<tr class="bg2" id="session_row_{{ forloop.counter0 }}"><td>Session {{ forloop.counter }}:<span class="required">*</span></td><td>{% include 'meeting/session_details_form.html' with form=session_form only %}</td></tr>
25+
{% endfor %}
2526
{% endif %}
2627
<tr class="bg1"><td>Number of Attendees:{% if not is_virtual %}<span class="required">*</span>{% endif %}</td><td>{{ form.attendees.errors }}{{ form.attendees }}</td></tr>
2728
<tr class="bg2"><td>People who must be present:</td><td>{{ form.bethere.errors }}{{ form.bethere }}</td></tr>

ietf/secr/templates/includes/sessions_request_view.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
{% endif %}
1919
</dl>
2020
</td></tr>
21-
{% if forloop.counter == 2 and not is_virtual %}
21+
{% if group.features.acts_like_wg and forloop.counter == 2 and not is_virtual %}
2222
<tr class="row2"><td>Time between sessions:</td><td>{% if session.session_time_relation_display %}{{ session.session_time_relation_display }}{% else %}No preference{% endif %}</td></tr>
2323
{% endif %}
2424
{% endif %}{% endfor %}

ietf/secr/templates/sreq/confirm.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ <h2>Sessions - Confirm</h2>
3030

3131
{% include "includes/sessions_request_view.html" %}
3232

33-
{% if form.session_forms.forms_to_keep|length > 2 %}
33+
{% if group.features.acts_like_wg and form.session_forms.forms_to_keep|length > 2 %}
3434
<br>
3535
<span class="alert"><p><b>Note: Your request for a third session must be approved by an area director before
3636
being submitted to agenda@ietf.org. Click "Submit" below to email an approval

0 commit comments

Comments
 (0)