# Copyright The IETF Trust 2007-2019, All Rights Reserved # -*- coding: utf-8 -*- import datetime import os import time from django.conf import settings from django.contrib import messages from django.db.models import Max from django.forms.models import inlineformset_factory from django.shortcuts import render, get_object_or_404, redirect import debug # pyflakes:ignore 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, populate_important_dates from ietf.meeting.models import Meeting, Session, Room, TimeSlot, SchedTimeSessAssignment, Schedule, SchedulingEvent from ietf.meeting.utils import add_event_info_to_session_qs from ietf.meeting.utils import only_sessions_that_can_meet from ietf.name.models import SessionStatusName 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, MiscSessionForm, TimeSlotForm, RegularSessionEditForm, UploadBlueSheetForm ) from ietf.secr.proceedings.utils import handle_upload_file from ietf.secr.sreq.views import get_initial_session from ietf.secr.utils.meeting import get_session, get_timeslot from ietf.mailtrigger.utils import gather_address_lists # prep for agenda changes # -------------------------------------------------- # Helper Functions # -------------------------------------------------- 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='regular') # Don't do anything if the room is not capable of handling sessions if room and not room.session_types.filter(slug='regular'): return if room: rooms = [room] else: rooms = meeting.room_set.filter(session_types__slug='regular') 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='regular'): 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='regular', meeting=meeting, name=t.name, time=new_time, location=room, duration=t.duration) def check_misc_sessions(meeting,schedule): ''' Ensure misc session timeslots exist and have appropriate SchedTimeSessAssignment objects for the specified schedule. ''' slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead','offagenda')) plenary = slots.filter(type='plenary').first() if plenary: assignments = plenary.sessionassignments.all() if not assignments.filter(schedule=schedule): source = assignments.first().schedule copy_assignments(slots,source,schedule) def copy_assignments(slots,source,target): ''' Copy SchedTimeSessAssignment objects from source schedule to target schedule. Slots is a queryset of slots ''' for ss in SchedTimeSessAssignment.objects.filter(schedule=source,timeslot__in=slots): SchedTimeSessAssignment.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.schedule if session.timeslotassignments.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(). ''' now = datetime.datetime.now() for group in groups: sessions = group.session_set.filter(meeting=meeting) addrs = gather_address_lists('session_scheduled',group=group,session=sessions[0]) 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 items = [ {'session':s, 'timeslot':get_timeslot(s)} for s in sessions ] items.sort(key=lambda d: d['timeslot'].time) for i,d in enumerate(items): s = d['session'] t = d['timeslot'] dur = s.requested_duration.seconds/60 items[i]['duration'] = "%d:%02d" % (dur//60, dur%60) items[i]['period'] = '%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')) # send email first_event = SchedulingEvent.objects.filter(session=sessions[0]).select_related('by').order_by('time', 'id').first() requested_by = None if first_event and first_event.status_id in ['appw', 'schedw']: requested_by = first_event.by context = { 'items': items, 'meeting': meeting, 'baseurl': settings.IDTRACKER_BASE_URL, } context['to_name'] = str(requested_by) or "Requester" context['agenda_note'] = sessions[0].agenda_note context['session'] = get_initial_session(sessions) context['group'] = group context['login'] = requested_by send_mail(None, addrs.to, from_email, subject, template, context, cc=addrs.cc) # create sent_notification event GroupEvent.objects.create(group=group,time=now,type='sent_notification', by=person,desc='sent scheduled notification for %s' % meeting) # ------------------------------------------------- # 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 # from ietf.utils import log # log.unreachable("2017-07-08") # 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('ietf.secr.meetings.views.main') 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.schedule = 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() populate_important_dates(meeting) # Create Physical new meeting directory and subdirectories make_materials_directories(meeting) messages.success(request, 'The Meeting was created successfully!') return redirect('ietf.secr.meetings.views.main') 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(request, 'meetings/add.html', { 'form': form}, ) @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 blank_sheets_path = settings.SECR_BLUE_SHEET_PATH try: last_run = time.ctime(os.stat(blank_sheets_path).st_ctime) except OSError: last_run = None uploaded_sheets_path = os.path.join(settings.SECR_PROCEEDINGS_DIR,meeting.number,'bluesheets') uploaded_files = sorted(os.listdir(uploaded_sheets_path)) if request.method == 'POST': form = UploadBlueSheetForm(request.POST,request.FILES) if form.is_valid(): file = request.FILES['file'] save_error = handle_upload_file(file,file.name,meeting,'bluesheets') if save_error: form.add_error(None, save_error) else: messages.success(request, 'File Uploaded') return redirect('ietf.secr.meetings.views.blue_sheet', meeting_id=meeting.number) else: form = UploadBlueSheetForm() return render(request, 'meetings/blue_sheet.html', { 'meeting': meeting, 'url': url, 'form': form, 'last_run': last_run, 'uploaded_files': uploaded_files}, ) @role_required('Secretariat') def blue_sheet_generate(request, meeting_id): ''' Generate bluesheets ''' meeting = get_object_or_404(Meeting, number=meeting_id) if request.method == "POST": # TODO: Why aren't 'ag' in here as well? groups = Group.objects.filter( type__in=['wg','rg'], session__timeslotassignments__schedule=meeting.schedule).order_by('acronym') create_blue_sheets(meeting, groups) messages.success(request, 'Blue Sheets generated') return redirect('ietf.secr.meetings.views.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('ietf.secr.meetings.views.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('ietf.secr.meetings.views.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('ietf.secr.meetings.views.view', meeting_id=meeting_id) else: form = MeetingModelForm(instance=meeting) return render(request, 'meetings/edit_meeting.html', { 'meeting': meeting, 'form' : form, }, ) @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('-date') if request.method == 'POST': return redirect('ietf.secr.meetings.views.view', meeting_id=request.POST['meeting']) choices = [ (str(x.number),str(x.number)) for x in meetings ] form = MeetingSelectForm(choices=choices) return render(request, 'meetings/main.html', { 'form': form, 'meetings': meetings}, ) @role_required('Secretariat') def misc_sessions(request, meeting_id, schedule_name): ''' Display and add misc session time slots, e.g. 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_misc_sessions(meeting,schedule) misc_session_types = ['break','reg','other','plenary','lead'] assignments = schedule.assignments.filter(timeslot__type__in=misc_session_types) assignments = assignments.order_by('-timeslot__type__name','timeslot__time') if request.method == 'POST': form = MiscSessionForm(request.POST, meeting=meeting) if form.is_valid(): 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'] location = form.cleaned_data['location'] # create TimeSlot object timeslot = TimeSlot.objects.create(type=type, meeting=meeting, name=name, time=time, duration=duration, location=location, 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.objects.create(meeting=meeting, name=name, short=short, group=group, type=type) SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='sched'), by=request.user.person, ) # create association SchedTimeSessAssignment.objects.create(timeslot=timeslot, session=session, schedule=schedule) messages.success(request, 'Misc. sessions updated successfully') return redirect('ietf.secr.meetings.views.misc_sessions', meeting_id=meeting_id, schedule_name=schedule_name) else: form = MiscSessionForm(initial={'show_location':True}, meeting=meeting) no_room = TimeSlot.objects.filter(meeting=meeting,type='other',location__isnull=True) if no_room: messages.warning(request, 'There are misc. session time slots which do not have a room assigned') session_statuses = { e.session_id: e.status_id for e in SchedulingEvent.objects.filter(session__in=[a.session_id for a in assignments]).order_by('time', 'id') } for a in assignments: a.current_session_status = session_statuses.get(a.session_id) return render(request, 'meetings/misc_sessions.html', { 'assignments': assignments, 'form': form, 'meeting': meeting, 'schedule': schedule, 'selected': 'misc-sessions'}, ) @role_required('Secretariat') def misc_session_cancel(request, meeting_id, schedule_name, slot_id): ''' This function cancels the misc session TimeSlot. Check for uploaded material first. SchedTimeSessAssignment objects get cancelled as well. ''' slot = get_object_or_404(TimeSlot, id=slot_id) meeting = get_object_or_404(Meeting, number=meeting_id) schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name) if request.method == 'POST' and request.POST['post'] == 'yes': for session in Session.objects.filter(timeslotassignments__schedule=schedule, timeslotassignments__timeslot=slot): SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='canceled'), by=request.user.person, ) messages.success(request, 'The session was cancelled successfully') return redirect('ietf.secr.meetings.views.misc_sessions', meeting_id=meeting_id, schedule_name=schedule_name) return render(request, 'confirm_cancel.html', {'object': slot}) @role_required('Secretariat') def misc_session_delete(request, meeting_id, schedule_name, slot_id): ''' This function deletes the misc session TimeSlot. Check for uploaded material first. SchedTimeSessAssignment objects get deleted as well. ''' slot = get_object_or_404(TimeSlot, id=slot_id) if request.method == 'POST' and request.POST['post'] == 'yes': assignments = slot.sessionassignments.all() session_objects = [ x.session for x in assignments ] 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('ietf.secr.meetings.views.misc_sessions', meeting_id=meeting_id, schedule_name=schedule_name) # delete high order assignments, then sessions and slots assignments.delete() Session.objects.filter(pk__in=[ x.pk for x in session_objects ]).delete() slot.delete() messages.success(request, 'The entry was deleted successfully') return redirect('ietf.secr.meetings.views.misc_sessions', meeting_id=meeting_id, schedule_name=schedule_name) return render(request, 'confirm_delete.html', {'object': slot}) @role_required('Secretariat') def misc_session_edit(request, meeting_id, schedule_name, slot_id): ''' Allows the user to assign a location to this misc 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 == 'Back': return redirect('ietf.secr.meetings.views.misc_sessions', meeting_id=meeting_id, schedule_name=schedule_name) form = MiscSessionForm(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 session.name = name session.short = short session.save() messages.success(request, 'Location saved') return redirect('ietf.secr.meetings.views.misc_sessions', 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 delta = slot.time.date() - meeting.date initial = {'location':slot.location, 'group':session.group, 'name':session.name, '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 = MiscSessionForm(initial=initial, meeting=meeting, session=session) return render(request, 'meetings/misc_session_edit.html', { 'meeting': meeting, 'form': form, 'schedule': schedule, 'slot': slot}, ) @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.schedule.assignments.filter(timeslot__type='regular'): 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 sessions = add_event_info_to_session_qs(Session.objects.filter(timeslotassignments__schedule=meeting.schedule_id)).filter(current_status__in=["schedw", "appr"]) for session in sessions: SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='sched'), by=request.user.person, ) send_notifications(meeting,groups,request.user.person) messages.success(request, "Notifications Sent") return redirect('ietf.secr.meetings.views.view', meeting_id=meeting.number) return render(request, 'meetings/notifications.html', { 'meeting': meeting, 'groups': sorted(groups, key=lambda a: a.acronym), 'last_notice': last_notice }, ) @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('ietf.secr.meetings.views.main', 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('ietf.secr.meetings.views.rooms', meeting_id=meeting_id, schedule_name=schedule_name) else: formset = RoomFormset(instance=meeting, prefix='room') return render(request, 'meetings/rooms.html', { 'meeting': meeting, 'schedule': schedule, 'formset': formset, 'selected': 'rooms'} ) @role_required('Secretariat') def regular_sessions(request, meeting_id, schedule_name): ''' Display and edit Session 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) sessions = add_event_info_to_session_qs( only_sessions_that_can_meet(schedule.meeting.session_set) ).order_by('group__acronym') if request.method == 'POST': if 'cancel' in request.POST: pk = request.POST.get('pk') session = get_object_or_404(sessions, pk=pk) SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='canceled'), by=request.user.person, ) messages.success(request, 'Session cancelled') status_names = {n.slug: n.name for n in SessionStatusName.objects.all()} for s in sessions: s.current_status_name = status_names.get(s.current_status, s.current_status) return render(request, 'meetings/sessions.html', { 'meeting': meeting, 'schedule': schedule, 'sessions': sessions, 'formset': None, 'selected': 'regular-sessions',}, ) @role_required('Secretariat') def regular_session_edit(request, meeting_id, schedule_name, session_id): ''' Edit session details ''' meeting = get_object_or_404(Meeting, number=meeting_id) schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name) session = get_object_or_404(Session, id=session_id) assignment = SchedTimeSessAssignment.objects.filter(schedule=schedule, session=session).first() if request.method == 'POST': form = RegularSessionEditForm(request.POST, instance=session) if form.is_valid(): form.save() messages.success(request, 'Session saved') return redirect('ietf.secr.meetings.views.regular_sessions', meeting_id=meeting_id,schedule_name=schedule_name) else: form = RegularSessionEditForm(instance=session) current_status_name = None latest_event = SchedulingEvent.objects.filter(session=session).order_by('-time', '-id').first() if latest_event: current_status_name = latest_event.status.name return render(request, 'meetings/regular_session_edit.html', { 'meeting': meeting, 'schedule': schedule, 'session': session, 'timeslot': assignment.timeslot if assignment else None, 'current_status_name': current_status_name, 'form': form}, ) @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='regular'): 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, meeting=meeting) if form.is_valid(): time = get_timeslot_time(form, meeting) duration = form.cleaned_data['duration'] name = form.cleaned_data['name'] # don't allow creation of timeslots with same start time as existing timeslots # assert False, (new_time, time_seen) 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='regular', meeting=meeting, name=name, time=time, location=room, duration=duration) messages.success(request, 'Timeslots created') return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name) else: form = TimeSlotForm(meeting=meeting) return render(request, 'meetings/times.html', { 'form': form, 'meeting': meeting, 'schedule': schedule, 'times': times, 'selected': '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): ''' 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('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name) form = TimeSlotForm(request.POST, meeting=meeting) if form.is_valid(): day = form.cleaned_data['day'] time = get_timeslot_time(form, meeting) duration = form.cleaned_data['duration'] name = form.cleaned_data['name'] for timeslot in timeslots: timeslot.time = time timeslot.duration = duration timeslot.name = name timeslot.save() messages.success(request, 'TimeSlot saved') return redirect('ietf.secr.meetings.views.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, meeting=meeting) return render(request, 'meetings/times_edit.html', { 'meeting': meeting, 'schedule': schedule, 'form': form}, ) @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) status = SessionStatusName.objects.get(slug='schedw') if request.method == 'POST' and request.POST['post'] == 'yes': for slot in TimeSlot.objects.filter(meeting=meeting,time=dtime): for assignment in slot.sessionassignments.all(): if assignment.session: session = assignment.session latest_event = SchedulingEvent.objects.filter(session=session).order_by('-time', '-id').first() if not latest_event or latest_event.status_id != 'schedw': SchedulingEvent.objects.create( session=session, status=status, by=request.user.person, ) assignment.delete() slot.delete() messages.success(request, 'The entry was deleted successfully') return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name) return render(request, 'confirm_delete.html', { 'object': '%s timeslots' % dtime.strftime("%A %H:%M"), 'extra': 'Any sessions assigned to this timeslot will be unscheduled' }) @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(request, 'meetings/view.html', { 'meeting': meeting}, )