Skip to content

Commit 930aacc

Browse files
committed
Add full edit capability for non-sessions. Fixes ietf-tools#2235. Commit ready for merge.
- Legacy-Id: 13418
1 parent 264f6f3 commit 930aacc

4 files changed

Lines changed: 97 additions & 60 deletions

File tree

ietf/secr/meetings/forms.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -166,36 +166,17 @@ def clean(self):
166166

167167
return cleaned_data
168168

169-
class NonSessionEditForm(forms.Form):
170-
name = forms.CharField(help_text='Name that appears on the agenda')
171-
short = forms.CharField(max_length=32,label='Short Name',help_text='Enter an abbreviated session name (used for material file names)')
172-
location = forms.ModelChoiceField(queryset=Room.objects)
173-
group = forms.ModelChoiceField(
174-
queryset=Group.objects.filter(type__in=['ietf','team'],state='active'),
175-
help_text='''Select a group to associate with this session. For example:<br>
176-
Tutorials = Education,<br>
177-
Code Sprint = Tools Team,<br>
178-
Technical Plenary = IAB,<br>
179-
Administrative Plenary = IAOC or IESG''',empty_label=None)
180-
181-
def __init__(self,*args,**kwargs):
182-
meeting = kwargs.pop('meeting')
183-
self.session = kwargs.pop('session')
184-
super(NonSessionEditForm, self).__init__(*args,**kwargs)
185-
self.fields['location'].queryset = Room.objects.filter(meeting=meeting)
186-
187-
def clean_group(self):
188-
group = self.cleaned_data['group']
189-
if self.session.group != group and self.session.materials.all():
190-
raise forms.ValidationError("ERROR: can't change group after materials have been uploaded")
191-
return group
192-
193169
class TimeSlotForm(forms.Form):
194170
day = forms.ChoiceField(choices=DAYS_CHOICES)
195171
time = forms.TimeField()
196172
duration = ietf.utils.fields.DurationField()
197173
name = forms.CharField(help_text='Name that appears on the agenda')
198174

175+
def __init__(self,*args,**kwargs):
176+
super(TimeSlotForm, self).__init__(*args,**kwargs)
177+
self.fields["time"].widget.attrs["placeholder"] = "HH:MM"
178+
self.fields["duration"].widget.attrs["placeholder"] = "HH:MM"
179+
199180
def clean_duration(self):
200181
'''Limit to HH:MM format'''
201182
duration = self.data['duration']
@@ -208,14 +189,21 @@ class NonSessionForm(TimeSlotForm):
208189
type = forms.ModelChoiceField(queryset=TimeSlotTypeName.objects.filter(used=True).exclude(slug__in=('session',)),empty_label=None)
209190
group = forms.ModelChoiceField(
210191
queryset=Group.objects.filter(type__in=['ietf','team'],state='active'),
211-
help_text='Required for Session types: other, plenary',
192+
help_text='''Select a group to associate with this session. For example:<br>
193+
Tutorials = Education,<br>
194+
Code Sprint = Tools Team,<br>
195+
Plenary = IETF''',
212196
required=False)
197+
location = forms.ModelChoiceField(queryset=Room.objects, required=False)
213198
show_location = forms.BooleanField(required=False)
214199

215200
def __init__(self,*args,**kwargs):
201+
if 'meeting' in kwargs:
202+
self.meeting = kwargs.pop('meeting')
203+
if 'session' in kwargs:
204+
self.session = kwargs.pop('session')
216205
super(NonSessionForm, self).__init__(*args,**kwargs)
217-
self.fields["time"].widget.attrs["placeholder"] = "HH:MM"
218-
self.fields["duration"].widget.attrs["placeholder"] = "HH:MM"
206+
self.fields['location'].queryset = Room.objects.filter(meeting=self.meeting)
219207

220208
def clean(self):
221209
super(NonSessionForm, self).clean()
@@ -225,13 +213,19 @@ def clean(self):
225213
group = cleaned_data['group']
226214
type = cleaned_data['type']
227215
short = cleaned_data['short']
228-
if type.slug in ('other','plenary') and not group:
216+
if type.slug in ('other','plenary','lead') and not group:
229217
raise forms.ValidationError('ERROR: a group selection is required')
230-
if type.slug in ('other','plenary') and not short:
218+
if type.slug in ('other','plenary','lead') and not short:
231219
raise forms.ValidationError('ERROR: a short name is required')
232220

233221
return cleaned_data
234-
222+
223+
def clean_group(self):
224+
group = self.cleaned_data['group']
225+
if hasattr(self, 'session') and self.session.group != group and self.session.materials.all():
226+
raise forms.ValidationError("ERROR: can't change group after materials have been uploaded")
227+
return group
228+
235229
class UploadBlueSheetForm(forms.Form):
236230
file = forms.FileField(help_text='example: bluesheets-84-ancp-01.pdf')
237231

ietf/secr/meetings/tests.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,15 @@ def test_meetings_times(self):
207207
self.client.login(username="secretary", password="secretary+password")
208208
response = self.client.get(url)
209209
self.assertEqual(response.status_code, 200)
210-
210+
response = self.client.post(url, {
211+
'day': 0,
212+
'time':'08:00',
213+
'duration':'01:00',
214+
'name':'Test Morning Session'
215+
}, follow=True)
216+
self.assertRedirects(response, url)
217+
self.assertTrue('Test Morning Session' in response.content)
218+
211219
def test_meetings_times_delete(self):
212220
meeting = make_meeting_test_data()
213221
qs = TimeSlot.objects.filter(meeting=meeting,type='session')
@@ -266,6 +274,30 @@ def test_meetings_nonsession_add_invalid(self):
266274
self.assertEqual(response.status_code, 200)
267275
self.assertTrue('invalid format' in response.content)
268276

277+
def test_meetings_nonsession_edit(self):
278+
meeting = make_meeting_test_data()
279+
session = meeting.session_set.exclude(name='').first() # get first non-session session
280+
timeslot = session.official_timeslotassignment().timeslot
281+
url = reverse('ietf.secr.meetings.views.non_session_edit',kwargs={'meeting_id':42,'schedule_name':meeting.agenda.name,'slot_id':timeslot.pk})
282+
redirect_url = reverse('ietf.secr.meetings.views.non_session',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
283+
new_time = timeslot.time + datetime.timedelta(days=1)
284+
self.client.login(username="secretary", password="secretary+password")
285+
response = self.client.get(url)
286+
self.assertEqual(response.status_code, 200)
287+
response = self.client.post(url, {
288+
'name':'IETF Hackathon',
289+
'short':'hackathon',
290+
'location':timeslot.location.id,
291+
'group':session.group.id,
292+
'time':new_time.strftime('%H:%M'),
293+
'duration':'01:00',
294+
'day':'2',
295+
'type':'other',
296+
})
297+
self.assertRedirects(response, redirect_url)
298+
timeslot = session.official_timeslotassignment().timeslot
299+
self.assertEqual(timeslot.time,new_time)
300+
269301
def test_meetings_select_group(self):
270302
make_meeting_test_data()
271303
url = reverse('ietf.secr.meetings.views.select_group',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})

ietf/secr/meetings/views.py

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515

1616
from ietf.ietfauth.utils import role_required
1717
from ietf.utils.mail import send_mail
18+
from ietf.meeting.forms import duration_string
1819
from ietf.meeting.helpers import get_meeting, make_materials_directories
1920
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, SchedTimeSessAssignment, Schedule
2021
from ietf.group.models import Group, GroupEvent
2122
from ietf.person.models import Person
2223
from ietf.secr.meetings.blue_sheets import create_blue_sheets
2324
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm, MeetingSelectForm,
24-
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
25+
MeetingRoomForm, NewSessionForm, NonSessionForm, TimeSlotForm,
2526
UploadBlueSheetForm, get_next_slot )
2627
from ietf.secr.proceedings.views import build_choices
2728
from ietf.secr.proceedings.utils import handle_upload_file
@@ -459,23 +460,20 @@ def non_session(request, meeting_id, schedule_name):
459460
slots = slots.order_by('-type__name','time')
460461

461462
if request.method == 'POST':
462-
form = NonSessionForm(request.POST)
463+
form = NonSessionForm(request.POST, meeting=meeting)
463464
if form.is_valid():
464-
day = form.cleaned_data['day']
465-
time = form.cleaned_data['time']
465+
time = get_timeslot_time(form, meeting)
466466
name = form.cleaned_data['name']
467467
short = form.cleaned_data['short']
468468
type = form.cleaned_data['type']
469469
group = form.cleaned_data['group']
470470
duration = form.cleaned_data['duration']
471-
t = meeting.date + datetime.timedelta(days=int(day))
472-
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
473471

474472
# create TimeSlot object
475473
timeslot = TimeSlot.objects.create(type=type,
476474
meeting=meeting,
477475
name=name,
478-
time=new_time,
476+
time=time,
479477
duration=duration,
480478
show_location=form.cleaned_data['show_location'])
481479

@@ -501,7 +499,7 @@ def non_session(request, meeting_id, schedule_name):
501499
messages.success(request, 'Non-Sessions updated successfully')
502500
return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name)
503501
else:
504-
form = NonSessionForm(initial={'show_location':True})
502+
form = NonSessionForm(initial={'show_location':True}, meeting=meeting)
505503

506504
if TimeSlot.objects.filter(meeting=meeting,type='other',location__isnull=True):
507505
messages.warning(request, 'There are non-session items which do not have a room assigned')
@@ -552,14 +550,22 @@ def non_session_edit(request, meeting_id, schedule_name, slot_id):
552550
if button_text == 'Cancel':
553551
return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name)
554552

555-
form = NonSessionEditForm(request.POST,meeting=meeting, session=session)
553+
form = NonSessionForm(request.POST,meeting=meeting,session=session)
556554
if form.is_valid():
557555
location = form.cleaned_data['location']
558556
group = form.cleaned_data['group']
559557
name = form.cleaned_data['name']
560558
short = form.cleaned_data['short']
559+
duration = form.cleaned_data['duration']
560+
slot_type = form.cleaned_data['type']
561+
show_location = form.cleaned_data['show_location']
562+
time = get_timeslot_time(form, meeting)
561563
slot.location = location
562564
slot.name = name
565+
slot.time = time
566+
slot.duration = duration
567+
slot.type = slot_type
568+
slot.show_location = show_location
563569
slot.save()
564570
# save group to session object
565571
session.group = group
@@ -573,11 +579,17 @@ def non_session_edit(request, meeting_id, schedule_name, slot_id):
573579
else:
574580
# we need to pass the session to the form in order to disallow changing
575581
# of group after materials have been uploaded
582+
delta = slot.time.date() - meeting.date
576583
initial = {'location':slot.location,
577584
'group':session.group,
578585
'name':session.name,
579-
'short':session.short}
580-
form = NonSessionEditForm(meeting=meeting,session=session,initial=initial)
586+
'short':session.short,
587+
'day':delta.days,
588+
'time':slot.time.strftime('%H:%M'),
589+
'duration':duration_string(slot.duration),
590+
'show_location':slot.show_location,
591+
'type':slot.type}
592+
form = NonSessionForm(initial=initial, meeting=meeting, session=session)
581593

582594
return render(request, 'meetings/non_session_edit.html', {
583595
'meeting': meeting,
@@ -889,25 +901,21 @@ def times(request, meeting_id, schedule_name):
889901
if request.method == 'POST':
890902
form = TimeSlotForm(request.POST)
891903
if form.is_valid():
892-
day = form.cleaned_data['day']
893-
time = form.cleaned_data['time']
904+
time = get_timeslot_time(form, meeting)
894905
duration = form.cleaned_data['duration']
895906
name = form.cleaned_data['name']
896907

897-
t = meeting.date + datetime.timedelta(days=int(day))
898-
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
899-
900908
# don't allow creation of timeslots with same start time as existing timeslots
901909
# assert False, (new_time, time_seen)
902-
if new_time in time_seen:
903-
messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % new_time.strftime('%a %H:%M'))
910+
if time in time_seen:
911+
messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % time.strftime('%a %H:%M'))
904912
return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name)
905913

906914
for room in meeting.room_set.all():
907915
TimeSlot.objects.create(type_id='session',
908916
meeting=meeting,
909917
name=name,
910-
time=new_time,
918+
time=time,
911919
location=room,
912920
duration=duration)
913921

@@ -924,6 +932,14 @@ def times(request, meeting_id, schedule_name):
924932
'times': times},
925933
)
926934

935+
def get_timeslot_time(form, meeting):
936+
'''Returns datetime calculated from day and time form fields'''
937+
time = form.cleaned_data['time']
938+
day = form.cleaned_data['day']
939+
940+
date = meeting.date + datetime.timedelta(days=int(day))
941+
return datetime.datetime(date.year,date.month,date.day,time.hour,time.minute)
942+
927943
@role_required('Secretariat')
928944
def times_edit(request, meeting_id, schedule_name, time):
929945
'''
@@ -944,15 +960,12 @@ def times_edit(request, meeting_id, schedule_name, time):
944960
form = TimeSlotForm(request.POST)
945961
if form.is_valid():
946962
day = form.cleaned_data['day']
947-
time = form.cleaned_data['time']
963+
time = get_timeslot_time(form, meeting)
948964
duration = form.cleaned_data['duration']
949965
name = form.cleaned_data['name']
950966

951-
t = meeting.date + datetime.timedelta(days=int(day))
952-
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
953-
954967
for timeslot in timeslots:
955-
timeslot.time = new_time
968+
timeslot.time = time
956969
timeslot.duration = duration
957970
timeslot.name = name
958971
timeslot.save()

ietf/secr/templates/meetings/non_session.html

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ <h2>TimeSlots</h2>
1818
<th>Show Loc.</th>
1919
<th>Type</th>
2020
<th></th>
21+
<th></th>
2122
</tr>
2223
</thead>
2324
<tbody>
@@ -28,13 +29,10 @@ <h2>TimeSlots</h2>
2829
<td>{{ item.name }}</td>
2930
<td>{{ item.session.short }}</td>
3031
<td>{{ item.session.group.acronym }}</td>
31-
{% if item.type.slug != 'break' and item.type.slug != 'registration' %}
32-
<td><a href="{% url "ietf.secr.meetings.views.non_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">{{ item.location }}</a></td>
33-
{% else %}
34-
<td>{{ item.location }}</td>
35-
{% endif %}
32+
<td>{{ item.location }}</td>
3633
<td>{{ item.show_location }}</td>
3734
<td>{{ item.type }}</td>
35+
<td><a href="{% url "ietf.secr.meetings.views.non_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Edit</a></td>
3836
<td><a href="{% url "ietf.secr.meetings.views.non_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Delete</a></td>
3937
</tr>
4038
{% endfor %}

0 commit comments

Comments
 (0)