datatracker/ietf/secr/meetings/views.py

1020 lines
40 KiB
Python

import datetime
import json
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import Max
from django.forms.formsets import formset_factory
from django.forms.models import inlineformset_factory
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.utils.functional import curry
from ietf.ietfauth.utils import role_required
from ietf.utils.mail import send_mail
from ietf.meeting.helpers import get_meeting
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, ScheduledSession, 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,
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
UploadBlueSheetForm, get_next_slot )
from ietf.secr.proceedings.views import build_choices, handle_upload_file, make_directories
from ietf.secr.sreq.forms import GroupSelectForm
from ietf.secr.sreq.views import get_initial_session
from ietf.secr.utils.mail import get_cc_list
from ietf.secr.utils.meeting import get_session, get_timeslot
# prep for agenda changes
# --------------------------------------------------
# Helper Functions
# --------------------------------------------------
def assign(session,timeslot,meeting,schedule=None):
'''
Robust function to assign a session to a timeslot. Much simplyfied 2014-03-26.
'''
if schedule == None:
schedule = meeting.agenda
ScheduledSession.objects.create(schedule=schedule,
session=session,
timeslot=timeslot)
session.status_id = 'sched'
session.save()
def build_timeslots(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
or adding a room.
'''
slots = meeting.timeslot_set.filter(type='session')
if room:
rooms = [room]
else:
rooms = meeting.room_set.all()
if not slots or room:
# if we are just building timeslots for a new room, the room argument was passed,
# then we need to use current meeting times as a template, not the last meeting times
if room:
source_meeting = meeting
else:
source_meeting = get_last_meeting(meeting)
delta = meeting.date - source_meeting.date
timeslots = []
time_seen = set()
for t in source_meeting.timeslot_set.filter(type='session'):
if not t.time in time_seen:
time_seen.add(t.time)
timeslots.append(t)
for t in timeslots:
new_time = t.time + delta
for room in rooms:
TimeSlot.objects.create(type_id='session',
meeting=meeting,
name=t.name,
time=new_time,
location=room,
duration=t.duration)
def build_nonsession(meeting,schedule):
'''
This function takes a meeting object and creates non-session records
for a new meeting, based on the last meeting
'''
last_meeting = get_last_meeting(meeting)
if not last_meeting:
return None
delta = meeting.date - last_meeting.date
system = Person.objects.get(name='(system)')
secretariat = Group.objects.get(acronym='secretariat')
for slot in TimeSlot.objects.filter(meeting=last_meeting,type__in=('break','reg','other','plenary','lead')):
new_time = slot.time + delta
session = None
# create Session object for Tutorials to hold materials
if slot.type.slug in ('other','plenary'):
session = Session(meeting=meeting,
name=slot.name,
short=get_session(slot).short,
group=get_session(slot).group,
requested_by=system,
status_id='sched')
else:
session, __ = Session.objects.get_or_create(meeting=meeting,
name=slot.name,
group=secretariat,
requested_by=system,
status_id='sched')
session.save()
ts = TimeSlot.objects.create(type=slot.type,
meeting=meeting,
name=slot.name,
time=new_time,
duration=slot.duration,
show_location=slot.show_location)
if session:
ScheduledSession.objects.create(schedule=schedule,session=session,timeslot=ts)
def check_nonsession(meeting,schedule):
'''
Ensure non-session timeslots exist and have appropriate ScheduledSession objects
for the specified schedule.
'''
slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead','offagenda'))
if not slots:
build_nonsession(meeting,schedule)
return None
plenary = slots.filter(type='plenary').first()
if plenary:
scheduledsessions = plenary.scheduledsession_set.all()
if not scheduledsessions.filter(schedule=schedule):
source = scheduledsessions.first().schedule
copy_scheduledsessions(slots,source,schedule)
def copy_scheduledsessions(slots,source,target):
'''
Copy scheduledsession objects from source schedule to target schedule. Slots is
a queryset of slots
'''
for ss in ScheduledSession.objects.filter(schedule=source,timeslot__in=slots):
ScheduledSession.objects.create(schedule=target,session=ss.session,timeslot=ss.timeslot)
def get_last_meeting(meeting):
last_number = int(meeting.number) - 1
try:
return Meeting.objects.get(number=last_number)
except Meeting.DoesNotExist:
return None
def is_combined(session,meeting,schedule=None):
'''
Check to see if this session is using two combined timeslots
'''
if schedule == None:
schedule = meeting.agenda
if session.scheduledsession_set.filter(schedule=schedule).count() > 1:
return True
else:
return False
def send_notifications(meeting, groups, person):
'''
Send session scheduled email notifications for each group in groups. Person is the
user who initiated this action, request.uesr.get_profile().
'''
session_info_template = '''{0} Session {1} ({2})
{3}, {4} {5}
Room Name: {6}
---------------------------------------------
'''
now = datetime.datetime.now()
for group in groups:
sessions = group.session_set.filter(meeting=meeting)
to_email = sessions[0].requested_by.role_email('chair').address
# TODO confirm list, remove requested_by from cc, add session-request@ietf.org?
cc_list = get_cc_list(group)
from_email = ('"IETF Secretariat"','agenda@ietf.org')
if len(sessions) == 1:
subject = '%s - Requested session has been scheduled for IETF %s' % (group.acronym, meeting.number)
else:
subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, 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 ]
data = [ (s,t) for s,t in data if t ]
for s,t in data:
count += 1
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
context['agenda_note'] = sessions[0].agenda_note
context['session'] = get_initial_session(sessions)
context['session_info'] = session_info
context['group'] = group
context['login'] = sessions[0].requested_by
send_mail(None,
to_email,
from_email,
subject,
template,
context,
cc=cc_list)
# create sent_notification event
GroupEvent.objects.create(group=group,time=now,type='sent_notification',
by=person,desc='sent scheduled notification for %s' % meeting)
def sort_groups(meeting,schedule=None):
'''
Similar to sreq.views.sort_groups
Takes a Meeting object and returns a tuple scheduled_groups, unscheduled groups.
'''
if not schedule:
schedule = meeting.agenda
scheduled_groups = []
unscheduled_groups = []
#sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','notmeet','canceled'))
sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','canceled'))
groups_with_sessions = [ s.group for s in sessions ]
gset = set(groups_with_sessions)
sorted_groups_with_sessions = sorted(gset, key = lambda instance: instance.acronym)
scheduled_sessions = ScheduledSession.objects.filter(schedule=schedule,session__isnull=False)
groups_with_timeslots = [ x.session.group for x in scheduled_sessions ]
for group in sorted_groups_with_sessions:
if group in groups_with_timeslots:
scheduled_groups.append(group)
else:
unscheduled_groups.append(group)
return scheduled_groups, unscheduled_groups
# -------------------------------------------------
# AJAX Functions
# -------------------------------------------------
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
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(json.dumps(results), content_type='application/javascript')
# --------------------------------------------------
# STANDARD VIEW FUNCTIONS
# --------------------------------------------------
@role_required('Secretariat')
def add(request):
'''
Add a new IETF Meeting. Creates Meeting and Proceeding objects.
**Templates:**
* ``meetings/add.html``
**Template Variables:**
* proceedingform
'''
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings')
form = MeetingModelForm(request.POST)
if form.is_valid():
meeting = form.save()
schedule = Schedule.objects.create(meeting = meeting,
name = 'Empty-Schedule',
owner = Person.objects.get(name='(System)'),
visible = True,
public = True)
meeting.agenda = schedule
# we want to carry session request lock status over from previous meeting
previous_meeting = get_meeting( int(meeting.number) - 1 )
meeting.session_request_lock_message = previous_meeting.session_request_lock_message
meeting.save()
#Create Physical new meeting directory and subdirectories
make_directories(meeting)
messages.success(request, 'The Meeting was created successfully!')
return redirect('meetings')
else:
# display initial forms
max_number = Meeting.objects.filter(type='ietf').aggregate(Max('number'))['number__max']
form = MeetingModelForm(initial={'number':int(max_number) + 1})
return render_to_response('meetings/add.html', {
'form': form},
RequestContext(request, {}),
)
@role_required('Secretariat')
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():
file = request.FILES['file']
handle_upload_file(file,file.name,meeting,'bluesheets')
messages.success(request, 'File Uploaded')
return redirect('meetings_blue_sheet', meeting_id=meeting.number)
else:
form = UploadBlueSheetForm()
return render_to_response('meetings/blue_sheet.html', {
'meeting': meeting,
'url': url,
'form': form},
RequestContext(request, {}),
)
@role_required('Secretariat')
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')
return redirect('meetings_blue_sheet', meeting_id=meeting.number)
@role_required('Secretariat')
def blue_sheet_redirect(request):
'''
This is the generic blue sheet URL. It gets the next IETF meeting and redirects
to the meeting specific URL.
'''
today = datetime.date.today()
qs = Meeting.objects.filter(date__gt=today,type='ietf').order_by('date')
if qs:
meeting = qs[0]
else:
meeting = Meeting.objects.filter(type='ietf').order_by('-date')[0]
return redirect('meetings_blue_sheet', meeting_id=meeting.number)
@role_required('Secretariat')
def edit_meeting(request, meeting_id):
'''
Edit Meeting information.
**Templates:**
* ``meetings/meeting_edit.html``
**Template Variables:**
* meeting, form
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
if request.method == 'POST':
button_text = request.POST.get('submit','')
if button_text == 'Cancel':
return redirect('meetings_view', meeting_id=meeting_id)
form = MeetingModelForm(request.POST, instance=meeting)
if form.is_valid():
form.save()
messages.success(request,'The meeting entry was changed successfully')
return redirect('meetings_view', meeting_id=meeting_id)
else:
form = MeetingModelForm(instance=meeting)
return render_to_response('meetings/edit_meeting.html', {
'meeting': meeting,
'form' : form, },
RequestContext(request,{}),
)
@role_required('Secretariat')
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':
return redirect('meetings_view', meeting_id=request.POST['group'])
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},
RequestContext(request, {}),
)
@role_required('Secretariat')
def non_session(request, meeting_id, schedule_name):
'''
Display and add "non-session" time slots, ie. registration, beverage and snack breaks
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
check_nonsession(meeting,schedule)
slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead')).order_by('-type__name','time')
if request.method == 'POST':
form = NonSessionForm(request.POST)
if form.is_valid():
day = form.cleaned_data['day']
time = form.cleaned_data['time']
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,
duration=duration,
show_location=form.cleaned_data['show_location'])
if timeslot.type.slug not in ('other','plenary','lead'):
group = Group.objects.get(acronym='secretariat')
# create associated Session object
session = Session(meeting=meeting,
name=name,
short=short,
group=group,
requested_by=Person.objects.get(name='(system)'),
status_id='sched')
session.save()
# create association
ScheduledSession.objects.create(timeslot=timeslot,
session=session,
schedule=schedule)
messages.success(request, 'Non-Sessions updated successfully')
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
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,
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
@role_required('Secretariat')
def non_session_delete(request, meeting_id, schedule_name, 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 first. ScheduledSession objects get deleted as well.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
# schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
slot = get_object_or_404(TimeSlot, id=slot_id)
if slot.type_id in ('other','plenary','lead'):
scheduledsessions = slot.scheduledsession_set.filter(schedule__meeting=meeting)
session_objects = [ x.session for x in scheduledsessions ]
for session in session_objects:
if session.materials.exclude(states__slug='deleted'):
messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
else:
Session.objects.filter(pk__in=[ x.pk for x in session_objects ]).delete()
slot.delete()
messages.success(request, 'Non-Session timeslot deleted successfully')
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
@role_required('Secretariat')
def non_session_edit(request, meeting_id, schedule_name, slot_id):
'''
Allows the user to assign a location to this non-session timeslot
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
slot = get_object_or_404(TimeSlot, id=slot_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
session = get_session(slot,schedule=schedule)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
form = NonSessionEditForm(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']
slot.location = location
slot.name = name
slot.save()
# save group to session object
session.group = group
session.name = name
session.short = short
session.save()
messages.success(request, 'Location saved')
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
else:
# we need to pass the session to the form in order to disallow changing
# of group after materials have been uploaded
initial = {'location':slot.location,
'group':session.group,
'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,
'schedule': schedule,
'slot': slot},
RequestContext(request, {}),
)
@role_required('Secretariat')
def notifications(request, meeting_id):
'''
Send scheduled session email notifications. Finds all groups with
schedule changes since the last time notifications were sent.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
last_notice = GroupEvent.objects.filter(type='sent_notification').first()
groups = set()
for ss in meeting.agenda.scheduledsession_set.filter(timeslot__type='session'):
last_notice = ss.session.group.latest_event(type='sent_notification')
if last_notice and ss.modified > last_notice.time:
groups.add(ss.session.group)
elif not last_notice:
groups.add(ss.session.group)
if request.method == "POST":
# ensure session state is scheduled
for ss in meeting.agenda.scheduledsession_set.all():
session = ss.session
if session.status.slug == "schedw":
session.status_id = "sched"
session.scheduled = datetime.datetime.now()
session.save()
send_notifications(meeting,groups,request.user.person)
messages.success(request, "Notifications Sent")
return redirect('meetings_view', meeting_id=meeting.number)
return render_to_response('meetings/notifications.html', {
'meeting': meeting,
'groups': sorted(groups, key=lambda a: a.acronym),
'last_notice': last_notice },
RequestContext(request, {}),
)
@role_required('Secretariat')
def remove_session(request, meeting_id, acronym):
'''
Remove session from agenda. Disassociate session from timeslot and set status.
According to Wanda this option is used when people cancel, so the Session
request should be deleted as well.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
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
ss.modified = now
ss.save()
session.status_id = 'canceled'
session.modified = now
session.save()
messages.success(request, '%s Session removed from agenda' % (group.acronym))
return redirect('meetings_select_group', meeting_id=meeting.number)
@role_required('Secretariat')
def rooms(request, meeting_id, schedule_name):
'''
Display and edit MeetingRoom records for the specified meeting
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
# 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
RoomFormset = inlineformset_factory(Meeting, Room, form=MeetingRoomForm, formset=BaseMeetingRoomFormSet, can_delete=True, extra=extra)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings', meeting_id=meeting_id,schedule_name=schedule_name)
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(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(meeting,room=form.instance)
messages.success(request, 'Meeting Rooms changed successfully')
return redirect('meetings_rooms', meeting_id=meeting_id, schedule_name=schedule_name)
else:
formset = RoomFormset(instance=meeting, prefix='room')
return render_to_response('meetings/rooms.html', {
'meeting': meeting,
'schedule': schedule,
'formset': formset},
RequestContext(request, {}),
)
@role_required('Secretariat')
def schedule(request, meeting_id, schedule_name, acronym):
'''
This view handles scheduling session requests to TimeSlots
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
group = get_object_or_404(Group, acronym=acronym)
sessions = Session.objects.filter(meeting=meeting,group=group,status__in=('schedw','apprw','appr','sched','canceled'))
legacy_session = get_initial_session(sessions)
now = datetime.datetime.now()
# build initial
initial = []
for s in sessions:
d = {'session':s.id,
'note':s.agenda_note}
timeslot = get_timeslot(s, schedule=schedule)
if timeslot:
d['room'] = timeslot.location.id
d['day'] = timeslot.time.isoweekday() % 7 + 1 # adjust to django week_day
d['time'] = timeslot.time.strftime('%H%M')
else:
d['day'] = 2 # default
if is_combined(s,meeting,schedule=schedule):
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':
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
formset = NewSessionFormset(request.POST,initial=initial)
if formset.is_valid():
# TODO formsets don't have has_changed until Django 1.3
has_changed = False
for form in formset.forms:
if form.has_changed():
has_changed = True
id = form.cleaned_data['session']
note = form.cleaned_data['note']
room = form.cleaned_data['room']
time = form.cleaned_data['time']
day = form.cleaned_data['day']
combine = form.cleaned_data.get('combine',None)
session = Session.objects.get(id=id)
initial_timeslot = get_timeslot(session,schedule=schedule)
# 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=schedule,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:
# delete scheduledsession records to unschedule
session.scheduledsession_set.filter(schedule=schedule).delete()
if timeslot:
assign(session,timeslot,meeting,schedule=schedule)
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 - AFTER ---------------
if 'combine' in form.changed_data and combine:
next_slot = get_next_slot(timeslot)
assign(session,next_slot,meeting,schedule=schedule)
# ---------------------------------------
if has_changed:
messages.success(request, 'Session(s) Scheduled for %s.' % group.acronym )
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
else:
formset = NewSessionFormset(initial=initial)
return render_to_response('meetings/schedule.html', {
'group': group,
'meeting': meeting,
'schedule': schedule,
'show_request': True,
'session': legacy_session,
'formset': formset},
RequestContext(request, {}),
)
@role_required('Secretariat')
def select(request, meeting_id, schedule_name):
'''
Options to edit Rooms & Times or schedule a session
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
return render_to_response('meetings/select.html', {
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
@role_required('Secretariat')
def select_group(request, meeting_id, schedule_name):
'''
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)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
if request.method == 'POST':
group = request.POST.get('group',None)
if group:
redirect_url = reverse('meetings_schedule', kwargs={'meeting_id':meeting_id,'acronym':group,'schedule_name':schedule_name})
else:
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id,'schedule_name':schedule_name})
messages.error(request, 'No group selected')
return HttpResponseRedirect(redirect_url)
# split groups into scheduled / unscheduled
scheduled_groups, unscheduled_groups = sort_groups(meeting,schedule)
# 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,
'irtf_form': irtf_form,
'scheduled_groups': scheduled_groups,
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
@role_required('Secretariat')
def times(request, meeting_id, schedule_name):
'''
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,
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)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
# build list of timeslots
slots = []
timeslots = []
time_seen = set()
for t in meeting.timeslot_set.filter(type='session'):
if not t.time in time_seen:
time_seen.add(t.time)
timeslots.append(t)
for t in timeslots:
slots.append({'name':t.name,
'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():
day = form.cleaned_data['day']
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
# 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'))
return redirect('meetings_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,
location=room,
duration=duration)
messages.success(request, 'Timeslots created')
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
else:
form = TimeSlotForm()
return render_to_response('meetings/times.html', {
'form': form,
'meeting': meeting,
'schedule': schedule,
'times': times},
RequestContext(request, {}),
)
@role_required('Secretariat')
def times_edit(request, meeting_id, schedule_name, time):
'''
This view handles bulk edit of timeslot details.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
form = TimeSlotForm(request.POST)
if form.is_valid():
day = form.cleaned_data['day']
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)
for timeslot in timeslots:
timeslot.time = new_time
timeslot.duration = duration
timeslot.name = name
timeslot.save()
messages.success(request, 'TimeSlot saved')
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
else:
# we need to pass the session to the form in order to disallow changing
# of group after materials have been uploaded
day = dtime.strftime('%w')
if day == 6:
day = -1
initial = {'day':day,
'time':dtime.strftime('%H:%M'),
'duration':timeslots.first().duration,
'name':timeslots.first().name}
form = TimeSlotForm(initial=initial)
return render_to_response('meetings/times_edit.html', {
'meeting': meeting,
'schedule': schedule,
'form': form},
RequestContext(request, {}),
)
@role_required('Secretariat')
def times_delete(request, meeting_id, schedule_name, 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)
TimeSlot.objects.filter(meeting=meeting,time=dtime).delete()
messages.success(request, 'Timeslot deleted')
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
@role_required('Secretariat')
def unschedule(request, meeting_id, schedule_name, session_id):
'''
Unschedule given session object
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
session = get_object_or_404(Session, id=session_id)
session.scheduledsession_set.filter(schedule=meeting.agenda).delete()
# TODO: change session state?
messages.success(request, 'Session unscheduled')
return redirect('meetings_select_group', meeting_id=meeting_id, schedule_name=schedule_name)
@role_required('Secretariat')
def view(request, meeting_id):
'''
View Meeting information.
**Templates:**
* ``meetings/view.html``
**Template Variables:**
* meeting , proceeding
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
return render_to_response('meetings/view.html', {
'meeting': meeting},
RequestContext(request, {}),
)