# Copyright The IETF Trust 2013-2020, All Rights Reserved # -*- coding: utf-8 -*- import datetime from collections import defaultdict from django.conf import settings from django.contrib import messages from django.db.models import Q from django.shortcuts import render, get_object_or_404, redirect from django.http import Http404 import debug # pyflakes:ignore from ietf.group.models import Group, GroupFeatures from ietf.ietfauth.utils import has_role, role_required from ietf.meeting.models import Meeting, Session, Constraint, ResourceAssociation, SchedulingEvent from ietf.meeting.helpers import get_meeting from ietf.meeting.utils import add_event_info_to_session_qs from ietf.name.models import SessionStatusName, ConstraintName from ietf.secr.sreq.forms import SessionForm, ToolStatusForm, allowed_conflicting_groups, JOINT_FOR_SESSION_CHOICES from ietf.secr.utils.decorators import check_permissions from ietf.secr.utils.group import get_my_groups from ietf.utils.mail import send_mail from ietf.person.models import Person from ietf.mailtrigger.utils import gather_address_lists # ------------------------------------------------- # Globals # ------------------------------------------------- AUTHORIZED_ROLES=('WG Chair','WG Secretary','RG Chair','IAB Group Chair','Area Director','Secretariat','Team Chair','IRTF Chair') # ------------------------------------------------- # Helper Functions # ------------------------------------------------- def check_app_locked(meeting=None): ''' This function returns True if the application is locked to non-secretariat users. ''' if not meeting: meeting = get_meeting() return bool(meeting.session_request_lock_message) def get_initial_session(sessions, prune_conflicts=False): ''' This function takes a queryset of sessions ordered by 'id' for consistency. It returns a dictionary to be used as the initial for a legacy session form ''' initial = {} if len(sessions) == 0: return initial meeting = sessions[0].meeting group = sessions[0].group conflicts = group.constraint_source_set.filter(meeting=meeting) # even if there are three sessions requested, the old form has 2 in this field initial['num_session'] = min(sessions.count(), 2) # accessing these foreign key fields throw errors if they are unset so we # need to catch these initial['length_session1'] = str(sessions[0].requested_duration.seconds) try: initial['length_session2'] = str(sessions[1].requested_duration.seconds) initial['length_session3'] = str(sessions[2].requested_duration.seconds) except IndexError: pass initial['attendees'] = sessions[0].attendees def valid_conflict(conflict): return conflict.target != sessions[0].group and allowed_conflicting_groups().filter(pk=conflict.target_id).exists() initial['conflict1'] = ' '.join(c.target.acronym for c in conflicts.filter(name__slug='conflict') if not prune_conflicts or valid_conflict(c)) initial['conflict2'] = ' '.join(c.target.acronym for c in conflicts.filter(name__slug='conflic2') if not prune_conflicts or valid_conflict(c)) initial['conflict3'] = ' '.join(c.target.acronym for c in conflicts.filter(name__slug='conflic3') if not prune_conflicts or valid_conflict(c)) initial['comments'] = sessions[0].comments initial['resources'] = sessions[0].resources.all() initial['bethere'] = [x.person for x in sessions[0].constraints().filter(name='bethere').select_related("person")] wg_adjacent = conflicts.filter(name__slug='wg_adjacent') initial['adjacent_with_wg'] = wg_adjacent[0].target.acronym if wg_adjacent else None time_relation = conflicts.filter(name__slug='time_relation') initial['session_time_relation'] = time_relation[0].time_relation if time_relation else None initial['session_time_relation_display'] = time_relation[0].get_time_relation_display if time_relation else None timeranges = conflicts.filter(name__slug='timerange') initial['timeranges'] = timeranges[0].timeranges.all() if timeranges else [] initial['timeranges_display'] = [t.desc for t in initial['timeranges']] for idx, session in enumerate(sessions): if session.joint_with_groups.count(): initial['joint_with_groups'] = ' '.join(session.joint_with_groups_acronyms()) initial['joint_for_session'] = str(idx + 1) initial['joint_for_session_display'] = dict(JOINT_FOR_SESSION_CHOICES)[initial['joint_for_session']] return initial def get_lock_message(meeting=None): ''' Returns the message to display to non-secretariat users when the tool is locked. ''' if not meeting: meeting = get_meeting() return meeting.session_request_lock_message def get_requester_text(person,group): ''' This function takes a Person object and a Group object and returns the text to use in the session request notification email, ie. Joe Smith, a Chair of the ancp working group ''' roles = group.role_set.filter(name__in=('chair','secr'),person=person) if roles: return '%s, a %s of the %s working group' % (person.ascii, roles[0].name, group.acronym) if group.parent and group.parent.role_set.filter(name='ad',person=person): return '%s, a %s Area Director' % (person.ascii, group.parent.acronym.upper()) if person.role_set.filter(name='secr',group__acronym='secretariat'): return '%s, on behalf of the %s working group' % (person.ascii, group.acronym) def save_conflicts(group, meeting, conflicts, name): ''' This function takes a Group, Meeting a string which is a list of Groups acronyms (conflicts), and the constraint name (conflict|conflic2|conflic3) and creates Constraint records ''' constraint_name = ConstraintName.objects.get(slug=name) acronyms = conflicts.replace(',',' ').split() for acronym in acronyms: target = Group.objects.get(acronym=acronym) constraint = Constraint(source=group, target=target, meeting=meeting, name=constraint_name) constraint.save() def send_notification(group,meeting,login,session,action): ''' This function generates email notifications for various session request activities. session argument is a dictionary of fields from the session request form action argument is a string [new|update]. ''' (to_email, cc_list) = gather_address_lists('session_requested',group=group,person=login) from_email = (settings.SESSION_REQUEST_FROM_EMAIL) subject = '%s - New Meeting Session Request for IETF %s' % (group.acronym, meeting.number) template = 'sreq/session_request_notification.txt' # send email context = {} context['session'] = session context['group'] = group context['meeting'] = meeting context['login'] = login context['header'] = 'A new' context['requester'] = get_requester_text(login,group) # update overrides if action == 'update': subject = '%s - Update to a Meeting Session Request for IETF %s' % (group.acronym, meeting.number) context['header'] = 'An update to a' # if third session requested approval is required # change headers TO=ADs, CC=session-request, submitter and cochairs if session.get('length_session3',None): context['session']['num_session'] = 3 (to_email, cc_list) = gather_address_lists('session_requested_long',group=group,person=login) subject = '%s - Request for meeting session approval for IETF %s' % (group.acronym, meeting.number) template = 'sreq/session_approval_notification.txt' #status_text = 'the %s Directors for approval' % group.parent send_mail(None, to_email, from_email, subject, template, context, cc=cc_list) def session_conflicts_as_string(group, meeting): ''' Takes a Group object and Meeting object and returns a string of other groups which have a conflict with this one ''' groups = group.constraint_target_set.filter(meeting=meeting, name__in=['conflict', 'conflic2', 'conflic3']) group_list = [g.source.acronym for g in groups] return ', '.join(group_list) # ------------------------------------------------- # View Functions # ------------------------------------------------- @check_permissions def approve(request, acronym): ''' This view approves the third session. For use by ADs or Secretariat. ''' meeting = get_meeting() group = get_object_or_404(Group, acronym=acronym) session = add_event_info_to_session_qs(Session.objects.filter(meeting=meeting, group=group)).filter(current_status='apprw').first() if session is None: raise Http404 if has_role(request.user,'Secretariat') or group.parent.role_set.filter(name='ad',person=request.user.person): SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='appr'), by=request.user.person, ) session_changed(session) messages.success(request, 'Third session approved') return redirect('ietf.secr.sreq.views.view', acronym=acronym) else: # if an unauthorized user gets here return error messages.error(request, 'Not authorized to approve the third session') return redirect('ietf.secr.sreq.views.view', acronym=acronym) @check_permissions def cancel(request, acronym): ''' This view cancels a session request and sends a notification. To cancel, or withdraw the request set status = deleted. "canceled" status is used by the secretariat. NOTE: this function can also be called after a session has been scheduled during the period when the session request tool is reopened. In this case be sure to clear the timeslot assignment as well. ''' meeting = get_meeting() group = get_object_or_404(Group, acronym=acronym) sessions = Session.objects.filter(meeting=meeting,group=group).order_by('id') login = request.user.person # delete conflicts Constraint.objects.filter(meeting=meeting,source=group).delete() # mark sessions as deleted for session in sessions: SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='deleted'), by=request.user.person, ) session_changed(session) # clear schedule assignments if already scheduled session.timeslotassignments.all().delete() # send notifitcation (to_email, cc_list) = gather_address_lists('session_request_cancelled',group=group,person=login) from_email = (settings.SESSION_REQUEST_FROM_EMAIL) subject = '%s - Cancelling a meeting request for IETF %s' % (group.acronym, meeting.number) send_mail(request, to_email, from_email, subject, 'sreq/session_cancel_notification.txt', {'requester':get_requester_text(login,group), 'meeting':meeting}, cc=cc_list) messages.success(request, 'The %s Session Request has been cancelled' % group.acronym) return redirect('ietf.secr.sreq.views.main') @role_required(*AUTHORIZED_ROLES) def confirm(request, acronym): ''' This view displays details of the new session that has been requested for the user to confirm for submission. ''' # FIXME: this should be using form.is_valid/form.cleaned_data - invalid input will make it crash group = get_object_or_404(Group,acronym=acronym) form = SessionForm(group, request.POST, hidden=True) form.is_valid() meeting = get_meeting() login = request.user.person # check if request already exists for this group if add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(Q(current_status__isnull=True) | ~Q(current_status__in=['deleted', 'notmeet'])): messages.warning(request, 'Sessions for working group %s have already been requested once.' % group.acronym) return redirect('ietf.secr.sreq.views.main') session_data = form.data.copy() if 'bethere' in session_data: person_id_list = [ id for id in form.data['bethere'].split(',') if id ] session_data['bethere'] = Person.objects.filter(pk__in=person_id_list) if session_data.get('session_time_relation'): session_data['session_time_relation_display'] = dict(Constraint.TIME_RELATION_CHOICES)[session_data['session_time_relation']] if session_data.get('joint_for_session'): session_data['joint_for_session_display'] = dict(JOINT_FOR_SESSION_CHOICES)[session_data['joint_for_session']] if form.cleaned_data.get('timeranges'): session_data['timeranges_display'] = [t.desc for t in form.cleaned_data['timeranges']] session_data['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in request.POST.getlist('resources') ] button_text = request.POST.get('submit', '') if button_text == 'Cancel': messages.success(request, 'Session Request has been cancelled') return redirect('ietf.secr.sreq.views.main') button_text = request.POST.get('submit', '') if button_text == 'Cancel': messages.success(request, 'Session Request has been cancelled') return redirect('ietf.secr.sreq.views.main') if request.method == 'POST' and button_text == 'Submit': # delete any existing session records with status = canceled or notmeet add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(current_status__in=['canceled', 'notmeet']).delete() # create new session records count = 0 # lenth_session2 and length_session3 fields might be disabled by javascript and so # wouldn't appear in form data for duration in (form.data.get('length_session1',None),form.data.get('length_session2',None),form.data.get('length_session3',None)): count += 1 if duration: slug = 'apprw' if count == 3 else 'schedw' new_session = Session.objects.create( meeting=meeting, group=group, attendees=form.cleaned_data['attendees'], requested_duration=datetime.timedelta(0,int(duration)), comments=form.cleaned_data['comments'], type_id='regular', ) SchedulingEvent.objects.create( session=new_session, status=SessionStatusName.objects.get(slug=slug), by=login, ) if 'resources' in form.data: new_session.resources.set(session_data['resources']) if int(form.data.get('joint_for_session', '-1')) == count: groups_split = form.cleaned_data.get('joint_with_groups').replace(',',' ').split() joint = Group.objects.filter(acronym__in=groups_split) new_session.joint_with_groups.set(joint) session_changed(new_session) # write constraint records save_conflicts(group,meeting,form.data.get('conflict1',''),'conflict') save_conflicts(group,meeting,form.data.get('conflict2',''),'conflic2') save_conflicts(group,meeting,form.data.get('conflict3',''),'conflic3') save_conflicts(group, meeting, form.data.get('adjacent_with_wg', ''), 'wg_adjacent') if form.cleaned_data.get('session_time_relation'): cn = ConstraintName.objects.get(slug='time_relation') Constraint.objects.create(source=group, meeting=meeting, name=cn, time_relation=form.cleaned_data['session_time_relation']) if form.cleaned_data.get('timeranges'): cn = ConstraintName.objects.get(slug='timerange') constraint = Constraint.objects.create(source=group, meeting=meeting, name=cn) constraint.timeranges.set(form.cleaned_data['timeranges']) if 'bethere' in form.data: bethere_cn = ConstraintName.objects.get(slug='bethere') for p in session_data['bethere']: Constraint.objects.create(name=bethere_cn, source=group, person=p, meeting=new_session.meeting) # clear not meeting add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(current_status='notmeet').delete() # send notification send_notification(group,meeting,login,session_data,'new') status_text = 'IETF Agenda to be scheduled' messages.success(request, 'Your request has been sent to %s' % status_text) return redirect('ietf.secr.sreq.views.main') # POST from request submission session_conflicts = session_conflicts_as_string(group, meeting) return render(request, 'sreq/confirm.html', { 'form': form, 'session': session_data, 'group': group, 'session_conflicts': session_conflicts}, ) #Move this into make_initial def add_essential_people(group,initial): # This will be easier when the form uses Person instead of Email people = set() if 'bethere' in initial: people.update(initial['bethere']) people.update(Person.objects.filter(role__group=group, role__name__in=['chair','ad'])) initial['bethere'] = list(people) def session_changed(session): latest_event = SchedulingEvent.objects.filter(session=session).order_by('-time', '-id').first() if latest_event and latest_event.status_id == "schedw" and session.meeting.schedule != None: # send an email to iesg-secretariat to alert to change pass @check_permissions def edit(request, acronym, num=None): ''' This view allows the user to edit details of the session request ''' meeting = get_meeting(num) group = get_object_or_404(Group, acronym=acronym) sessions = add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(Q(current_status__isnull=True) | ~Q(current_status__in=['canceled', 'notmeet'])).order_by('id') sessions_count = sessions.count() initial = get_initial_session(sessions) if 'resources' in initial: initial['resources'] = [x.pk for x in initial['resources']] # check if app is locked is_locked = check_app_locked(meeting=meeting) if is_locked: messages.warning(request, "The Session Request Tool is closed") session_conflicts = session_conflicts_as_string(group, meeting) login = request.user.person session = Session() if(len(sessions) > 0): session = sessions[0] if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Cancel': return redirect('ietf.secr.sreq.views.view', acronym=acronym) form = SessionForm(group, request.POST, initial=initial) if form.is_valid(): if form.has_changed(): # might be cleaner to simply delete and rewrite all records (but maintain submitter?) # adjust duration or add sessions # session 1 if 'length_session1' in form.changed_data: session = sessions[0] session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1'])) session.save() session_changed(session) # session 2 if 'length_session2' in form.changed_data: length_session2 = form.cleaned_data['length_session2'] if length_session2 == '': sessions[1].delete() elif sessions_count < 2: duration = datetime.timedelta(0,int(form.cleaned_data['length_session2'])) new_session = Session.objects.create( meeting=meeting, group=group, attendees=form.cleaned_data['attendees'], requested_duration=duration, comments=form.cleaned_data['comments'], type_id='regular', ) SchedulingEvent.objects.create( session=new_session, status=SessionStatusName.objects.get(slug='schedw'), by=request.user.person, ) else: duration = datetime.timedelta(0,int(form.cleaned_data['length_session2'])) session = sessions[1] session.requested_duration = duration session.save() # session 3 if 'length_session3' in form.changed_data: length_session3 = form.cleaned_data['length_session3'] if length_session3 == '': sessions[2].delete() elif sessions_count < 3: duration = datetime.timedelta(0,int(form.cleaned_data['length_session3'])) new_session = Session.objects.create( meeting=meeting, group=group, attendees=form.cleaned_data['attendees'], requested_duration=duration, comments=form.cleaned_data['comments'], type_id='regular', ) SchedulingEvent.objects.create( session=new_session, status=SessionStatusName.objects.get(slug='apprw'), by=request.user.person, ) else: duration = datetime.timedelta(0,int(form.cleaned_data['length_session3'])) session = sessions[2] session.requested_duration = duration session.save() session_changed(session) # New sessions may have been created, refresh the sessions list sessions = add_event_info_to_session_qs( Session.objects.filter(group=group, meeting=meeting)).filter( Q(current_status__isnull=True) | ~Q( current_status__in=['canceled', 'notmeet'])).order_by('id') if 'joint_with_groups' in form.changed_data or 'joint_for_session' in form.changed_data: joint_with_groups_list = form.cleaned_data.get('joint_with_groups').replace(',', ' ').split() new_joint_with_groups = Group.objects.filter(acronym__in=joint_with_groups_list) new_joint_for_session_idx = int(form.data.get('joint_for_session', '-1')) - 1 current_joint_for_session_idx = None current_joint_with_groups = None for idx, session in enumerate(sessions): if session.joint_with_groups.count(): current_joint_for_session_idx = idx current_joint_with_groups = session.joint_with_groups.all() if current_joint_with_groups != new_joint_with_groups or current_joint_for_session_idx != new_joint_for_session_idx: if current_joint_for_session_idx is not None: sessions[current_joint_for_session_idx].joint_with_groups.clear() session_changed(sessions[current_joint_for_session_idx]) sessions[new_joint_for_session_idx].joint_with_groups.set(new_joint_with_groups) session_changed(sessions[new_joint_for_session_idx]) if 'attendees' in form.changed_data: sessions.update(attendees=form.cleaned_data['attendees']) if 'comments' in form.changed_data: sessions.update(comments=form.cleaned_data['comments']) if 'conflict1' in form.changed_data: Constraint.objects.filter(meeting=meeting,source=group,name='conflict').delete() save_conflicts(group,meeting,form.cleaned_data['conflict1'],'conflict') if 'conflict2' in form.changed_data: Constraint.objects.filter(meeting=meeting,source=group,name='conflic2').delete() save_conflicts(group,meeting,form.cleaned_data['conflict2'],'conflic2') if 'conflict3' in form.changed_data: Constraint.objects.filter(meeting=meeting,source=group,name='conflic3').delete() save_conflicts(group,meeting,form.cleaned_data['conflict3'],'conflic3') if 'adjacent_with_wg' in form.changed_data: Constraint.objects.filter(meeting=meeting, source=group, name='wg_adjacent').delete() save_conflicts(group, meeting, form.cleaned_data['adjacent_with_wg'], 'wg_adjacent') if 'resources' in form.changed_data: new_resource_ids = form.cleaned_data['resources'] new_resources = [ ResourceAssociation.objects.get(pk=a) for a in new_resource_ids] session.resources = new_resources if 'bethere' in form.changed_data and set(form.cleaned_data['bethere'])!=set(initial['bethere']): session.constraints().filter(name='bethere').delete() bethere_cn = ConstraintName.objects.get(slug='bethere') for p in form.cleaned_data['bethere']: Constraint.objects.create(name=bethere_cn, source=group, person=p, meeting=session.meeting) if 'session_time_relation' in form.changed_data: Constraint.objects.filter(meeting=meeting, source=group, name='time_relation').delete() if form.cleaned_data['session_time_relation']: cn = ConstraintName.objects.get(slug='time_relation') Constraint.objects.create(source=group, meeting=meeting, name=cn, time_relation=form.cleaned_data['session_time_relation']) if 'timeranges' in form.changed_data: Constraint.objects.filter(meeting=meeting, source=group, name='timerange').delete() if form.cleaned_data['timeranges']: cn = ConstraintName.objects.get(slug='timerange') constraint = Constraint.objects.create(source=group, meeting=meeting, name=cn) constraint.timeranges.set(form.cleaned_data['timeranges']) # deprecated # log activity #add_session_activity(group,'Session Request was updated',meeting,user) # send notification send_notification(group,meeting,login,form.cleaned_data,'update') # nuke any cache that might be lingering around. from ietf.meeting.helpers import session_constraint_expire session_constraint_expire(request,session) messages.success(request, 'Session Request updated') return redirect('ietf.secr.sreq.views.view', acronym=acronym) else: if not sessions: return redirect('ietf.secr.sreq.views.new', acronym=acronym) form = SessionForm(group, initial=initial) return render(request, 'sreq/edit.html', { 'is_locked': is_locked, 'meeting': meeting, 'form': form, 'group': group, 'session_conflicts': session_conflicts}, ) @role_required(*AUTHORIZED_ROLES) def main(request): ''' Display list of groups the user has access to. Template variables form: a select box populated with unscheduled groups meeting: the current meeting scheduled_sessions: ''' # check for locked flag is_locked = check_app_locked() if is_locked and not has_role(request.user,'Secretariat'): message = get_lock_message() return render(request, 'sreq/locked.html', { 'message': message}, ) meeting = get_meeting() scheduled_groups = [] unscheduled_groups = [] group_types = GroupFeatures.objects.filter(has_meetings=True).values_list('type', flat=True) my_groups = [g for g in get_my_groups(request.user, conclude=True) if g.type_id in group_types] sessions_by_group = defaultdict(list) for s in add_event_info_to_session_qs(Session.objects.filter(meeting=meeting, group__in=my_groups)).filter(current_status__in=['schedw', 'apprw', 'appr', 'sched']): sessions_by_group[s.group_id].append(s) for group in my_groups: group.meeting_sessions = sessions_by_group.get(group.pk, []) if group.pk in sessions_by_group: # include even if concluded as we need to to see that the # sessions are there scheduled_groups.append(group) else: if group.state_id not in ['conclude', 'bof-conc']: # too late for unscheduled if concluded unscheduled_groups.append(group) # warn if there are no associated groups if not scheduled_groups and not unscheduled_groups: messages.warning(request, 'The account %s is not associated with any groups. If you have multiple Datatracker accounts you may try another or report a problem to ietf-action@ietf.org' % request.user) # add session status messages for use in template for group in scheduled_groups: if len(group.meeting_sessions) < 3: group.status_message = group.meeting_sessions[0].current_status else: group.status_message = 'First two sessions: %s, Third session: %s' % (group.meeting_sessions[0].current_status, group.meeting_sessions[2].current_status) # add not meeting indicators for use in template for group in unscheduled_groups: if any(s.current_status == 'notmeet' for s in group.meeting_sessions): group.not_meeting = True return render(request, 'sreq/main.html', { 'is_locked': is_locked, 'meeting': meeting, 'scheduled_groups': scheduled_groups, 'unscheduled_groups': unscheduled_groups}, ) @check_permissions def new(request, acronym): ''' This view gathers details for a new session request. The user proceeds to confirm() to create the request. ''' group = get_object_or_404(Group, acronym=acronym) meeting = get_meeting() session_conflicts = session_conflicts_as_string(group, meeting) # check if app is locked is_locked = check_app_locked() if is_locked and not has_role(request.user,'Secretariat'): messages.warning(request, "The Session Request Tool is closed") return redirect('ietf.secr.sreq.views.main') if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Cancel': return redirect('ietf.secr.sreq.views.main') form = SessionForm(group, request.POST) if form.is_valid(): return confirm(request, acronym) # the "previous" querystring causes the form to be returned # pre-populated with data from last meeeting's session request elif request.method == 'GET' and 'previous' in request.GET: latest_session = add_event_info_to_session_qs(Session.objects.filter(meeting__type_id='ietf', group=group)).exclude(current_status__in=['notmeet', 'deleted', 'canceled',]).order_by('-meeting__date').first() if latest_session: previous_meeting = Meeting.objects.get(number=latest_session.meeting.number) previous_sessions = add_event_info_to_session_qs(Session.objects.filter(meeting=previous_meeting, group=group)).exclude(current_status__in=['notmeet', 'deleted']).order_by('id') if not previous_sessions: messages.warning(request, 'This group did not meet at %s' % previous_meeting) return redirect('ietf.secr.sreq.views.new', acronym=acronym) else: messages.info(request, 'Fetched session info from %s' % previous_meeting) else: messages.warning(request, 'Did not find any previous meeting') return redirect('ietf.secr.sreq.views.new', acronym=acronym) initial = get_initial_session(previous_sessions, prune_conflicts=True) add_essential_people(group,initial) if 'resources' in initial: initial['resources'] = [x.pk for x in initial['resources']] form = SessionForm(group, initial=initial) else: initial={} add_essential_people(group,initial) form = SessionForm(group, initial=initial) return render(request, 'sreq/new.html', { 'meeting': meeting, 'form': form, 'group': group, 'session_conflicts': session_conflicts}, ) @check_permissions def no_session(request, acronym): ''' The user has indicated that the named group will not be having a session this IETF meeting. Actions: - send notification - update session_activity log ''' meeting = get_meeting() group = get_object_or_404(Group, acronym=acronym) login = request.user.person # delete canceled record if there is one add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(current_status='canceled').delete() # skip if state is already notmeet if add_event_info_to_session_qs(Session.objects.filter(group=group, meeting=meeting)).filter(current_status='notmeet'): messages.info(request, 'The group %s is already marked as not meeting' % group.acronym) return redirect('ietf.secr.sreq.views.main') session = Session.objects.create( group=group, meeting=meeting, requested_duration=datetime.timedelta(0), type_id='regular', ) SchedulingEvent.objects.create( session=session, status=SessionStatusName.objects.get(slug='notmeet'), by=login, ) session_changed(session) # send notification (to_email, cc_list) = gather_address_lists('session_request_not_meeting',group=group,person=login) from_email = (settings.SESSION_REQUEST_FROM_EMAIL) subject = '%s - Not having a session at IETF %s' % (group.acronym, meeting.number) send_mail(request, to_email, from_email, subject, 'sreq/not_meeting_notification.txt', {'login':login, 'group':group, 'meeting':meeting}, cc=cc_list) # deprecated? # log activity #text = 'A message was sent to notify not having a session at IETF %d' % meeting.meeting_num #add_session_activity(group,text,meeting,request.person) # redirect messages.success(request, 'A message was sent to notify not having a session at IETF %s' % meeting.number) return redirect('ietf.secr.sreq.views.main') @role_required('Secretariat') def tool_status(request): ''' This view handles locking and unlocking of the tool to the public. ''' meeting = get_meeting() is_locked = check_app_locked(meeting=meeting) if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Back': return redirect('ietf.secr.sreq.views.main') form = ToolStatusForm(request.POST) if button_text == 'Lock': if form.is_valid(): meeting.session_request_lock_message = form.cleaned_data['message'] meeting.save() messages.success(request, 'Session Request Tool is now Locked') return redirect('ietf.secr.sreq.views.main') elif button_text == 'Unlock': meeting.session_request_lock_message = '' meeting.save() messages.success(request, 'Session Request Tool is now Unlocked') return redirect('ietf.secr.sreq.views.main') else: if is_locked: message = get_lock_message() initial = {'message': message} form = ToolStatusForm(initial=initial) else: form = ToolStatusForm() return render(request, 'sreq/tool_status.html', { 'is_locked': is_locked, 'form': form}, ) @role_required(*AUTHORIZED_ROLES) def view(request, acronym, num = None): ''' This view displays the session request info ''' meeting = get_meeting(num) group = get_object_or_404(Group, acronym=acronym) sessions = add_event_info_to_session_qs(Session.objects.filter(meeting=meeting, group=group)).filter(Q(current_status__isnull=True) | ~Q(current_status__in=('canceled','notmeet','deleted'))).order_by('id') # check if app is locked is_locked = check_app_locked() if is_locked: messages.warning(request, "The Session Request Tool is closed") # if there are no session requests yet, redirect to new session request page if not sessions: if is_locked: return redirect('ietf.secr.sreq.views.main') else: return redirect('ietf.secr.sreq.views.new', acronym=acronym) activities = [{ 'act_date': e.time.strftime('%b %d, %Y'), 'act_time': e.time.strftime('%H:%M:%S'), 'activity': e.status.name, 'act_by': e.by, } for e in sessions[0].schedulingevent_set.select_related('status', 'by')] # other groups that list this group in their conflicts session_conflicts = session_conflicts_as_string(group, meeting) show_approve_button = False # if sessions include a 3rd session waiting approval and the user is a secretariat or AD of the group # display approve button if any(s.current_status == 'apprw' for s in sessions): if has_role(request.user,'Secretariat') or group.parent.role_set.filter(name='ad',person=request.user.person): show_approve_button = True # build session dictionary (like querydict from new session request form) for use in template session = get_initial_session(sessions) return render(request, 'sreq/view.html', { 'is_locked': is_locked, 'session': session, 'activities': activities, 'meeting': meeting, 'group': group, 'session_conflicts': session_conflicts, 'show_approve_button': show_approve_button}, )