Skip to content

Commit

Permalink
Allow non-WG-like groups to request additional sessions/durations and…
Browse files Browse the repository at this point in the history
… bypass approval

 - Legacy-Id: 19424
  • Loading branch information
jennifer-richards committed Oct 14, 2021
1 parent 1054f90 commit 5318081
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 100 deletions.
73 changes: 49 additions & 24 deletions ietf/meeting/forms.py
Expand Up @@ -531,27 +531,49 @@ def _day_choices(days):


class DurationChoiceField(forms.ChoiceField):
duration_choices = (('3600', '60 minutes'), ('7200', '120 minutes'))

# todo expand range of choices and make configurable
def __init__(self, *args, **kwargs):
def __init__(self, durations=None, *args, **kwargs):
if durations is None:
durations = (3600, 7200)
super().__init__(
choices=(('','--Please select'), *self.duration_choices),
choices=self._make_choices(durations),
*args, **kwargs,
)

def prepare_value(self, value):
"""Converts incoming value into string used for the option value"""
if value:
return str(int(value.total_seconds())) if hasattr(value, 'total_seconds') else str(value)
return str(int(value.total_seconds())) if isinstance(value, datetime.timedelta) else str(value)
return ''

def clean(self, value):
"""Convert option value string back to a timedelta"""
val = super().clean(value)
if val == '':
return None
return datetime.timedelta(seconds=int(val))
def to_python(self, value):
return datetime.timedelta(seconds=round(float(value))) if value not in self.empty_values else None

def valid_value(self, value):
return super().valid_value(self.prepare_value(value))

def _format_duration_choice(self, dur):
seconds = int(dur.total_seconds()) if isinstance(dur, datetime.timedelta) else int(dur)
hours = int(seconds / 3600)
minutes = round((seconds - 3600 * hours) / 60)
hr_str = '{} hour{}'.format(hours, '' if hours == 1 else 's')
min_str = '{} minute{}'.format(minutes, '' if minutes == 1 else 's')
if hours > 0 and minutes > 0:
time_str = ' '.join((hr_str, min_str))
elif hours > 0:
time_str = hr_str
else:
time_str = min_str
return (str(seconds), time_str)

def _make_choices(self, durations):
return (
('','--Please select'),
*[self._format_duration_choice(dur) for dur in durations])

def _set_durations(self, durations):
self.choices = self._make_choices(durations)

durations = property(None, _set_durations)


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

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

def save_new(self, form, commit=True):
form.instance.meeting = self._meeting
super().save_new(form, commit)
return super().save_new(form, commit)

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

SessionDetailsFormSet = forms.inlineformset_factory(
Group,
Session,
formset=SessionDetailsInlineFormset,
form=SessionDetailsForm,
can_delete=True,
can_order=False,
min_num=1,
max_num=3,
extra=3,
)
def sessiondetailsformset_factory(min_num=1, max_num=3):
return forms.inlineformset_factory(
Group,
Session,
formset=SessionDetailsInlineFormset,
form=SessionDetailsForm,
can_delete=True,
can_order=False,
min_num=min_num,
max_num=max_num,
extra=max_num, # only creates up to max_num total
)
7 changes: 5 additions & 2 deletions ietf/secr/sreq/forms.py
Expand Up @@ -8,7 +8,7 @@

from ietf.name.models import TimerangeName, ConstraintName
from ietf.group.models import Group
from ietf.meeting.forms import SessionDetailsFormSet
from ietf.meeting.forms import sessiondetailsformset_factory
from ietf.meeting.models import ResourceAssociation, Constraint
from ietf.person.fields import SearchablePersonsField
from ietf.utils.html import clean_text_field
Expand Down Expand Up @@ -90,9 +90,12 @@ def __init__(self, group, meeting, data=None, *args, **kwargs):
self.hidden = False

self.group = group
self.session_forms = SessionDetailsFormSet(group=self.group, meeting=meeting, data=data)
formset_class = sessiondetailsformset_factory(max_num=3 if group.features.acts_like_wg else 12)
self.session_forms = formset_class(group=self.group, meeting=meeting, data=data)
super(SessionForm, self).__init__(data=data, *args, **kwargs)

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

other_groups = list(allowed_conflicting_groups().exclude(pk=group.pk).values_list('acronym', 'acronym').order_by('acronym'))
Expand Down
90 changes: 22 additions & 68 deletions ietf/secr/sreq/views.py
Expand Up @@ -61,7 +61,7 @@ def get_initial_session(sessions, prune_conflicts=False):
conflicts = constraints.filter(name__is_group_conflict=True) # only the group conflict constraints

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

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


def status_slug_for_new_session(session, session_number):
if session.group.features.acts_like_wg and session_number == 2:
return 'apprw'
return 'schedw'


@role_required(*AUTHORIZED_ROLES)
def confirm(request, acronym):
'''
Expand Down Expand Up @@ -311,7 +318,6 @@ def confirm(request, acronym):
# Create new session records
# Should really use sess_form.save(), but needs data from the main form as well. Need to sort that out properly.
for count, sess_form in enumerate(form.session_forms[:num_sessions]):
slug = 'apprw' if count == 3 else 'schedw'
new_session = Session.objects.create(
meeting=meeting,
group=group,
Expand All @@ -324,7 +330,7 @@ def confirm(request, acronym):
)
SchedulingEvent.objects.create(
session=new_session,
status=SessionStatusName.objects.get(slug=slug),
status=SessionStatusName.objects.get(slug=status_slug_for_new_session(new_session, count)),
by=login,
)
if 'resources' in form.data:
Expand Down Expand Up @@ -412,7 +418,6 @@ def edit(request, acronym, num=None):
).filter(
Q(current_status__isnull=True) | ~Q(current_status__in=['canceled', 'notmeet', 'deleted'])
).order_by('id')
sessions_count = sessions.count()
initial = get_initial_session(sessions)
FormClass = get_session_form_class()

Expand Down Expand Up @@ -442,68 +447,16 @@ def edit(request, acronym, num=None):
form = FormClass(group, meeting, request.POST, initial=initial)
if form.is_valid():
if form.has_changed():
form.session_forms.save() # todo maintain event creation in commented-out old code!!
# might be cleaner to simply delete and rewrite all records (but maintain submitter?)
# adjust duration or add sessions
# session 1
# if 'length_session1' in form.changed_data:
# session = sessions[0]
# session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1']))
# session.save()
# session_changed(session)
#
# # session 2
# if 'length_session2' in form.changed_data:
# length_session2 = form.cleaned_data['length_session2']
# if length_session2 == '':
# sessions[1].delete()
# elif sessions_count < 2:
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session2']))
# new_session = Session.objects.create(
# meeting=meeting,
# group=group,
# attendees=form.cleaned_data['attendees'],
# requested_duration=duration,
# comments=form.cleaned_data['comments'],
# type_id='regular',
# )
# SchedulingEvent.objects.create(
# session=new_session,
# status=SessionStatusName.objects.get(slug='schedw'),
# by=request.user.person,
# )
# else:
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session2']))
# session = sessions[1]
# session.requested_duration = duration
# session.save()
#
# # session 3
# if 'length_session3' in form.changed_data:
# length_session3 = form.cleaned_data['length_session3']
# if length_session3 == '':
# sessions[2].delete()
# elif sessions_count < 3:
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session3']))
# new_session = Session.objects.create(
# meeting=meeting,
# group=group,
# attendees=form.cleaned_data['attendees'],
# requested_duration=duration,
# comments=form.cleaned_data['comments'],
# type_id='regular',
# )
# SchedulingEvent.objects.create(
# session=new_session,
# status=SessionStatusName.objects.get(slug='apprw'),
# by=request.user.person,
# )
# else:
# duration = datetime.timedelta(0,int(form.cleaned_data['length_session3']))
# session = sessions[2]
# session.requested_duration = duration
# session.save()
# session_changed(session)
changed_session_forms = [sf for sf in form.session_forms.forms_to_keep if sf.has_changed()]
form.session_forms.save()
for n, new_session in enumerate(form.session_forms.created_instances):
SchedulingEvent.objects.create(
session=new_session,
status_id=status_slug_for_new_session(new_session, n),
by=request.user.person,
)
for sf in changed_session_forms:
session_changed(sf.instance)

# New sessions may have been created, refresh the sessions list
sessions = add_event_info_to_session_qs(
Expand All @@ -528,7 +481,8 @@ def edit(request, acronym, num=None):
session_changed(sessions[current_joint_for_session_idx])
sessions[new_joint_for_session_idx].joint_with_groups.set(new_joint_with_groups)
session_changed(sessions[new_joint_for_session_idx])


# Update sessions to match changes to shared form fields
if 'attendees' in form.changed_data:
sessions.update(attendees=form.cleaned_data['attendees'])
if 'comments' in form.changed_data:
Expand Down Expand Up @@ -660,7 +614,7 @@ def main(request):

# add session status messages for use in template
for group in scheduled_groups:
if len(group.meeting_sessions) < 3:
if not group.features.acts_like_wg or (len(group.meeting_sessions) < 3):
group.status_message = group.meeting_sessions[0].current_status
else:
group.status_message = 'First two sessions: %s, Third session: %s' % (group.meeting_sessions[0].current_status, group.meeting_sessions[2].current_status)
Expand Down
9 changes: 5 additions & 4 deletions ietf/secr/templates/includes/sessions_request_form.html
Expand Up @@ -7,21 +7,22 @@
<tr class="bg1"><td>Working Group Name:</td><td>{{ group.name }} ({{ group.acronym }})</td></tr>
<tr class="bg2"><td>Area Name:</td><td>{% if group.parent %}{{ group.parent.name }} ({{ group.parent.acronym }}){% endif %}</td></tr>
<tr class="bg1"><td>Number of Sessions:<span class="required">*</span></td><td>{{ form.num_session.errors }}{{ form.num_session }}</td></tr>
<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>
{% 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>
<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>
{% if not is_virtual %}
<tr class="bg2"><td>Time between two sessions:</td><td>{{ form.session_time_relation.errors }}{{ form.session_time_relation }}</td></tr>
{% endif %}
{% if group.type.slug == "wg" %}
<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>
Additional slot may be available after agenda scheduling has closed and with the approval of an Area Director.<br>
<div id="session_row_2">
Third Session:
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 only %}
</div>
</td></tr>
{% else %}{# else group.type.slug != "wg" #}
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 hidden=True only %}
{% else %}{# else not group.features.acts_like_wg #}
{% for session_form in form.session_forms %}
<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>
{% endfor %}
{% endif %}
<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>
<tr class="bg2"><td>People who must be present:</td><td>{{ form.bethere.errors }}{{ form.bethere }}</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion ietf/secr/templates/includes/sessions_request_view.html
Expand Up @@ -18,7 +18,7 @@
{% endif %}
</dl>
</td></tr>
{% if forloop.counter == 2 and not is_virtual %}
{% if group.features.acts_like_wg and forloop.counter == 2 and not is_virtual %}
<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>
{% endif %}
{% endif %}{% endfor %}
Expand Down
2 changes: 1 addition & 1 deletion ietf/secr/templates/sreq/confirm.html
Expand Up @@ -30,7 +30,7 @@ <h2>Sessions - Confirm</h2>

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

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

0 comments on commit 5318081

Please sign in to comment.