Allow non-WG-like groups to request additional sessions/durations and bypass approval
- Legacy-Id: 19424
This commit is contained in:
parent
1054f90873
commit
5318081608
|
@ -531,27 +531,49 @@ class TimeSlotCreateForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class DurationChoiceField(forms.ChoiceField):
|
class DurationChoiceField(forms.ChoiceField):
|
||||||
duration_choices = (('3600', '60 minutes'), ('7200', '120 minutes'))
|
def __init__(self, durations=None, *args, **kwargs):
|
||||||
|
if durations is None:
|
||||||
# todo expand range of choices and make configurable
|
durations = (3600, 7200)
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
choices=(('','--Please select'), *self.duration_choices),
|
choices=self._make_choices(durations),
|
||||||
*args, **kwargs,
|
*args, **kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def prepare_value(self, value):
|
def prepare_value(self, value):
|
||||||
"""Converts incoming value into string used for the option value"""
|
"""Converts incoming value into string used for the option value"""
|
||||||
if 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 ''
|
return ''
|
||||||
|
|
||||||
def clean(self, value):
|
def to_python(self, value):
|
||||||
"""Convert option value string back to a timedelta"""
|
return datetime.timedelta(seconds=round(float(value))) if value not in self.empty_values else None
|
||||||
val = super().clean(value)
|
|
||||||
if val == '':
|
def valid_value(self, value):
|
||||||
return None
|
return super().valid_value(self.prepare_value(value))
|
||||||
return datetime.timedelta(seconds=int(val))
|
|
||||||
|
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):
|
class SessionDetailsForm(forms.ModelForm):
|
||||||
|
@ -573,6 +595,8 @@ class SessionDetailsForm(forms.ModelForm):
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
self.fields['purpose'].queryset = SessionPurposeName.objects.filter(pk__in=session_purposes)
|
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:
|
class Meta:
|
||||||
model = Session
|
model = Session
|
||||||
|
@ -606,7 +630,7 @@ class SessionDetailsInlineFormset(forms.BaseInlineFormSet):
|
||||||
|
|
||||||
def save_new(self, form, commit=True):
|
def save_new(self, form, commit=True):
|
||||||
form.instance.meeting = self._meeting
|
form.instance.meeting = self._meeting
|
||||||
super().save_new(form, commit)
|
return super().save_new(form, commit)
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
existing_instances = set(form.instance for form in self.forms if form.instance.pk)
|
existing_instances = set(form.instance for form in self.forms if form.instance.pk)
|
||||||
|
@ -619,14 +643,15 @@ class SessionDetailsInlineFormset(forms.BaseInlineFormSet):
|
||||||
"""Get the not-deleted forms"""
|
"""Get the not-deleted forms"""
|
||||||
return [f for f in self.forms if f not in self.deleted_forms]
|
return [f for f in self.forms if f not in self.deleted_forms]
|
||||||
|
|
||||||
SessionDetailsFormSet = forms.inlineformset_factory(
|
def sessiondetailsformset_factory(min_num=1, max_num=3):
|
||||||
Group,
|
return forms.inlineformset_factory(
|
||||||
Session,
|
Group,
|
||||||
formset=SessionDetailsInlineFormset,
|
Session,
|
||||||
form=SessionDetailsForm,
|
formset=SessionDetailsInlineFormset,
|
||||||
can_delete=True,
|
form=SessionDetailsForm,
|
||||||
can_order=False,
|
can_delete=True,
|
||||||
min_num=1,
|
can_order=False,
|
||||||
max_num=3,
|
min_num=min_num,
|
||||||
extra=3,
|
max_num=max_num,
|
||||||
)
|
extra=max_num, # only creates up to max_num total
|
||||||
|
)
|
|
@ -8,7 +8,7 @@ import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.name.models import TimerangeName, ConstraintName
|
from ietf.name.models import TimerangeName, ConstraintName
|
||||||
from ietf.group.models import Group
|
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.meeting.models import ResourceAssociation, Constraint
|
||||||
from ietf.person.fields import SearchablePersonsField
|
from ietf.person.fields import SearchablePersonsField
|
||||||
from ietf.utils.html import clean_text_field
|
from ietf.utils.html import clean_text_field
|
||||||
|
@ -90,9 +90,12 @@ class SessionForm(forms.Form):
|
||||||
self.hidden = False
|
self.hidden = False
|
||||||
|
|
||||||
self.group = group
|
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)
|
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'})
|
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'))
|
other_groups = list(allowed_conflicting_groups().exclude(pk=group.pk).values_list('acronym', 'acronym').order_by('acronym'))
|
||||||
|
|
|
@ -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
|
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
|
# 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
|
initial['attendees'] = sessions[0].attendees
|
||||||
|
|
||||||
def valid_conflict(conflict):
|
def valid_conflict(conflict):
|
||||||
|
@ -259,6 +259,13 @@ def cancel(request, acronym):
|
||||||
messages.success(request, 'The %s Session Request has been cancelled' % group.acronym)
|
messages.success(request, 'The %s Session Request has been cancelled' % group.acronym)
|
||||||
return redirect('ietf.secr.sreq.views.main')
|
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)
|
@role_required(*AUTHORIZED_ROLES)
|
||||||
def confirm(request, acronym):
|
def confirm(request, acronym):
|
||||||
'''
|
'''
|
||||||
|
@ -311,7 +318,6 @@ def confirm(request, acronym):
|
||||||
# Create new session records
|
# 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.
|
# 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]):
|
for count, sess_form in enumerate(form.session_forms[:num_sessions]):
|
||||||
slug = 'apprw' if count == 3 else 'schedw'
|
|
||||||
new_session = Session.objects.create(
|
new_session = Session.objects.create(
|
||||||
meeting=meeting,
|
meeting=meeting,
|
||||||
group=group,
|
group=group,
|
||||||
|
@ -324,7 +330,7 @@ def confirm(request, acronym):
|
||||||
)
|
)
|
||||||
SchedulingEvent.objects.create(
|
SchedulingEvent.objects.create(
|
||||||
session=new_session,
|
session=new_session,
|
||||||
status=SessionStatusName.objects.get(slug=slug),
|
status=SessionStatusName.objects.get(slug=status_slug_for_new_session(new_session, count)),
|
||||||
by=login,
|
by=login,
|
||||||
)
|
)
|
||||||
if 'resources' in form.data:
|
if 'resources' in form.data:
|
||||||
|
@ -412,7 +418,6 @@ def edit(request, acronym, num=None):
|
||||||
).filter(
|
).filter(
|
||||||
Q(current_status__isnull=True) | ~Q(current_status__in=['canceled', 'notmeet', 'deleted'])
|
Q(current_status__isnull=True) | ~Q(current_status__in=['canceled', 'notmeet', 'deleted'])
|
||||||
).order_by('id')
|
).order_by('id')
|
||||||
sessions_count = sessions.count()
|
|
||||||
initial = get_initial_session(sessions)
|
initial = get_initial_session(sessions)
|
||||||
FormClass = get_session_form_class()
|
FormClass = get_session_form_class()
|
||||||
|
|
||||||
|
@ -442,68 +447,16 @@ def edit(request, acronym, num=None):
|
||||||
form = FormClass(group, meeting, request.POST, initial=initial)
|
form = FormClass(group, meeting, request.POST, initial=initial)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
if form.has_changed():
|
if form.has_changed():
|
||||||
form.session_forms.save() # todo maintain event creation in commented-out old code!!
|
changed_session_forms = [sf for sf in form.session_forms.forms_to_keep if sf.has_changed()]
|
||||||
# might be cleaner to simply delete and rewrite all records (but maintain submitter?)
|
form.session_forms.save()
|
||||||
# adjust duration or add sessions
|
for n, new_session in enumerate(form.session_forms.created_instances):
|
||||||
# session 1
|
SchedulingEvent.objects.create(
|
||||||
# if 'length_session1' in form.changed_data:
|
session=new_session,
|
||||||
# session = sessions[0]
|
status_id=status_slug_for_new_session(new_session, n),
|
||||||
# session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1']))
|
by=request.user.person,
|
||||||
# session.save()
|
)
|
||||||
# session_changed(session)
|
for sf in changed_session_forms:
|
||||||
#
|
session_changed(sf.instance)
|
||||||
# # 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)
|
|
||||||
|
|
||||||
# New sessions may have been created, refresh the sessions list
|
# New sessions may have been created, refresh the sessions list
|
||||||
sessions = add_event_info_to_session_qs(
|
sessions = add_event_info_to_session_qs(
|
||||||
|
@ -528,7 +481,8 @@ def edit(request, acronym, num=None):
|
||||||
session_changed(sessions[current_joint_for_session_idx])
|
session_changed(sessions[current_joint_for_session_idx])
|
||||||
sessions[new_joint_for_session_idx].joint_with_groups.set(new_joint_with_groups)
|
sessions[new_joint_for_session_idx].joint_with_groups.set(new_joint_with_groups)
|
||||||
session_changed(sessions[new_joint_for_session_idx])
|
session_changed(sessions[new_joint_for_session_idx])
|
||||||
|
|
||||||
|
# Update sessions to match changes to shared form fields
|
||||||
if 'attendees' in form.changed_data:
|
if 'attendees' in form.changed_data:
|
||||||
sessions.update(attendees=form.cleaned_data['attendees'])
|
sessions.update(attendees=form.cleaned_data['attendees'])
|
||||||
if 'comments' in form.changed_data:
|
if 'comments' in form.changed_data:
|
||||||
|
@ -660,7 +614,7 @@ def main(request):
|
||||||
|
|
||||||
# add session status messages for use in template
|
# add session status messages for use in template
|
||||||
for group in scheduled_groups:
|
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
|
group.status_message = group.meeting_sessions[0].current_status
|
||||||
else:
|
else:
|
||||||
group.status_message = 'First two sessions: %s, Third session: %s' % (group.meeting_sessions[0].current_status, group.meeting_sessions[2].current_status)
|
group.status_message = 'First two sessions: %s, Third session: %s' % (group.meeting_sessions[0].current_status, group.meeting_sessions[2].current_status)
|
||||||
|
|
|
@ -7,12 +7,11 @@
|
||||||
<tr class="bg1"><td>Working Group Name:</td><td>{{ group.name }} ({{ group.acronym }})</td></tr>
|
<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="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="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>
|
<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 %}
|
{% 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>
|
<tr class="bg2"><td>Time between two sessions:</td><td>{{ form.session_time_relation.errors }}{{ form.session_time_relation }}</td></tr>
|
||||||
{% endif %}
|
{% 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>
|
<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>
|
Additional slot may be available after agenda scheduling has closed and with the approval of an Area Director.<br>
|
||||||
<div id="session_row_2">
|
<div id="session_row_2">
|
||||||
|
@ -20,8 +19,10 @@
|
||||||
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 only %}
|
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 only %}
|
||||||
</div>
|
</div>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
{% else %}{# else group.type.slug != "wg" #}
|
{% else %}{# else not group.features.acts_like_wg #}
|
||||||
{% include 'meeting/session_details_form.html' with form=form.session_forms.2 hidden=True only %}
|
{% 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 %}
|
{% 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="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>
|
<tr class="bg2"><td>People who must be present:</td><td>{{ form.bethere.errors }}{{ form.bethere }}</td></tr>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dl>
|
</dl>
|
||||||
</td></tr>
|
</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>
|
<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 %}
|
||||||
{% endif %}{% endfor %}
|
{% endif %}{% endfor %}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
{% include "includes/sessions_request_view.html" %}
|
{% 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>
|
<br>
|
||||||
<span class="alert"><p><b>Note: Your request for a third session must be approved by an area director before
|
<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
|
being submitted to agenda@ietf.org. Click "Submit" below to email an approval
|
||||||
|
|
Loading…
Reference in a new issue