Add full edit capability for non-sessions. Fixes #2235. Commit ready for merge.

- Legacy-Id: 13418
This commit is contained in:
Ryan Cross 2017-05-24 21:08:41 +00:00
parent 264f6f36e2
commit 930aaccc87
4 changed files with 97 additions and 60 deletions

View file

@ -166,36 +166,17 @@ class NewSessionForm(forms.Form):
return cleaned_data
class NonSessionEditForm(forms.Form):
name = forms.CharField(help_text='Name that appears on the agenda')
short = forms.CharField(max_length=32,label='Short Name',help_text='Enter an abbreviated session name (used for material file names)')
location = forms.ModelChoiceField(queryset=Room.objects)
group = forms.ModelChoiceField(
queryset=Group.objects.filter(type__in=['ietf','team'],state='active'),
help_text='''Select a group to associate with this session. For example:<br>
Tutorials = Education,<br>
Code Sprint = Tools Team,<br>
Technical Plenary = IAB,<br>
Administrative Plenary = IAOC or IESG''',empty_label=None)
def __init__(self,*args,**kwargs):
meeting = kwargs.pop('meeting')
self.session = kwargs.pop('session')
super(NonSessionEditForm, self).__init__(*args,**kwargs)
self.fields['location'].queryset = Room.objects.filter(meeting=meeting)
def clean_group(self):
group = self.cleaned_data['group']
if self.session.group != group and self.session.materials.all():
raise forms.ValidationError("ERROR: can't change group after materials have been uploaded")
return group
class TimeSlotForm(forms.Form):
day = forms.ChoiceField(choices=DAYS_CHOICES)
time = forms.TimeField()
duration = ietf.utils.fields.DurationField()
name = forms.CharField(help_text='Name that appears on the agenda')
def __init__(self,*args,**kwargs):
super(TimeSlotForm, self).__init__(*args,**kwargs)
self.fields["time"].widget.attrs["placeholder"] = "HH:MM"
self.fields["duration"].widget.attrs["placeholder"] = "HH:MM"
def clean_duration(self):
'''Limit to HH:MM format'''
duration = self.data['duration']
@ -208,14 +189,21 @@ class NonSessionForm(TimeSlotForm):
type = forms.ModelChoiceField(queryset=TimeSlotTypeName.objects.filter(used=True).exclude(slug__in=('session',)),empty_label=None)
group = forms.ModelChoiceField(
queryset=Group.objects.filter(type__in=['ietf','team'],state='active'),
help_text='Required for Session types: other, plenary',
help_text='''Select a group to associate with this session. For example:<br>
Tutorials = Education,<br>
Code Sprint = Tools Team,<br>
Plenary = IETF''',
required=False)
location = forms.ModelChoiceField(queryset=Room.objects, required=False)
show_location = forms.BooleanField(required=False)
def __init__(self,*args,**kwargs):
if 'meeting' in kwargs:
self.meeting = kwargs.pop('meeting')
if 'session' in kwargs:
self.session = kwargs.pop('session')
super(NonSessionForm, self).__init__(*args,**kwargs)
self.fields["time"].widget.attrs["placeholder"] = "HH:MM"
self.fields["duration"].widget.attrs["placeholder"] = "HH:MM"
self.fields['location'].queryset = Room.objects.filter(meeting=self.meeting)
def clean(self):
super(NonSessionForm, self).clean()
@ -225,13 +213,19 @@ class NonSessionForm(TimeSlotForm):
group = cleaned_data['group']
type = cleaned_data['type']
short = cleaned_data['short']
if type.slug in ('other','plenary') and not group:
if type.slug in ('other','plenary','lead') and not group:
raise forms.ValidationError('ERROR: a group selection is required')
if type.slug in ('other','plenary') and not short:
if type.slug in ('other','plenary','lead') and not short:
raise forms.ValidationError('ERROR: a short name is required')
return cleaned_data
def clean_group(self):
group = self.cleaned_data['group']
if hasattr(self, 'session') and self.session.group != group and self.session.materials.all():
raise forms.ValidationError("ERROR: can't change group after materials have been uploaded")
return group
class UploadBlueSheetForm(forms.Form):
file = forms.FileField(help_text='example: bluesheets-84-ancp-01.pdf')

View file

@ -207,7 +207,15 @@ class SecrMeetingTestCase(TestCase):
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, {
'day': 0,
'time':'08:00',
'duration':'01:00',
'name':'Test Morning Session'
}, follow=True)
self.assertRedirects(response, url)
self.assertTrue('Test Morning Session' in response.content)
def test_meetings_times_delete(self):
meeting = make_meeting_test_data()
qs = TimeSlot.objects.filter(meeting=meeting,type='session')
@ -266,6 +274,30 @@ class SecrMeetingTestCase(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTrue('invalid format' in response.content)
def test_meetings_nonsession_edit(self):
meeting = make_meeting_test_data()
session = meeting.session_set.exclude(name='').first() # get first non-session session
timeslot = session.official_timeslotassignment().timeslot
url = reverse('ietf.secr.meetings.views.non_session_edit',kwargs={'meeting_id':42,'schedule_name':meeting.agenda.name,'slot_id':timeslot.pk})
redirect_url = reverse('ietf.secr.meetings.views.non_session',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
new_time = timeslot.time + datetime.timedelta(days=1)
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, {
'name':'IETF Hackathon',
'short':'hackathon',
'location':timeslot.location.id,
'group':session.group.id,
'time':new_time.strftime('%H:%M'),
'duration':'01:00',
'day':'2',
'type':'other',
})
self.assertRedirects(response, redirect_url)
timeslot = session.official_timeslotassignment().timeslot
self.assertEqual(timeslot.time,new_time)
def test_meetings_select_group(self):
make_meeting_test_data()
url = reverse('ietf.secr.meetings.views.select_group',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})

View file

@ -15,13 +15,14 @@ from django.utils.functional import curry
from ietf.ietfauth.utils import role_required
from ietf.utils.mail import send_mail
from ietf.meeting.forms import duration_string
from ietf.meeting.helpers import get_meeting, make_materials_directories
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, SchedTimeSessAssignment, Schedule
from ietf.group.models import Group, GroupEvent
from ietf.person.models import Person
from ietf.secr.meetings.blue_sheets import create_blue_sheets
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm, MeetingSelectForm,
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
MeetingRoomForm, NewSessionForm, NonSessionForm, TimeSlotForm,
UploadBlueSheetForm, get_next_slot )
from ietf.secr.proceedings.views import build_choices
from ietf.secr.proceedings.utils import handle_upload_file
@ -459,23 +460,20 @@ def non_session(request, meeting_id, schedule_name):
slots = slots.order_by('-type__name','time')
if request.method == 'POST':
form = NonSessionForm(request.POST)
form = NonSessionForm(request.POST, meeting=meeting)
if form.is_valid():
day = form.cleaned_data['day']
time = form.cleaned_data['time']
time = get_timeslot_time(form, meeting)
name = form.cleaned_data['name']
short = form.cleaned_data['short']
type = form.cleaned_data['type']
group = form.cleaned_data['group']
duration = form.cleaned_data['duration']
t = meeting.date + datetime.timedelta(days=int(day))
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
# create TimeSlot object
timeslot = TimeSlot.objects.create(type=type,
meeting=meeting,
name=name,
time=new_time,
time=time,
duration=duration,
show_location=form.cleaned_data['show_location'])
@ -501,7 +499,7 @@ def non_session(request, meeting_id, schedule_name):
messages.success(request, 'Non-Sessions updated successfully')
return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name)
else:
form = NonSessionForm(initial={'show_location':True})
form = NonSessionForm(initial={'show_location':True}, meeting=meeting)
if TimeSlot.objects.filter(meeting=meeting,type='other',location__isnull=True):
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):
if button_text == 'Cancel':
return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name)
form = NonSessionEditForm(request.POST,meeting=meeting, session=session)
form = NonSessionForm(request.POST,meeting=meeting,session=session)
if form.is_valid():
location = form.cleaned_data['location']
group = form.cleaned_data['group']
name = form.cleaned_data['name']
short = form.cleaned_data['short']
duration = form.cleaned_data['duration']
slot_type = form.cleaned_data['type']
show_location = form.cleaned_data['show_location']
time = get_timeslot_time(form, meeting)
slot.location = location
slot.name = name
slot.time = time
slot.duration = duration
slot.type = slot_type
slot.show_location = show_location
slot.save()
# save group to session object
session.group = group
@ -573,11 +579,17 @@ def non_session_edit(request, meeting_id, schedule_name, slot_id):
else:
# we need to pass the session to the form in order to disallow changing
# of group after materials have been uploaded
delta = slot.time.date() - meeting.date
initial = {'location':slot.location,
'group':session.group,
'name':session.name,
'short':session.short}
form = NonSessionEditForm(meeting=meeting,session=session,initial=initial)
'short':session.short,
'day':delta.days,
'time':slot.time.strftime('%H:%M'),
'duration':duration_string(slot.duration),
'show_location':slot.show_location,
'type':slot.type}
form = NonSessionForm(initial=initial, meeting=meeting, session=session)
return render(request, 'meetings/non_session_edit.html', {
'meeting': meeting,
@ -889,25 +901,21 @@ def times(request, meeting_id, schedule_name):
if request.method == 'POST':
form = TimeSlotForm(request.POST)
if form.is_valid():
day = form.cleaned_data['day']
time = form.cleaned_data['time']
time = get_timeslot_time(form, meeting)
duration = form.cleaned_data['duration']
name = form.cleaned_data['name']
t = meeting.date + datetime.timedelta(days=int(day))
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
# don't allow creation of timeslots with same start time as existing timeslots
# assert False, (new_time, time_seen)
if new_time in time_seen:
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'))
if time in time_seen:
messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % time.strftime('%a %H:%M'))
return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name)
for room in meeting.room_set.all():
TimeSlot.objects.create(type_id='session',
meeting=meeting,
name=name,
time=new_time,
time=time,
location=room,
duration=duration)
@ -924,6 +932,14 @@ def times(request, meeting_id, schedule_name):
'times': times},
)
def get_timeslot_time(form, meeting):
'''Returns datetime calculated from day and time form fields'''
time = form.cleaned_data['time']
day = form.cleaned_data['day']
date = meeting.date + datetime.timedelta(days=int(day))
return datetime.datetime(date.year,date.month,date.day,time.hour,time.minute)
@role_required('Secretariat')
def times_edit(request, meeting_id, schedule_name, time):
'''
@ -944,15 +960,12 @@ def times_edit(request, meeting_id, schedule_name, time):
form = TimeSlotForm(request.POST)
if form.is_valid():
day = form.cleaned_data['day']
time = form.cleaned_data['time']
time = get_timeslot_time(form, meeting)
duration = form.cleaned_data['duration']
name = form.cleaned_data['name']
t = meeting.date + datetime.timedelta(days=int(day))
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
for timeslot in timeslots:
timeslot.time = new_time
timeslot.time = time
timeslot.duration = duration
timeslot.name = name
timeslot.save()

View file

@ -18,6 +18,7 @@
<th>Show Loc.</th>
<th>Type</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@ -28,13 +29,10 @@
<td>{{ item.name }}</td>
<td>{{ item.session.short }}</td>
<td>{{ item.session.group.acronym }}</td>
{% if item.type.slug != 'break' and item.type.slug != 'registration' %}
<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>
{% else %}
<td>{{ item.location }}</td>
{% endif %}
<td>{{ item.location }}</td>
<td>{{ item.show_location }}</td>
<td>{{ item.type }}</td>
<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>
<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>
</tr>
{% endfor %}