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):
|
||||
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):
|
||||
|
@ -573,6 +595,8 @@ class SessionDetailsForm(forms.ModelForm):
|
|||
}),
|
||||
})
|
||||
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
|
||||
|
@ -606,7 +630,7 @@ class SessionDetailsInlineFormset(forms.BaseInlineFormSet):
|
|||
|
||||
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)
|
||||
|
@ -619,14 +643,15 @@ class SessionDetailsInlineFormset(forms.BaseInlineFormSet):
|
|||
"""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
|
||||
)
|
|
@ -8,7 +8,7 @@ import debug # pyflakes:ignore
|
|||
|
||||
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
|
||||
|
@ -90,9 +90,12 @@ class SessionForm(forms.Form):
|
|||
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'))
|
||||
|
|
|
@ -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):
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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(
|
||||
|
@ -529,6 +482,7 @@ def edit(request, acronym, num=None):
|
|||
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:
|
||||
|
@ -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)
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
<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">
|
||||
|
@ -20,8 +19,10 @@
|
|||
{% 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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
{% 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
|
||||
|
|
Loading…
Reference in a new issue