datatracker/ietf/secr/sreq/views.py
Henrik Levkowetz f2b883d2bb Merged in ^/trunk@17617.
- Legacy-Id: 17618
2020-04-14 17:11:51 +00:00

844 lines
38 KiB
Python

# 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},
)