fixed 2 bugs where combined sessions aren't being handled properly in edit() and remove()

- Legacy-Id: 5807
This commit is contained in:
Ryan Cross 2013-07-08 18:51:13 +00:00
parent d19fe19ce0
commit 3655dbb36e

View file

@ -54,12 +54,12 @@ def assign(session,timeslot,meeting):
timeslot=timeslot)
session.status_id = 'sched'
session.save()
def build_timeslots(request,meeting,room=None):
'''
This function takes a Meeting object and an optional room argument. If room isn't passed we
pre-create the full set of timeslot records using the last meeting as a template.
If room is passed pre-create timeslots for the new room. Call this after saving new rooms
This function takes a Meeting object and an optional room argument. If room isn't passed we
pre-create the full set of timeslot records using the last meeting as a template.
If room is passed pre-create timeslots for the new room. Call this after saving new rooms
or adding a room.
'''
schedule = get_schedule(request,meeting)
@ -75,7 +75,7 @@ def build_timeslots(request,meeting,room=None):
source_meeting = meeting
else:
source_meeting = get_last_meeting(meeting)
delta = meeting.date - source_meeting.date
initial = []
timeslots = []
@ -104,7 +104,7 @@ def build_nonsession(request, meeting):
delta = meeting.date - last_meeting.date
system = Person.objects.get(name='(system)')
schedule = get_schedule(request, meeting)
for slot in TimeSlot.objects.filter(meeting=last_meeting,type__in=('break','reg','other','plenary')):
new_time = slot.time + delta
session = None
@ -117,7 +117,7 @@ def build_nonsession(request, meeting):
requested_by=system,
status_id='sched')
session.save()
ts = TimeSlot.objects.create(type=slot.type,
meeting=meeting,
name=slot.name,
@ -125,7 +125,7 @@ def build_nonsession(request, meeting):
duration=slot.duration,
show_location=slot.show_location)
ScheduledSession.create(schedule=schedule,session=session,timeslot=ts)
def get_last_meeting(meeting):
last_number = int(meeting.number) - 1
return Meeting.objects.get(number=last_number)
@ -133,7 +133,7 @@ def get_last_meeting(meeting):
def get_schedule(request,meeting):
'''
This function takes a Request object and Meeting object and returns the Official
Schdeule for the meeting. It will create the Schedule if it doesn't already
Schdeule for the meeting. It will create the Schedule if it doesn't already
exist. Call this function after creating a new meeting.
'''
schedule = meeting.agenda
@ -148,7 +148,7 @@ def get_schedule(request,meeting):
meeting.agenda = schedule
meeting.save()
return schedule
def is_combined(session, meeting):
'''
Check to see if this session is using two combined timeslots
@ -157,7 +157,7 @@ def is_combined(session, meeting):
return True
else:
return False
def make_directories(meeting):
'''
This function takes a meeting object and creates the appropriate materials directories
@ -189,21 +189,21 @@ def send_notification(request, sessions):
else:
subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, sessions[0].meeting.number)
template = 'meetings/session_schedule_notification.txt'
# easier to populate template from timeslot perspective. assuming one-to-one timeslot-session
count = 0
session_info = ''
data = [ (s,get_timeslot(s)) for s in sessions ]
for s,t in data:
count += 1
session_info += session_info_template.format(group.acronym,
count,
session_info += session_info_template.format(group.acronym,
count,
s.requested_duration,
t.time.strftime('%A'),
t.name,
'%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')),
t.location)
# send email
context = {}
context['to_name'] = sessions[0].requested_by
@ -237,9 +237,9 @@ def sort_groups(meeting):
scheduled_groups.append(group)
else:
unscheduled_groups.append(group)
return scheduled_groups, unscheduled_groups
# -------------------------------------------------
# AJAX Functions
# -------------------------------------------------
@ -248,14 +248,14 @@ def ajax_get_times(request, meeting_id, day):
Ajax function to get timeslot times for a given day.
returns JSON format response: [{id:start_time, value:start_time-end_time},...]
'''
# TODO strip duplicates if there are any
# TODO strip duplicates if there are any
results=[]
room = Room.objects.filter(meeting__number=meeting_id)[0]
slots = TimeSlot.objects.filter(meeting__number=meeting_id,time__week_day=day,location=room).order_by('time')
for slot in slots:
d = {'id': slot.time.strftime('%H%M'), 'value': '%s-%s' % (slot.time.strftime('%H%M'), slot.end_time().strftime('%H%M'))}
results.append(d)
return HttpResponse(simplejson.dumps(results), mimetype='application/javascript')
# --------------------------------------------------
# STANDARD VIEW FUNCTIONS
@ -282,10 +282,10 @@ def add(request):
form = MeetingModelForm(request.POST)
if form.is_valid():
meeting = form.save()
#Create initial Official schedule
get_schedule(request,meeting)
#Create Physical new meeting directory and subdirectories
make_directories(meeting)
@ -307,9 +307,9 @@ def blue_sheet(request, meeting_id):
Blue Sheet view. The user can generate blue sheets or upload scanned bluesheets
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
url = settings.SECR_BLUE_SHEET_URL
if request.method == 'POST':
form = UploadBlueSheetForm(request.POST,request.FILES)
if form.is_valid():
@ -318,26 +318,26 @@ def blue_sheet(request, meeting_id):
messages.success(request, 'File Uploaded')
url = reverse('meetings_blue_sheet', kwargs={'meeting_id':meeting.number})
return HttpResponseRedirect(url)
else:
form = UploadBlueSheetForm()
return render_to_response('meetings/blue_sheet.html', {
'meeting': meeting,
'url': url,
'form': form},
RequestContext(request, {}),
)
def blue_sheet_generate(request, meeting_id):
'''
Generate bluesheets
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
groups = Group.objects.filter(session__meeting=meeting).order_by('acronym')
create_blue_sheets(meeting, groups)
messages.success(request, 'Blue Sheets generated')
url = reverse('meetings_blue_sheet', kwargs={'meeting_id':meeting.number})
return HttpResponseRedirect(url)
@ -398,14 +398,14 @@ def main(request):
In this view the user can choose a meeting to manage or elect to create a new meeting.
'''
meetings = Meeting.objects.filter(type='ietf').order_by('-number')
if request.method == 'POST':
redirect_url = reverse('meetings_view', kwargs={'meeting_id':request.POST['group']})
return HttpResponseRedirect(redirect_url)
choices = [ (str(x.number),str(x.number)) for x in meetings ]
form = GroupSelectForm(choices=choices)
return render_to_response('meetings/main.html', {
'form': form,
'meetings': meetings},
@ -417,13 +417,13 @@ def non_session(request, meeting_id):
Display and add "non-session" time slots, ie. registration, beverage and snack breaks
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
# if the Break/Registration records don't exist yet (new meeting) create them
if not TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other')):
build_nonsession(request, meeting)
slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary')).order_by('-type__name','time')
if request.method == 'POST':
form = NonSessionForm(request.POST)
if form.is_valid():
@ -436,7 +436,7 @@ def non_session(request, meeting_id):
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=form.cleaned_data['type'],
meeting=meeting,
@ -444,9 +444,9 @@ def non_session(request, meeting_id):
time=new_time,
duration=duration,
show_location=form.cleaned_data['show_location'])
# create a dummy Session object to hold materials
# NOTE: we're setting group to none here, but the set_room page will force user
# NOTE: we're setting group to none here, but the set_room page will force user
# to pick a legitimate group
session = None
if type.slug in ('other','plenary'):
@ -457,21 +457,21 @@ def non_session(request, meeting_id):
requested_by=Person.objects.get(name='(system)'),
status_id='sched')
session.save()
# create association
ScheduledSession.objects.create(timeslot=timeslot,
session=session,
schedule=meeting.agenda)
messages.success(request, 'Non-Sessions updated successfully')
url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
else:
else:
form = NonSessionForm(initial={'show_location':True})
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')
return render_to_response('meetings/non_session.html', {
'slots': slots,
'form': form,
@ -482,7 +482,7 @@ def non_session(request, meeting_id):
def non_session_delete(request, meeting_id, slot_id):
'''
This function deletes the non-session TimeSlot. For "other" and "plenary" timeslot types
we need to delete the corresponding Session object as well. Check for uploaded material
we need to delete the corresponding Session object as well. Check for uploaded material
first.
'''
slot = get_object_or_404(TimeSlot, id=slot_id)
@ -492,11 +492,11 @@ def non_session_delete(request, meeting_id, slot_id):
messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name)
url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
else:
slot.sessions.all().delete()
slot.delete()
messages.success(request, 'Non-Session timeslot deleted successfully')
url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
@ -508,13 +508,13 @@ def non_session_edit(request, meeting_id, slot_id):
meeting = get_object_or_404(Meeting, number=meeting_id)
slot = get_object_or_404(TimeSlot, id=slot_id)
session = get_session(slot)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
form = NonSessionEditForm(request.POST,meeting=meeting, session=session)
if form.is_valid():
location = form.cleaned_data['location']
@ -529,11 +529,11 @@ def non_session_edit(request, meeting_id, slot_id):
session.name = name
session.short = short
session.save()
messages.success(request, 'Location saved')
url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
else:
# we need to pass the session to the form in order to disallow changing
# of group after materials have been uploaded
@ -542,14 +542,14 @@ def non_session_edit(request, meeting_id, slot_id):
'name':session.name,
'short':session.short}
form = NonSessionEditForm(meeting=meeting,session=session,initial=initial)
return render_to_response('meetings/non_session_edit.html', {
'meeting': meeting,
'form': form,
'slot': slot},
RequestContext(request, {}),
)
def remove_session(request, meeting_id, acronym):
'''
Remove session from agenda. Disassociate session from timeslot and set status.
@ -560,7 +560,7 @@ def remove_session(request, meeting_id, acronym):
group = get_object_or_404(Group, acronym=acronym)
sessions = Session.objects.filter(meeting=meeting,group=group)
now = datetime.datetime.now()
for session in sessions:
ss = session.official_scheduledsession()
ss.session = None
@ -569,7 +569,7 @@ def remove_session(request, meeting_id, acronym):
session.status_id = 'canceled'
session.modified = now
session.save()
messages.success(request, '%s Session removed from agenda' % (group.acronym))
url = reverse('meetings_select_group', kwargs={'meeting_id':meeting.number})
return HttpResponseRedirect(url)
@ -579,7 +579,7 @@ def rooms(request, meeting_id):
Display and edit MeetingRoom records for the specified meeting
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
# if no rooms exist yet (new meeting) formset extra=10
first_time = not bool(meeting.room_set.all())
extra = 10 if first_time else 0
@ -594,18 +594,18 @@ def rooms(request, meeting_id):
formset = RoomFormset(request.POST, instance=meeting, prefix='room')
if formset.is_valid():
formset.save()
# if we are creating rooms for the first time create full set of timeslots
if first_time:
build_timeslots(request, meeting)
# otherwise if we're modifying rooms
else:
# add timeslots for new rooms, deleting rooms automatically deletes timeslots
for form in formset.forms[formset.initial_form_count():]:
if form.instance.pk:
build_timeslots(request, meeting,room=form.instance)
messages.success(request, 'Meeting Rooms changed successfully')
url = reverse('meetings_rooms', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
@ -628,15 +628,15 @@ def schedule(request, meeting_id, acronym):
legacy_session = get_initial_session(sessions)
session_conflicts = session_conflicts_as_string(group, meeting)
now = datetime.datetime.now()
# build initial
initial = []
for s in sessions:
d = {'session':s.id,
'note':s.agenda_note}
timeslot = get_timeslot(s)
if timeslot:
d['room'] = timeslot.location.id
d['day'] = timeslot.time.isoweekday() % 7 + 1 # adjust to django week_day
@ -646,11 +646,11 @@ def schedule(request, meeting_id, acronym):
if is_combined(s,meeting):
d['combine'] = True
initial.append(d)
# need to use curry here to pass custom variable to form init
NewSessionFormset = formset_factory(NewSessionForm, extra=0)
NewSessionFormset.form = staticmethod(curry(NewSessionForm, meeting=meeting))
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
@ -658,8 +658,8 @@ def schedule(request, meeting_id, acronym):
return HttpResponseRedirect(url)
formset = NewSessionFormset(request.POST,initial=initial)
extra_form = ExtraSessionForm(request.POST)
extra_form = ExtraSessionForm(request.POST)
if formset.is_valid() and extra_form.is_valid():
# TODO formsets don't have has_changed until Django 1.3
has_changed = False
@ -674,13 +674,20 @@ def schedule(request, meeting_id, acronym):
combine = form.cleaned_data.get('combine',None)
session = Session.objects.get(id=id)
initial_timeslot = get_timeslot(session)
# find new timeslot
new_day = meeting.date + datetime.timedelta(days=int(day)-1)
hour = datetime.time(int(time[:2]),int(time[2:]))
new_time = datetime.datetime.combine(new_day,hour)
timeslot = TimeSlot.objects.filter(meeting=meeting,time=new_time,location=room)[0]
# COMBINE SECTION - BEFORE --------------
if 'combine' in form.changed_data and not combine:
next_slot = get_next_slot(initial_timeslot)
for ss in next_slot.scheduledsession_set.filter(schedule=meeting.agenda,session=session):
ss.session = None
ss.save()
# ---------------------------------------
if any(x in form.changed_data for x in ('day','time','room')):
# clear the old association
if initial_timeslot:
@ -688,54 +695,48 @@ def schedule(request, meeting_id, acronym):
for ss in session.scheduledsession_set.filter(schedule=meeting.agenda):
ss.session = None
ss.save()
if timeslot:
assign(session,timeslot,meeting)
if timeslot.sessions.all().count() > 1:
messages.warning(request, 'WARNING: There are now multiple sessions scheduled for the timeslot: %s' % timeslot)
else:
session.status_id = 'schedw'
session.modified = now
session.save()
if 'note' in form.changed_data:
session.agenda_note = note
session.modified = now
session.save()
# COMBINE SECTION ----------------------
if 'combine' in form.changed_data:
next_slot = get_next_slot(timeslot)
if combine:
assign(session,next_slot,meeting)
else:
for ss in next_slot.scheduledsession_set.filter(schedule=meeting.agenda,session=session):
ss.session = None
ss.save()
# COMBINE SECTION - AFTER ---------------
if 'combine' in form.changed_data and combine:
next_slot = get_next_slot(timeslot)
assign(session,next_slot,meeting)
# ---------------------------------------
# notify. dont send if Tutorial, BOF or indicated on form
notification_message = "No notification has been sent to anyone for this session."
if (has_changed
if (has_changed
and not extra_form.cleaned_data.get('no_notify',False)
and group.state.slug != 'bof'
and get_timeslot(session)): # and the session is scheduled, else skip
send_notification(request, sessions)
notification_message = "Notification sent."
if has_changed:
messages.success(request, 'Session(s) Scheduled for %s. %s' % (group.acronym, notification_message))
url = reverse('meetings_select_group', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
else:
formset = NewSessionFormset(initial=initial)
extra_form = ExtraSessionForm()
return render_to_response('meetings/schedule.html', {
'extra_form': extra_form,
'group': group,
@ -750,11 +751,11 @@ def select_group(request, meeting_id):
'''
In this view the user can select the group to schedule. Only those groups that have
submitted session requests appear in the dropdowns.
NOTE: BOF list includes Proposed Working Group type, per Wanda
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
if request.method == 'POST':
group = request.POST.get('group',None)
if group:
@ -762,24 +763,24 @@ def select_group(request, meeting_id):
else:
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id})
messages.error(request, 'No group selected')
return HttpResponseRedirect(redirect_url)
# split groups into scheduled / unscheduled
scheduled_groups, unscheduled_groups = sort_groups(meeting)
# prep group form
wgs = filter(lambda a: a.type_id in ('wg','ag') and a.state_id=='active', unscheduled_groups)
group_form = GroupSelectForm(choices=build_choices(wgs))
# prep BOFs form
bofs = filter(lambda a: a.type_id=='wg' and a.state_id in ('bof','proposed'), unscheduled_groups)
bof_form = GroupSelectForm(choices=build_choices(bofs))
# prep IRTF form
irtfs = filter(lambda a: a.type_id=='rg' and a.state_id in ('active','proposed'), unscheduled_groups)
irtf_form = GroupSelectForm(choices=build_choices(irtfs))
return render_to_response('meetings/select_group.html', {
'group_form': group_form,
'bof_form': bof_form,
@ -788,17 +789,17 @@ def select_group(request, meeting_id):
'meeting': meeting},
RequestContext(request, {}),
)
def times(request, meeting_id):
'''
Display and edit time slots (TimeSlots). It doesn't display every TimeSlot
object for the meeting because there is one timeslot per time per room,
object for the meeting because there is one timeslot per time per room,
rather it displays all the unique times.
The first time this view is called for a meeting it creates a form with times
prepopulated from the last meeting
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
# build list of timeslots
slots = []
timeslots = []
@ -812,7 +813,7 @@ def times(request, meeting_id):
'time':t.time,
'end_time':t.end_time()})
times = sorted(slots, key=lambda a: a['time'])
if request.method == 'POST':
form = TimeSlotForm(request.POST)
if form.is_valid():
@ -820,16 +821,16 @@ def times(request, meeting_id):
time = form.cleaned_data['time']
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
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'))
url = reverse('meetings_times', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
for room in meeting.room_set.all():
ts = TimeSlot.objects.create(type_id='session',
meeting=meeting,
@ -838,11 +839,11 @@ def times(request, meeting_id):
location=room,
duration=duration)
ScheduledSession.objects.create(schedule=meeting.agenda,timeslot=ts)
messages.success(request, 'Timeslots created')
url = reverse('meetings_times', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
else:
form = TimeSlotForm()
@ -852,31 +853,31 @@ def times(request, meeting_id):
'times': times},
RequestContext(request, {}),
)
def times_delete(request, meeting_id, time):
'''
This view handles bulk delete of all timeslots matching time (datetime) for the given
meeting. There is one timeslot for each room.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
qs = meeting.agenda.scheduledsession_set.filter(timeslot__time=dtime,
session__isnull=False)
if qs:
messages.error(request, 'ERROR deleting timeslot. There is one or more sessions scheduled for this timeslot.')
url = reverse('meetings_times', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
TimeSlot.objects.filter(meeting=meeting,time=dtime).delete()
messages.success(request, 'Timeslot deleted')
url = reverse('meetings_times', kwargs={'meeting_id':meeting_id})
return HttpResponseRedirect(url)
def view(request, meeting_id):
'''
View Meeting information.
@ -891,7 +892,7 @@ def view(request, meeting_id):
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
return render_to_response('meetings/view.html', {
'meeting': meeting},
RequestContext(request, {}),