From 825a054d1949406a9c7671a0a91e4a3de4cac47e Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Fri, 21 Feb 2020 13:27:11 +0000 Subject: [PATCH] Add support for structured entry and storage of joint sessions in meetings. - Also adds additional tests for the SessionForm - Fixes a javascript error in session requests for non-WG groups, that could cause incorrect form behaviour. - Expands the tests added in [17289] a bit. - Legacy-Id: 17321 Note: SVN reference [17289] has been migrated to Git commit a227813dc5b712a0cd7e45f440fc86eda4f690ac --- ietf/meeting/models.py | 4 + ietf/secr/sreq/forms.py | 28 ++- ietf/secr/sreq/tests.py | 186 +++++++++++++++++- ietf/secr/sreq/views.py | 36 +++- ietf/secr/static/secr/js/sessions.js | 19 +- ietf/secr/templates/includes/session_info.txt | 1 + .../includes/sessions_request_form.html | 16 ++ .../includes/sessions_request_view.html | 14 +- ietf/templates/meeting/requests.html | 3 + 9 files changed, 293 insertions(+), 14 deletions(-) diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index b687b4cf7..93839fd34 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -900,6 +900,7 @@ class Session(models.Model): short = models.CharField(blank=True, max_length=32, help_text="Short version of 'name' above, for use in filenames.") type = ForeignKey(TimeSlotTypeName) group = ForeignKey(Group) # The group type historically determined the session type. BOFs also need to be added as a group. Note that not all meeting requests have a natural group to associate with. + joint_with_groups = models.ManyToManyField(Group, related_name='sessions_joint_in') attendees = models.IntegerField(null=True, blank=True) agenda_note = models.CharField(blank=True, max_length=255) requested_duration = models.DurationField(default=datetime.timedelta(0)) @@ -1023,6 +1024,9 @@ class Session(models.Model): def is_material_submission_cutoff(self): return datetime.date.today() > self.meeting.get_submission_correction_date() + + def joint_with_groups_acronyms(self): + return [group.acronym for group in self.joint_with_groups.all()] def __str__(self): if self.meeting.type_id == "interim": diff --git a/ietf/secr/sreq/forms.py b/ietf/secr/sreq/forms.py index 960a4a7fe..fb721c445 100644 --- a/ietf/secr/sreq/forms.py +++ b/ietf/secr/sreq/forms.py @@ -22,6 +22,7 @@ NUM_SESSION_CHOICES = (('','--Please select'),('1','1'),('2','2')) # LENGTH_SESSION_CHOICES = (('','--Please select'),('1800','30 minutes'),('3600','1 hour'),('5400','1.5 hours'), ('7200','2 hours'),('9000','2.5 hours')) LENGTH_SESSION_CHOICES = (('','--Please select'),('1800','30 minutes'),('3600','1 hour'),('5400','1.5 hours'), ('7200','2 hours')) SESSION_TIME_RELATION_CHOICES = (('', 'No preference'),) + Constraint.TIME_RELATION_CHOICES +JOINT_FOR_SESSION_CHOICES = (('1', 'First session'), ('2', 'Second session'), ('3', 'Third session'), ) # ------------------------------------------------- # Helper Functions @@ -86,10 +87,13 @@ class SessionForm(forms.Form): conflict1 = forms.CharField(max_length=255,required=False) conflict2 = forms.CharField(max_length=255,required=False) conflict3 = forms.CharField(max_length=255,required=False) + joint_with_groups = forms.CharField(max_length=255,required=False) + joint_for_session = forms.ChoiceField(choices=JOINT_FOR_SESSION_CHOICES, required=False) comments = forms.CharField(max_length=200,required=False) wg_selector1 = forms.ChoiceField(choices=[],required=False) wg_selector2 = forms.ChoiceField(choices=[],required=False) wg_selector3 = forms.ChoiceField(choices=[],required=False) + wg_selector4 = forms.ChoiceField(choices=[],required=False) third_session = forms.BooleanField(required=False) resources = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,required=False) bethere = SearchablePersonsField(label="Must be present", required=False) @@ -115,7 +119,7 @@ class SessionForm(forms.Form): other_groups = list(allowed_conflicting_groups().exclude(pk=group.pk).values_list('acronym', 'acronym').order_by('acronym')) self.fields['adjacent_with_wg'].choices = [('', '--No preference')] + other_groups group_acronym_choices = [('','--Select WG(s)')] + other_groups - for i in range(1, 4): + for i in range(1, 5): self.fields['wg_selector{}'.format(i)].choices = group_acronym_choices # disabling handleconflictfield (which only enables or disables form elements) while we're hacking the meaning of the three constraints currently in use: @@ -125,6 +129,7 @@ class SessionForm(forms.Form): self.fields['wg_selector1'].widget.attrs['onChange'] = "document.form_post.conflict1.value=document.form_post.conflict1.value + ' ' + this.options[this.selectedIndex].value; return 1;" self.fields['wg_selector2'].widget.attrs['onChange'] = "document.form_post.conflict2.value=document.form_post.conflict2.value + ' ' + this.options[this.selectedIndex].value; return 1;" self.fields['wg_selector3'].widget.attrs['onChange'] = "document.form_post.conflict3.value=document.form_post.conflict3.value + ' ' + this.options[this.selectedIndex].value; return 1;" + self.fields['wg_selector4'].widget.attrs['onChange'] = "document.form_post.joint_with_groups.value=document.form_post.joint_with_groups.value + ' ' + this.options[this.selectedIndex].value; return 1;" # disabling check_prior_conflict javascript while we're hacking the meaning of the three constraints currently in use #self.fields['wg_selector2'].widget.attrs['onClick'] = "return check_prior_conflict(2);" @@ -159,7 +164,12 @@ class SessionForm(forms.Form): conflict = self.cleaned_data['conflict3'] check_conflict(conflict, self.group) return conflict - + + def clean_joint_with_groups(self): + groups = self.cleaned_data['joint_with_groups'] + check_conflict(groups, self.group) + return groups + def clean(self): super(SessionForm, self).clean() data = self.cleaned_data @@ -180,12 +190,20 @@ class SessionForm(forms.Form): if data.get('num_session','') == '2': if not data['length_session2']: raise forms.ValidationError('You must enter a length for all sessions') - elif data.get('session_time_relation'): - raise forms.ValidationError('Time between sessions can only be used when two sessions are requested.') - + else: + if data.get('session_time_relation'): + raise forms.ValidationError('Time between sessions can only be used when two ' + 'sessions are requested.') + if data['joint_for_session'] == '2': + raise forms.ValidationError('The second session can not be the joint session, ' + 'because you have not requested a second session.') + if data.get('third_session',False): if not data['length_session2'] or not data.get('length_session3',None): raise forms.ValidationError('You must enter a length for all sessions') + elif data['joint_for_session'] == '3': + raise forms.ValidationError('The third session can not be the joint session, ' + 'because you have not requested a third session.') return data diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index 9895a6319..04e912fe7 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -17,6 +17,7 @@ from ietf.meeting.models import Session, ResourceAssociation, SchedulingEvent, C from ietf.meeting.factories import MeetingFactory, SessionFactory from ietf.name.models import TimerangeName from ietf.person.models import Person +from ietf.secr.sreq.forms import SessionForm from ietf.utils.mail import outbox, empty_outbox from pyquery import PyQuery @@ -82,6 +83,8 @@ class SessionRequestTestCase(TestCase): meeting = MeetingFactory(type_id='ietf', date=datetime.date.today()) mars = RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars').group group2 = GroupFactory() + group3 = GroupFactory() + group4 = GroupFactory() SessionFactory(meeting=meeting,group=mars,status_id='sched') url = reverse('ietf.secr.sreq.views.edit', kwargs={'acronym':'mars'}) @@ -96,6 +99,8 @@ class SessionRequestTestCase(TestCase): 'comments':'need lights', 'session_time_relation': 'subsequent-days', 'adjacent_with_wg': group2.acronym, + 'joint_with_groups': group3.acronym + ' ' + group4.acronym, + 'joint_for_session': '2', 'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'], 'submit': 'Continue'} r = self.client.post(url, post_data, HTTP_HOST='example.com') @@ -112,12 +117,43 @@ class SessionRequestTestCase(TestCase): list(session.constraints().get(name='timerange').timeranges.all().values('name')), list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name')) ) - + self.assertFalse(sessions[0].joint_with_groups.count()) + self.assertEqual(list(sessions[1].joint_with_groups.all()), [group3, group4]) + # Check whether the updated data is visible on the view page r = self.client.get(redirect_url) self.assertContains(r, 'Schedule the sessions on subsequent days') self.assertContains(r, 'Thursday early afternoon, Thursday late afternoon') self.assertContains(r, group2.acronym) + self.assertContains(r, 'Second session with: {} {}'.format(group3.acronym, group4.acronym)) + + # Edit again, changing the joint sessions and clearing some fields. The behaviour of + # edit is different depending on whether previous joint sessions were recorded. + post_data = {'num_session':'2', + 'length_session1':'3600', + 'length_session2':'3600', + 'attendees':'10', + 'conflict1':'', + 'comments':'need lights', + 'joint_with_groups': group2.acronym, + 'joint_for_session': '1', + 'submit': 'Continue'} + r = self.client.post(url, post_data, HTTP_HOST='example.com') + self.assertRedirects(r, redirect_url) + + # Check whether updates were stored in the database + sessions = Session.objects.filter(meeting=meeting, group=mars) + self.assertEqual(len(sessions), 2) + session = sessions[0] + self.assertFalse(session.constraints().filter(name='time_relation')) + self.assertFalse(session.constraints().filter(name='wg_adjacent')) + self.assertFalse(session.constraints().filter(name='timerange')) + self.assertEqual(list(sessions[0].joint_with_groups.all()), [group2]) + self.assertFalse(sessions[1].joint_with_groups.count()) + + # Check whether the updated data is visible on the view page + r = self.client.get(redirect_url) + self.assertContains(r, 'First session with: {}'.format(group2.acronym)) def test_tool_status(self): MeetingFactory(type_id='ietf', date=datetime.date.today()) @@ -135,6 +171,8 @@ class SubmitRequestCase(TestCase): area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group group = GroupFactory(parent=area) group2 = GroupFactory(parent=area) + group3 = GroupFactory(parent=area) + group4 = GroupFactory(parent=area) session_count_before = Session.objects.filter(meeting=meeting, group=group).count() url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym}) confirm_url = reverse('ietf.secr.sreq.views.confirm',kwargs={'acronym':group.acronym}) @@ -146,6 +184,8 @@ class SubmitRequestCase(TestCase): 'comments':'need projector', 'adjacent_with_wg': group2.acronym, 'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'], + 'joint_with_groups': group3.acronym + ' ' + group4.acronym, + 'joint_for_session': '1', 'submit': 'Continue'} self.client.login(username="secretary", password="secretary+password") r = self.client.post(url,post_data) @@ -154,6 +194,7 @@ class SubmitRequestCase(TestCase): # Verify the contents of the confirm view self.assertContains(r, 'Thursday early afternoon, Thursday late afternoon') self.assertContains(r, group2.acronym) + self.assertContains(r, 'First session with: {} {}'.format(group3.acronym, group4.acronym)) post_data['submit'] = 'Submit' r = self.client.post(confirm_url,post_data) @@ -166,6 +207,15 @@ class SubmitRequestCase(TestCase): self.assertRedirects(r, main_url) session_count_after = Session.objects.filter(meeting=meeting, group=group, type='regular').count() self.assertEqual(session_count_after, session_count_before + 1) + + # Verify database content + session = Session.objects.get(meeting=meeting, group=group) + self.assertEqual(session.constraints().get(name='wg_adjacent').target.acronym, group2.acronym) + self.assertEqual( + list(session.constraints().get(name='timerange').timeranges.all().values('name')), + list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name')) + ) + self.assertEqual(list(session.joint_with_groups.all()), [group3, group4]) def test_submit_request_invalid(self): MeetingFactory(type_id='ietf', date=datetime.date.today()) @@ -237,6 +287,7 @@ class SubmitRequestCase(TestCase): RoleFactory(name_id='ad', person=ad, group=area) group = GroupFactory(acronym='ames', parent=area) group2 = GroupFactory(acronym='ames2', parent=area) + group3 = GroupFactory(acronym='ames2', parent=area) RoleFactory(name_id='chair', group=group, person__user__username='ameschairman') resource = ResourceAssociation.objects.create(name_id='project') # Bit of a test data hack - the fixture now has no used resources to pick from @@ -256,6 +307,8 @@ class SubmitRequestCase(TestCase): 'resources': resource.pk, 'session_time_relation': 'subsequent-days', 'adjacent_with_wg': group2.acronym, + 'joint_with_groups': group3.acronym, + 'joint_for_session': '2', 'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'], 'submit': 'Continue'} self.client.login(username="ameschairman", password="ameschairman+password") @@ -284,10 +337,11 @@ class SubmitRequestCase(TestCase): list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name')) ) resource = session.resources.first() + self.assertTrue(resource.desc in notification_payload) self.assertTrue('Schedule the sessions on subsequent days' in notification_payload) self.assertTrue(group2.acronym in notification_payload) self.assertTrue("Can't meet: Thursday early afternoon, Thursday late" in notification_payload) - self.assertTrue(resource.desc in notification_payload) + self.assertTrue('Second session joint with: {}'.format(group3.acronym) in notification_payload) self.assertTrue(ad.ascii_name() in notification_payload) class LockAppTestCase(TestCase): @@ -359,9 +413,133 @@ class NotMeetingCase(TestCase): class RetrievePreviousCase(TestCase): pass - - # test error if already scheduled # test get previous exists/doesn't exist # test that groups scheduled and unscheduled add up to total groups # test access by unauthorized + + +class SessionFormTest(TestCase): + def setUp(self): + self.group1 = GroupFactory() + self.group2 = GroupFactory() + self.group3 = GroupFactory() + self.group4 = GroupFactory() + self.group5 = GroupFactory() + self.group6 = GroupFactory() + + self.valid_form_data = { + 'num_session': '2', + 'third_session': 'true', + 'length_session1': '3600', + 'length_session2': '3600', + 'length_session3': '3600', + 'attendees': '10', + 'conflict1': self.group2.acronym, + 'conflict2': self.group3.acronym, + 'conflict3': self.group4.acronym, + 'comments': 'need lights', + 'session_time_relation': 'subsequent-days', + 'adjacent_with_wg': self.group5.acronym, + 'joint_with_groups': self.group6.acronym, + 'joint_for_session': '3', + 'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'], + 'submit': 'Continue' + } + + def test_valid(self): + # Test with three sessions + form = SessionForm(data=self.valid_form_data, group=self.group1) + self.assertTrue(form.is_valid()) + + # Test with two sessions + self.valid_form_data.update({ + 'length_session3': '', + 'third_session': '', + 'joint_for_session': '2' + }) + form = SessionForm(data=self.valid_form_data, group=self.group1) + self.assertTrue(form.is_valid()) + + # Test with one session + self.valid_form_data.update({ + 'length_session2': '', + 'num_session': 1, + 'joint_for_session': '1', + 'session_time_relation': '', + }) + form = SessionForm(data=self.valid_form_data, group=self.group1) + self.assertTrue(form.is_valid()) + + def test_invalid_groups(self): + new_form_data = { + 'conflict1': 'doesnotexist', + 'conflict2': 'doesnotexist', + 'conflict3': 'doesnotexist', + 'adjacent_with_wg': 'doesnotexist', + 'joint_with_groups': 'doesnotexist', + } + form = self._invalid_test_helper(new_form_data) + self.assertEqual(set(form.errors.keys()), set(new_form_data.keys())) + + def test_invalid_group_appears_in_multiple_conflicts(self): + new_form_data = { + 'conflict1': self.group2.acronym, + 'conflict2': self.group2.acronym, + } + form = self._invalid_test_helper(new_form_data) + self.assertEqual(form.non_field_errors(), ['%s appears in conflicts more than once' % self.group2.acronym]) + + def test_invalid_conflict_with_self(self): + new_form_data = { + 'conflict1': self.group1.acronym, + } + self._invalid_test_helper(new_form_data) + + def test_invalid_session_time_relation(self): + form = self._invalid_test_helper({ + 'third_session': '', + 'length_session2': '', + 'num_session': 1, + 'joint_for_session': '1', + }) + self.assertEqual(form.non_field_errors(), ['Time between sessions can only be used when two ' + 'sessions are requested.']) + + def test_invalid_joint_for_session(self): + form = self._invalid_test_helper({ + 'third_session': '', + 'num_session': 2, + 'joint_for_session': '3', + }) + self.assertEqual(form.non_field_errors(), ['The third session can not be the joint session, ' + 'because you have not requested a third session.']) + + form = self._invalid_test_helper({ + 'third_session': '', + 'length_session2': '', + 'num_session': 1, + 'joint_for_session': '2', + 'session_time_relation': '', + }) + self.assertEqual(form.non_field_errors(), ['The second session can not be the joint session, ' + 'because you have not requested a second session.']) + + def test_invalid_missing_session_length(self): + form = self._invalid_test_helper({ + 'length_session2': '', + 'third_session': 'true', + }) + self.assertEqual(form.non_field_errors(), ['You must enter a length for all sessions']) + + form = self._invalid_test_helper({'length_session2': ''}) + self.assertEqual(form.non_field_errors(), ['You must enter a length for all sessions']) + + form = self._invalid_test_helper({'length_session3': ''}) + self.assertEqual(form.non_field_errors(), ['You must enter a length for all sessions']) + + def _invalid_test_helper(self, new_form_data): + form_data = dict(self.valid_form_data, **new_form_data) + form = SessionForm(data=form_data, group=self.group1) + self.assertFalse(form.is_valid()) + return form \ No newline at end of file diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index 935a9957c..e893daffb 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -21,7 +21,7 @@ from ietf.meeting.models import Meeting, Session, Constraint, ResourceAssociatio from ietf.meeting.helpers import get_meeting from ietf.meeting.utils import add_event_info_to_session_qs from ietf.name.models import SessionStatusName, ConstraintName -from ietf.secr.sreq.forms import SessionForm, ToolStatusForm, allowed_conflicting_groups +from ietf.secr.sreq.forms import SessionForm, ToolStatusForm, allowed_conflicting_groups, JOINT_FOR_SESSION_CHOICES from ietf.secr.utils.decorators import check_permissions from ietf.secr.utils.group import get_my_groups from ietf.utils.mail import send_mail @@ -88,6 +88,11 @@ def get_initial_session(sessions, prune_conflicts=False): timeranges = conflicts.filter(name__slug='timerange') initial['timeranges'] = timeranges[0].timeranges.all() if timeranges else [] initial['timeranges_display'] = [t.desc for t in initial['timeranges']] + for idx, session in enumerate(sessions): + if session.joint_with_groups.count(): + initial['joint_with_groups'] = ' '.join(session.joint_with_groups_acronyms()) + initial['joint_for_session'] = str(idx + 1) + initial['joint_for_session_display'] = dict(JOINT_FOR_SESSION_CHOICES)[initial['joint_for_session']] return initial def get_lock_message(meeting=None): @@ -273,6 +278,8 @@ def confirm(request, acronym): session_data['bethere'] = Person.objects.filter(pk__in=person_id_list) if session_data.get('session_time_relation'): session_data['session_time_relation_display'] = dict(Constraint.TIME_RELATION_CHOICES)[session_data['session_time_relation']] + if session_data.get('joint_for_session'): + session_data['joint_for_session_display'] = dict(JOINT_FOR_SESSION_CHOICES)[session_data['joint_for_session']] if form.cleaned_data.get('timeranges'): session_data['timeranges_display'] = [t.desc for t in form.cleaned_data['timeranges']] session_data['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in request.POST.getlist('resources') ] @@ -314,6 +321,10 @@ def confirm(request, acronym): ) if 'resources' in form.data: new_session.resources.set(session_data['resources']) + if int(form.data.get('joint_for_session', '-1')) == count: + groups_split = form.cleaned_data.get('joint_with_groups').replace(',',' ').split() + joint = Group.objects.filter(acronym__in=groups_split) + new_session.joint_with_groups.set(joint) session_changed(new_session) # write constraint records @@ -469,7 +480,30 @@ def edit(request, acronym, num=None): session.save() session_changed(session) + # New sessions may have been created, refresh the sessions list + sessions = add_event_info_to_session_qs( + Session.objects.filter(group=group, meeting=meeting)).filter( + Q(current_status__isnull=True) | ~Q( + current_status__in=['canceled', 'notmeet'])).order_by('id') + if 'joint_with_groups' in form.changed_data or 'joint_for_session' in form.changed_data: + joint_with_groups_list = form.cleaned_data.get('joint_with_groups').replace(',', ' ').split() + new_joint_with_groups = Group.objects.filter(acronym__in=joint_with_groups_list) + new_joint_for_session_idx = int(form.data.get('joint_for_session', '-1')) - 1 + current_joint_for_session_idx = None + current_joint_with_groups = None + for idx, session in enumerate(sessions): + if session.joint_with_groups.count(): + current_joint_for_session_idx = idx + current_joint_with_groups = session.joint_with_groups.all() + + if current_joint_with_groups != new_joint_with_groups or current_joint_for_session_idx != new_joint_for_session_idx: + if current_joint_for_session_idx is not None: + sessions[current_joint_for_session_idx].joint_with_groups.clear() + 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]) + if 'attendees' in form.changed_data: sessions.update(attendees=form.cleaned_data['attendees']) if 'comments' in form.changed_data: diff --git a/ietf/secr/static/secr/js/sessions.js b/ietf/secr/static/secr/js/sessions.js index 0d6c274fd..7d087eb13 100644 --- a/ietf/secr/static/secr/js/sessions.js +++ b/ietf/secr/static/secr/js/sessions.js @@ -15,28 +15,34 @@ function stat_ls (val){ if (val == 0) { document.form_post.length_session1.disabled = true; document.form_post.length_session2.disabled = true; - document.form_post.length_session3.disabled = true; + if (document.form_post.length_session3) { document.form_post.length_session3.disabled = true; } document.form_post.session_time_relation.disabled = true; + document.form_post.joint_for_session.disabled = true; document.form_post.length_session1.value = 0; document.form_post.length_session2.value = 0; document.form_post.length_session3.value = 0; document.form_post.session_time_relation.value = ''; + document.form_post.joint_for_session.value = ''; document.form_post.third_session.checked=false; } if (val == 1) { document.form_post.length_session1.disabled = false; document.form_post.length_session2.disabled = true; - document.form_post.length_session3.disabled = true; + if (document.form_post.length_session3) { document.form_post.length_session3.disabled = true; } document.form_post.session_time_relation.disabled = true; + document.form_post.joint_for_session.disabled = true; document.form_post.length_session2.value = 0; document.form_post.length_session3.value = 0; document.form_post.session_time_relation.value = ''; + document.form_post.joint_for_session.value = '1'; document.form_post.third_session.checked=false; } if (val == 2) { document.form_post.length_session1.disabled = false; document.form_post.length_session2.disabled = false; + if (document.form_post.length_session3) { document.form_post.length_session3.disabled = false; } document.form_post.session_time_relation.disabled = false; + document.form_post.joint_for_session.disabled = false; } } @@ -116,6 +122,15 @@ function delete_last3 () { document.form_post.conflict3.value = b; document.form_post.wg_selector3.selectedIndex=0; } +function delete_last_joint_with_groups () { + var b = document.form_post.joint_with_groups.value; + var temp = new Array(); + temp = b.split(' '); + temp.pop(); + b = temp.join(' '); + document.form_post.joint_with_groups.value = b; + document.form_post.wg_selector4.selectedIndex=0; +} // Not calling check_prior_confict (see ietf/secr/sreq/forms.py definition of SessionForm) // while we are hacking the use of the current three constraint types around. We could bring diff --git a/ietf/secr/templates/includes/session_info.txt b/ietf/secr/templates/includes/session_info.txt index 5d4a06c56..9fc7f72fa 100644 --- a/ietf/secr/templates/includes/session_info.txt +++ b/ietf/secr/templates/includes/session_info.txt @@ -3,6 +3,7 @@ Working Group Name: {{ group.name }} Area Name: {{ group.parent }} Session Requester: {{ login }} +{% if session.joint_with_groups %}{{ session.joint_for_session_display }} joint with: {{ session.joint_with_groups }}{% endif %} Number of Sessions: {{ session.num_session }} Length of Session(s): {{ session.length_session1|display_duration }}{% if session.length_session2 %}, {{ session.length_session2|display_duration }}{% endif %}{% if session.length_session3 %}, {{ session.length_session3|display_duration }}{% endif %} diff --git a/ietf/secr/templates/includes/sessions_request_form.html b/ietf/secr/templates/includes/sessions_request_form.html index 2ce3f5e9f..dc25af7dd 100755 --- a/ietf/secr/templates/includes/sessions_request_form.html +++ b/ietf/secr/templates/includes/sessions_request_form.html @@ -72,6 +72,22 @@ {{ form.adjacent_with_wg.errors }}{{ form.adjacent_with_wg }} + + Joint session with:
+ (To request one session for multiple WGs together.) + + {{ form.wg_selector4 }} +
+ {{ form.joint_with_groups.errors }}{{ form.joint_with_groups }} + + + + + Of the sessions requested by this WG, the joint session, if applicable, is: + + {{ form.joint_for_session.errors }}{{ form.joint_for_session }} + + Special Requests:
 
i.e. restrictions on meeting times / days, etc. {{ form.comments.errors }}{{ form.comments }} diff --git a/ietf/secr/templates/includes/sessions_request_view.html b/ietf/secr/templates/includes/sessions_request_view.html index 7c5d8b197..9e9a1008c 100644 --- a/ietf/secr/templates/includes/sessions_request_view.html +++ b/ietf/secr/templates/includes/sessions_request_view.html @@ -7,7 +7,7 @@ Length of Session 1:{{ session.length_session1|display_duration }} {% if session.length_session2 %} Length of Session 2:{{ session.length_session2|display_duration }} - Time between sessions:{{ session.session_time_relation_display|default:"No preference" }} + Time between sessions:{% if session.session_time_relation_display %}{{ session.session_time_relation_display }}{% else %}No preference{% endif %} {% endif %} {% if session.length_session3 %} Length of Session 3:{{ session.length_session3|display_duration }} @@ -42,7 +42,17 @@ Adjacent with WG: {{ session.adjacent_with_wg|default:'No preference' }} + + Joint session: + + {% if session.joint_with_groups %} + {{ session.joint_for_session_display }} with: {{ session.joint_with_groups }} + {% else %} + Not a joint session + {% endif %} + + {% autoescape off %} - Special Requests:{{ session.comments }} + Special Requests:{{ session.comments }} {% endautoescape %} diff --git a/ietf/templates/meeting/requests.html b/ietf/templates/meeting/requests.html index a6a401442..5b0678d29 100644 --- a/ietf/templates/meeting/requests.html +++ b/ietf/templates/meeting/requests.html @@ -62,6 +62,9 @@ {{session.group.acronym}} + {% if session.joint_with_groups.count %} + joint with {{ session.joint_with_groups_acronyms|join:' ' }} + {% endif %}