add edit view. refactor to use modelforms
- Legacy-Id: 11176
This commit is contained in:
parent
facead5d95
commit
3f3e6f0b14
|
@ -610,3 +610,9 @@ def session_end_time(session):
|
|||
timeslot = session.official_timeslotassignment().timeslot
|
||||
return timeslot.time + timeslot.duration
|
||||
|
||||
@register.filter
|
||||
def format_timedelta(timedelta):
|
||||
s = timedelta.seconds
|
||||
hours, remainder = divmod(s, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
return '{hours:02d}:{minutes:02d}'.format(hours=hours,minutes=minutes)
|
||||
|
|
|
@ -9,22 +9,27 @@ from django.utils.encoding import force_text
|
|||
from django.utils import six
|
||||
|
||||
from ietf.doc.models import Document, DocAlias, State, NewRevisionDocEvent
|
||||
from ietf.doc.utils import get_document_content
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.meeting.models import Session, countries, timezones
|
||||
from ietf.meeting.helpers import assign_interim_session
|
||||
from ietf.meeting.models import Session, Meeting, Schedule, countries, timezones
|
||||
from ietf.meeting.helpers import get_next_interim_number, assign_interim_session
|
||||
from ietf.meeting.helpers import is_meeting_approved
|
||||
from ietf.message.models import Message
|
||||
from ietf.person.models import Person
|
||||
from ietf.secr.utils.meeting import get_upload_root
|
||||
from ietf.utils.fields import DatepickerDateField
|
||||
|
||||
# need to insert empty option for use in ChoiceField
|
||||
#countries.insert(0, ('', '-'*9 ))
|
||||
# countries.insert(0, ('', '-'*9 ))
|
||||
countries.insert(0, ('', ''))
|
||||
timezones.insert(0, ('', '-'*9 ))
|
||||
timezones.insert(0, ('', '-' * 9))
|
||||
|
||||
# -------------------------------------------------
|
||||
# DurationField from Django 1.8
|
||||
# -------------------------------------------------
|
||||
|
||||
|
||||
def duration_string(duration):
|
||||
days = duration.days
|
||||
seconds = duration.seconds
|
||||
|
@ -36,7 +41,8 @@ def duration_string(duration):
|
|||
hours = minutes // 60
|
||||
minutes = minutes % 60
|
||||
|
||||
string = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
|
||||
# string = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
|
||||
string = '{:02d}:{:02d}'.format(hours, minutes)
|
||||
if days:
|
||||
string = '{} '.format(days) + string
|
||||
if microseconds:
|
||||
|
@ -44,6 +50,10 @@ def duration_string(duration):
|
|||
|
||||
return string
|
||||
|
||||
custom_duration_re = re.compile(
|
||||
r'^(?P<hours>\d+):(?P<minutes>\d+)$'
|
||||
)
|
||||
|
||||
standard_duration_re = re.compile(
|
||||
r'^'
|
||||
r'(?:(?P<days>-?\d+) (days?, )?)?'
|
||||
|
@ -67,6 +77,7 @@ iso8601_duration_re = re.compile(
|
|||
r'$'
|
||||
)
|
||||
|
||||
|
||||
def parse_duration(value):
|
||||
"""Parses a duration string and returns a datetime.timedelta.
|
||||
|
||||
|
@ -74,7 +85,9 @@ def parse_duration(value):
|
|||
|
||||
Also supports ISO 8601 representation.
|
||||
"""
|
||||
match = standard_duration_re.match(value)
|
||||
match = custom_duration_re.match(value)
|
||||
if not match:
|
||||
match = standard_duration_re.match(value)
|
||||
if not match:
|
||||
match = iso8601_duration_re.match(value)
|
||||
if match:
|
||||
|
@ -84,6 +97,7 @@ def parse_duration(value):
|
|||
kw = {k: float(v) for k, v in six.iteritems(kw) if v is not None}
|
||||
return datetime.timedelta(**kw)
|
||||
|
||||
|
||||
class DurationField(Field):
|
||||
default_error_messages = {
|
||||
'invalid': 'Enter a valid duration.',
|
||||
|
@ -109,6 +123,7 @@ class DurationField(Field):
|
|||
# Helpers
|
||||
# -------------------------------------------------
|
||||
|
||||
|
||||
class GroupModelChoiceField(forms.ModelChoiceField):
|
||||
'''
|
||||
Custom ModelChoiceField, changes the label to a more readable format
|
||||
|
@ -116,43 +131,42 @@ class GroupModelChoiceField(forms.ModelChoiceField):
|
|||
def label_from_instance(self, obj):
|
||||
return obj.acronym
|
||||
|
||||
'''
|
||||
class BaseSessionFormSet(BaseFormSet):
|
||||
def save_new_objects(self, commit=True):
|
||||
self.new_objects = []
|
||||
for form in self.extra_forms:
|
||||
if not form.has_changed():
|
||||
continue
|
||||
# If someone has marked an add form for deletion, don't save the
|
||||
# object.
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
continue
|
||||
self.new_objects.append(self.save_new(form, commit=commit))
|
||||
if not commit:
|
||||
self.saved_forms.append(form)
|
||||
return self.new_objects
|
||||
|
||||
def save(self,commit=True):
|
||||
#return self.save_existing_objects(commit) + self.save_new_objects(commit)
|
||||
return self.save_new_objects(commit)
|
||||
'''
|
||||
|
||||
# -------------------------------------------------
|
||||
# Forms
|
||||
# -------------------------------------------------
|
||||
|
||||
class InterimRequestForm(forms.Form):
|
||||
group = GroupModelChoiceField(queryset = Group.objects.filter(type__in=('wg','rg'),state='active').order_by('acronym'))
|
||||
|
||||
class InterimMeetingModelForm(forms.ModelForm):
|
||||
group = GroupModelChoiceField(queryset=Group.objects.filter(type__in=('wg', 'rg'), state='active').order_by('acronym'))
|
||||
in_person = forms.BooleanField(required=False)
|
||||
meeting_type = forms.ChoiceField(choices=(("single", "Single"), ("multi-day", "Multi-Day"), ('series','Series')), required=False, initial='single', widget=forms.RadioSelect)
|
||||
meeting_type = forms.ChoiceField(choices=(
|
||||
("single", "Single"),
|
||||
("multi-day", "Multi-Day"),
|
||||
('series', 'Series')), required=False, initial='single', widget=forms.RadioSelect)
|
||||
approved = forms.BooleanField(required=False)
|
||||
|
||||
city = forms.CharField(max_length=255, required=False)
|
||||
country = forms.ChoiceField(choices=countries, required=False)
|
||||
time_zone = forms.ChoiceField(choices=timezones)
|
||||
|
||||
class Meta:
|
||||
model = Meeting
|
||||
fields = ('group', 'in_person', 'meeting_type', 'approved', 'city', 'country', 'time_zone')
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(InterimRequestForm, self).__init__(*args, **kwargs)
|
||||
super(InterimMeetingModelForm, self).__init__(*args, **kwargs)
|
||||
self.user = request.user
|
||||
self.person = self.user.person
|
||||
self.fields["group"].widget.attrs["class"] = "select2-field"
|
||||
self.is_edit = bool(self.instance.pk)
|
||||
self.fields['group'].widget.attrs['class'] = "select2-field"
|
||||
self.fields['time_zone'].initial = 'UTC'
|
||||
self.set_group_options()
|
||||
if self.is_edit:
|
||||
self.fields['group'].initial = self.instance.session_set.first().group
|
||||
self.fields['group'].widget.attrs['disabled'] = True
|
||||
if self.instance.city or self.instance.country:
|
||||
self.fields['in_person'].initial = True
|
||||
if is_meeting_approved(self.instance):
|
||||
self.fields['approved'].initial = True
|
||||
|
||||
def set_group_options(self):
|
||||
'''Set group options based on user accessing the form'''
|
||||
|
@ -160,40 +174,132 @@ class InterimRequestForm(forms.Form):
|
|||
if has_role(self.user, "Secretariat"):
|
||||
return # don't reduce group options
|
||||
if has_role(self.user, "Area Director"):
|
||||
queryset = Group.objects.filter(type="wg", state__in=("active","proposed")).order_by('acronym')
|
||||
queryset = Group.objects.filter(type="wg", state__in=("active", "proposed")).order_by('acronym')
|
||||
elif has_role(self.user, "IRTF Chair"):
|
||||
queryset = Group.objects.filter(type="rg", state__in=("active","proposed")).order_by('acronym')
|
||||
queryset = Group.objects.filter(type="rg", state__in=("active", "proposed")).order_by('acronym')
|
||||
elif has_role(self.user, "WG Chair"):
|
||||
queryset = Group.objects.filter(type="wg", state__in=("active","proposed"), role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
queryset = Group.objects.filter(type="wg", state__in=("active", "proposed"), role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
elif has_role(self.user, "RG Chair"):
|
||||
queryset = Group.objects.filter(type="rg", state__in=("active","proposed"), role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
queryset = Group.objects.filter(type="rg", state__in=("active", "proposed"), role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
self.fields['group'].queryset = queryset
|
||||
|
||||
# if there's only one possibility make it the default
|
||||
if len(queryset) == 1:
|
||||
self.fields['group'].initial = queryset[0]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
'''Save must handle fields not included in the form: date,number,type_id'''
|
||||
date = kwargs.pop('date')
|
||||
group = self.cleaned_data.get('group')
|
||||
meeting = super(InterimMeetingModelForm, self).save(commit=False)
|
||||
if not meeting.type_id:
|
||||
meeting.type_id = 'interim'
|
||||
if not meeting.number:
|
||||
meeting.number = get_next_interim_number(group, date)
|
||||
meeting.date = date
|
||||
if kwargs.get('commit', True):
|
||||
# create schedule with meeting
|
||||
if not meeting.agenda:
|
||||
meeting.agenda = Schedule.objects.create(
|
||||
meeting=meeting,
|
||||
owner=Person.objects.get(name='(System)'))
|
||||
meeting.save()
|
||||
|
||||
return meeting
|
||||
|
||||
|
||||
class InterimSessionModelForm(forms.ModelForm):
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1"}, label='Date', required=False)
|
||||
time = forms.TimeField(widget=forms.TimeInput(format='%H:%M'), required=False)
|
||||
time_utc = forms.TimeField(required=False)
|
||||
requested_duration = DurationField(required=False)
|
||||
end_time = forms.TimeField(required=False)
|
||||
end_time_utc = forms.TimeField(required=False)
|
||||
remote_instructions = forms.CharField(max_length=1024, required=False)
|
||||
agenda = forms.CharField(required=False, widget=forms.Textarea)
|
||||
agenda_note = forms.CharField(max_length=255, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Session
|
||||
fields = ('date', 'time', 'time_utc', 'requested_duration', 'end_time',
|
||||
'end_time_utc', 'remote_instructions', 'agenda', 'agenda_note')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'user' in kwargs:
|
||||
self.user = kwargs.pop('user')
|
||||
if 'group' in kwargs:
|
||||
self.group = kwargs.pop('group')
|
||||
if 'is_approved' in kwargs:
|
||||
self.is_approved = kwargs.pop('is_approved')
|
||||
super(InterimSessionModelForm, self).__init__(*args, **kwargs)
|
||||
self.is_edit = bool(self.instance.pk)
|
||||
# setup fields that aren't intrinsic to the Session object
|
||||
if self.is_edit:
|
||||
self.initial['date'] = self.instance.official_timeslotassignment().timeslot.time
|
||||
self.initial['time'] = self.instance.official_timeslotassignment().timeslot.time
|
||||
if self.instance.agenda():
|
||||
doc = self.instance.agenda()
|
||||
path = os.path.join(doc.get_file_path(), doc.filename_with_rev())
|
||||
self.initial['agenda'] = get_document_content(os.path.basename(path), path, markup=False)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""NOTE: as the baseform of an inlineformset self.save(commit=True)
|
||||
never gets called"""
|
||||
session = super(InterimSessionModelForm, self).save(commit=kwargs.get('commit', True))
|
||||
if self.is_approved:
|
||||
session.status_id = 'scheda'
|
||||
else:
|
||||
session.status_id = 'apprw'
|
||||
session.group = self.group
|
||||
session.type_id = 'session'
|
||||
if not self.instance.pk:
|
||||
session.requested_by = self.user.person
|
||||
|
||||
return session
|
||||
|
||||
def save_agenda(self):
|
||||
if self.instance.agenda():
|
||||
doc = self.instance.agenda()
|
||||
doc.rev = str(int(doc.rev) + 1).zfill(2)
|
||||
doc.save()
|
||||
else:
|
||||
filename = 'agenda-interim-{group}-{date}-{time}'.format(
|
||||
group=self.group.acronym,
|
||||
date=self.cleaned_data['date'].strftime("%Y-%m-%d-"),
|
||||
time=self.cleaned_data['time'].strftime("%H%M"))
|
||||
doc = Document.objects.create(
|
||||
type_id='agenda',
|
||||
group=self.group,
|
||||
name=filename,
|
||||
rev='00')
|
||||
doc.set_state(State.objects.get(type=doc.type, slug='active'))
|
||||
DocAlias.objects.create(name=doc.name, document=doc)
|
||||
self.instance.sessionpresentation_set.create(document=doc, rev=doc.rev)
|
||||
NewRevisionDocEvent.objects.create(
|
||||
type='new_revision',
|
||||
by=self.user.person,
|
||||
doc=doc,
|
||||
rev=doc.rev,
|
||||
desc='New revision available')
|
||||
# write file
|
||||
path = os.path.join(get_upload_root(self.instance.meeting), 'agenda', doc.filename_with_rev())
|
||||
directory = os.path.dirname(path)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
with open(path, "w") as file:
|
||||
file.write(self.cleaned_data['agenda'])
|
||||
|
||||
|
||||
class InterimSessionForm(forms.Form):
|
||||
# unset: date,time,duration
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Date', required=False)
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1"}, label='Date', required=False)
|
||||
time = forms.TimeField(required=False)
|
||||
time_utc = forms.TimeField(required=False)
|
||||
duration = DurationField(required=False)
|
||||
end_time = forms.TimeField(required=False)
|
||||
end_time_utc = forms.TimeField(required=False)
|
||||
remote_instructions = forms.CharField(max_length=1024,required=False)
|
||||
agenda = forms.CharField(required=False,widget=forms.Textarea)
|
||||
agenda_note = forms.CharField(max_length=255,required=False)
|
||||
city = forms.CharField(max_length=255,required=False)
|
||||
country = forms.ChoiceField(choices=countries,required=False)
|
||||
timezone = forms.ChoiceField(choices=timezones)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InterimSessionForm, self).__init__(*args, **kwargs)
|
||||
self.fields['timezone'].initial = 'UTC'
|
||||
|
||||
def _save_agenda(self, session):
|
||||
pass
|
||||
remote_instructions = forms.CharField(max_length=1024, required=False)
|
||||
agenda = forms.CharField(required=False, widget=forms.Textarea)
|
||||
agenda_note = forms.CharField(max_length=255, required=False)
|
||||
|
||||
def save(self, request, group, meeting, is_approved):
|
||||
person = request.user.person
|
||||
|
@ -208,7 +314,8 @@ class InterimSessionForm(forms.Form):
|
|||
status_id = 'scheda'
|
||||
else:
|
||||
status_id = 'apprw'
|
||||
session = Session.objects.create(meeting=meeting,
|
||||
session = Session.objects.create(
|
||||
meeting=meeting,
|
||||
group=group,
|
||||
requested_by=person,
|
||||
requested_duration=duration,
|
||||
|
@ -216,22 +323,23 @@ class InterimSessionForm(forms.Form):
|
|||
type_id='session',
|
||||
remote_instructions=remote_instructions,
|
||||
agenda_note=agenda_note,)
|
||||
assign_interim_session(session,time)
|
||||
|
||||
assign_interim_session(session, time)
|
||||
|
||||
if agenda:
|
||||
# create objects
|
||||
filename = 'agenda-interim-%s-%s' % (group.acronym,time.strftime("%Y-%m-%d-%H%M"))
|
||||
doc = Document.objects.create(type_id='agenda',group=group,name=filename,rev='00')
|
||||
doc.set_state(State.objects.get(type=doc.type,slug='active'))
|
||||
filename = 'agenda-interim-%s-%s' % (group.acronym, time.strftime("%Y-%m-%d-%H%M"))
|
||||
doc = Document.objects.create(type_id='agenda', group=group, name=filename, rev='00')
|
||||
doc.set_state(State.objects.get(type=doc.type, slug='active'))
|
||||
DocAlias.objects.create(name=doc.name, document=doc)
|
||||
session.sessionpresentation_set.create(document=doc,rev=doc.rev)
|
||||
NewRevisionDocEvent.objects.create(type='new_revision',
|
||||
session.sessionpresentation_set.create(document=doc, rev=doc.rev)
|
||||
NewRevisionDocEvent.objects.create(
|
||||
type='new_revision',
|
||||
by=request.user.person,
|
||||
doc=doc,
|
||||
rev=doc.rev,
|
||||
desc='New revision available')
|
||||
# write file
|
||||
path = os.path.join(get_upload_root(meeting),'agenda',doc.filename_with_rev())
|
||||
path = os.path.join(get_upload_root(meeting), 'agenda', doc.filename_with_rev())
|
||||
directory = os.path.dirname(path)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
@ -240,19 +348,17 @@ class InterimSessionForm(forms.Form):
|
|||
|
||||
return session
|
||||
|
||||
|
||||
class InterimAnnounceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ('to','frm','cc','bcc','reply_to','subject','body')
|
||||
fields = ('to', 'frm', 'cc', 'bcc', 'reply_to', 'subject', 'body')
|
||||
|
||||
#def __init__(self):
|
||||
# super(InterimAnnounceForm, self).__init__(*args,**kwargs)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
message = super(InterimAnnounceForm, self).save(commit=False)
|
||||
message.by = user.person
|
||||
message.save()
|
||||
|
||||
return message
|
||||
return message
|
||||
|
|
|
@ -16,6 +16,7 @@ from django.template.loader import render_to_string
|
|||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.models import Document
|
||||
from ietf.doc.utils import get_document_content
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role, user_is_person
|
||||
from ietf.liaisons.utils import get_person_for_user
|
||||
|
@ -290,67 +291,31 @@ def session_constraint_expire(request,session):
|
|||
# -------------------------------------------------
|
||||
# Interim Meeting Helpers
|
||||
# -------------------------------------------------
|
||||
def get_announcement_initial(meeting):
|
||||
'''Returns a dictionary suitable to initialize an InterimAnnouncementForm (Message ModelForm)'''
|
||||
group = meeting.session_set.first().group
|
||||
in_person = bool(meeting.city)
|
||||
initial = {}
|
||||
initial['to'] = settings.INTERIM_ANNOUNCE_TO_EMAIL
|
||||
initial['cc'] = group.list_email
|
||||
initial['frm'] = settings.INTERIM_ANNOUNCE_FROM_EMAIL
|
||||
if in_person:
|
||||
desc = 'Interim'
|
||||
|
||||
|
||||
def assign_interim_session(form):
|
||||
"""Helper function to create a timeslot and assign the interim session"""
|
||||
time = datetime.datetime.combine(
|
||||
form.cleaned_data['date'],
|
||||
form.cleaned_data['time'])
|
||||
session = form.instance
|
||||
if session.official_timeslotassignment():
|
||||
slot = session.official_timeslotassignment().timeslot
|
||||
slot.time = time
|
||||
slot.save()
|
||||
else:
|
||||
desc = 'Virtual'
|
||||
initial['subject'] = '%s (%s) WG %s Meeting: %s' % (group.name,group.acronym,desc,meeting.date)
|
||||
body = render_to_string('meeting/interim_announcement.txt', locals())
|
||||
initial['body'] = body
|
||||
return initial
|
||||
slot = TimeSlot.objects.create(
|
||||
meeting=session.meeting,
|
||||
type_id="session",
|
||||
duration=session.requested_duration,
|
||||
time=time)
|
||||
SchedTimeSessAssignment.objects.create(
|
||||
timeslot=slot,
|
||||
session=session,
|
||||
schedule=session.meeting.agenda)
|
||||
|
||||
def get_earliest_session(session_formset):
|
||||
'''Return earliest InterimSessionForm from formset'''
|
||||
earliest = session_formset[0]
|
||||
if len(session_formset) == 1:
|
||||
return earliest
|
||||
for form in session_formset[1:]:
|
||||
date = form.cleaned_data.get('date')
|
||||
if date and date < earliest.cleaned_data.get('date'):
|
||||
earliest = form
|
||||
return earliest
|
||||
|
||||
def get_next_interim_number(group,date):
|
||||
sequence = Meeting.objects.filter(number__startswith='interim-%s-%s' % (date.year,group.acronym)).count() + 1
|
||||
return 'interim-%s-%s-%s' % (date.year,group.acronym,sequence)
|
||||
|
||||
def create_interim_meeting(group,date,city='',country='',timezone='UTC',person=None):
|
||||
'''Helper function to create interim meeting and associated schedule'''
|
||||
if not person:
|
||||
person = Person.objects.get(name="(System)")
|
||||
number = get_next_interim_number(group,date)
|
||||
meeting = Meeting.objects.create(number=number,type_id='interim',date=date,city=city,
|
||||
country=country,time_zone=timezone)
|
||||
schedule = Schedule.objects.create(meeting=meeting, owner=person, visible=True, public=True)
|
||||
meeting.agenda = schedule
|
||||
meeting.save()
|
||||
return meeting
|
||||
|
||||
def create_interim_meeting_from_forms(request_form,session_form):
|
||||
'''Create an Interim meeting, given an InterimRequestForm and InterimSessionForm'''
|
||||
group = request_form.cleaned_data.get('group')
|
||||
date = session_form.cleaned_data.get('date')
|
||||
city = session_form.cleaned_data.get('city')
|
||||
country = session_form.cleaned_data.get('country')
|
||||
timezone = session_form.cleaned_data.get('timezone')
|
||||
person = request_form.person
|
||||
return create_interim_meeting(group=group,date=date,city=city,country=country,timezone=timezone,person=person)
|
||||
|
||||
def assign_interim_session(session,time):
|
||||
'''Helper function to create a timeslot and assign the interim session'''
|
||||
slot = TimeSlot.objects.create(meeting=session.meeting, type_id="session",
|
||||
duration=session.requested_duration, time=time)
|
||||
SchedTimeSessAssignment.objects.create(timeslot=slot, session=session, schedule=session.meeting.agenda)
|
||||
|
||||
def can_approve_interim_request(meeting,user):
|
||||
def can_approve_interim_request(meeting, user):
|
||||
'''Returns True if the user has permissions to approve an interim meeting request'''
|
||||
if meeting.type.slug != 'interim':
|
||||
return False
|
||||
|
@ -361,26 +326,29 @@ def can_approve_interim_request(meeting,user):
|
|||
if not session:
|
||||
return False
|
||||
group = session.group
|
||||
if group.type.slug == 'wg' and group.parent.role_set.filter(name='ad',person=person):
|
||||
if group.type.slug == 'wg' and group.parent.role_set.filter(name='ad', person=person):
|
||||
return True
|
||||
if group.type.slug == 'rg' and group.parent.role_set.filter(name='chair',person=person):
|
||||
if group.type.slug == 'rg' and group.parent.role_set.filter(name='chair', person=person):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_edit_interim_request(meeting,user):
|
||||
|
||||
def can_edit_interim_request(meeting, user):
|
||||
'''Returns True if the user can edit the interim meeting request'''
|
||||
|
||||
if can_approve_interim_request(meeting,user):
|
||||
|
||||
if can_approve_interim_request(meeting, user):
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def can_request_interim_meeting(user):
|
||||
if has_role(user, ('Secretariat','Area Director','WG Chair','IRTF Chair', 'RG Chair')):
|
||||
if has_role(user, ('Secretariat', 'Area Director', 'WG Chair', 'IRTF Chair', 'RG Chair')):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_view_interim_request(meeting,user):
|
||||
|
||||
def can_view_interim_request(meeting, user):
|
||||
'''Returns True if the user can see the pending interim request in the pending interim view'''
|
||||
if meeting.type.slug != 'interim':
|
||||
return False
|
||||
|
@ -395,6 +363,129 @@ def can_view_interim_request(meeting,user):
|
|||
return True
|
||||
if has_role(user, 'IRTF Chair') and group.type.slug == 'rg':
|
||||
return True
|
||||
if group.role_set.filter(name='chair',person=person):
|
||||
if group.role_set.filter(name='chair', person=person):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def create_interim_meeting(group, date, city='', country='', timezone='UTC',
|
||||
person=None):
|
||||
"""Helper function to create interim meeting and associated schedule"""
|
||||
if not person:
|
||||
person = Person.objects.get(name='(System)')
|
||||
number = get_next_interim_number(group, date)
|
||||
meeting = Meeting.objects.create(
|
||||
number=number,
|
||||
type_id='interim',
|
||||
date=date,
|
||||
city=city,
|
||||
country=country,
|
||||
time_zone=timezone)
|
||||
schedule = Schedule.objects.create(
|
||||
meeting=meeting,
|
||||
owner=person,
|
||||
visible=True,
|
||||
public=True)
|
||||
meeting.agenda = schedule
|
||||
meeting.save()
|
||||
return meeting
|
||||
|
||||
|
||||
def get_announcement_initial(meeting):
|
||||
'''Returns a dictionary suitable to initialize an InterimAnnouncementForm (Message ModelForm)'''
|
||||
group = meeting.session_set.first().group
|
||||
in_person = bool(meeting.city)
|
||||
initial = {}
|
||||
initial['to'] = settings.INTERIM_ANNOUNCE_TO_EMAIL
|
||||
initial['cc'] = group.list_email
|
||||
initial['frm'] = settings.INTERIM_ANNOUNCE_FROM_EMAIL
|
||||
if in_person:
|
||||
desc = 'Interim'
|
||||
else:
|
||||
desc = 'Virtual'
|
||||
initial['subject'] = '%s (%s) WG %s Meeting: %s' % (group.name, group.acronym, desc, meeting.date)
|
||||
body = render_to_string('meeting/interim_announcement.txt', locals())
|
||||
initial['body'] = body
|
||||
return initial
|
||||
|
||||
|
||||
def get_earliest_session_date(formset):
|
||||
'''Return earliest date from InterimSession Formset'''
|
||||
return sorted([f.cleaned_data['date'] for f in formset.forms if f.cleaned_data.get('date')])[0]
|
||||
|
||||
|
||||
def get_interim_initial(meeting):
|
||||
'''Returns a dictionary suitable to initialize a InterimRequestForm'''
|
||||
initial = {}
|
||||
initial['group'] = meeting.session_set.first().group
|
||||
if meeting.city:
|
||||
initial['in_person'] = True
|
||||
else:
|
||||
initial['in_person'] = False
|
||||
if meeting.session_set.count() > 1:
|
||||
initial['meeting_type'] = 'multi-day'
|
||||
else:
|
||||
initial['meeting_type'] = 'single'
|
||||
if meeting.session_set.first().status.slug == 'apprw':
|
||||
initial['approved'] = False
|
||||
else:
|
||||
initial['approved'] = True
|
||||
return initial
|
||||
|
||||
|
||||
def get_interim_session_initial(meeting):
|
||||
'''Returns a list of dictionaries suitable to initialize a InterimSessionForm'''
|
||||
initials = []
|
||||
for session in meeting.session_set.all():
|
||||
initial = {}
|
||||
initial['date'] = session.official_timeslotassignment().timeslot.time
|
||||
initial['time'] = session.official_timeslotassignment().timeslot.time
|
||||
initial['duration'] = session.requested_duration
|
||||
initial['remote_instructions'] = session.remote_instructions
|
||||
initial['agenda_note'] = session.agenda_note
|
||||
doc = session.agenda()
|
||||
if doc:
|
||||
path = os.path.join(doc.get_file_path(), doc.filename_with_rev())
|
||||
initial['agenda'] = get_document_content(os.path.basename(path), path, markup=False)
|
||||
initials.append(initial)
|
||||
|
||||
return initials
|
||||
|
||||
|
||||
def is_meeting_approved(meeting):
|
||||
"""Returns True if the meeting is approved"""
|
||||
if meeting.session_set.first().status.slug == 'apprw':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_next_interim_number(group, date):
|
||||
"""Returns a unique number to use for the next interim meeting for
|
||||
*group*"""
|
||||
meetings = Meeting.objects.filter(
|
||||
number__startswith='interim-{year}-{group}'.format(
|
||||
year=date.year,
|
||||
group=group.acronym))
|
||||
if meetings:
|
||||
sequences = [int(m.number.split('-')[-1]) for m in meetings]
|
||||
last_sequence = sorted(sequences)[-1]
|
||||
else:
|
||||
last_sequence = 0
|
||||
return 'interim-{year}-{group}-{sequence}'.format(
|
||||
year=date.year,
|
||||
group=group.acronym,
|
||||
sequence=last_sequence + 1)
|
||||
|
||||
|
||||
def sessions_post_save(forms):
|
||||
"""Helper function to perform various post save operations on each form of a
|
||||
InterimSessionModelForm formset"""
|
||||
|
||||
for form in forms:
|
||||
if not form.has_changed():
|
||||
continue
|
||||
if ('date' in form.changed_data) or ('time' in form.changed_data):
|
||||
assign_interim_session(form)
|
||||
if 'agenda' in form.changed_data:
|
||||
form.save_agenda()
|
||||
|
|
|
@ -995,7 +995,7 @@ class Session(models.Model):
|
|||
return Constraint.objects.filter(target=self.group, meeting=self.meeting).order_by('name__name')
|
||||
|
||||
def timeslotassignment_for_agenda(self, schedule):
|
||||
return self.timeslotassignments.filter(schedule=schedule)[0]
|
||||
return self.timeslotassignments.filter(schedule=schedule).first()
|
||||
|
||||
def official_timeslotassignment(self):
|
||||
return self.timeslotassignment_for_agenda(self.meeting.agenda)
|
||||
|
|
|
@ -16,7 +16,16 @@ def make_interim_meeting(group,date,status='sched'):
|
|||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id=status,
|
||||
scheduled=datetime.datetime.now(),type_id="session")
|
||||
assign_interim_session(session,time)
|
||||
#assign_interim_session(session,time)
|
||||
slot = TimeSlot.objects.create(
|
||||
meeting=meeting,
|
||||
type_id="session",
|
||||
duration=session.requested_duration,
|
||||
time=time)
|
||||
SchedTimeSessAssignment.objects.create(
|
||||
timeslot=slot,
|
||||
session=session,
|
||||
schedule=session.meeting.agenda)
|
||||
return meeting
|
||||
|
||||
def make_meeting_test_data():
|
||||
|
|
|
@ -6,6 +6,7 @@ import urlparse
|
|||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import transaction
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
|
@ -468,6 +469,46 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(Group.objects.filter(type__in=('wg','rg'),state='active').count(),
|
||||
len(q("#id_group option")) -1 ) # -1 for options placeholder
|
||||
|
||||
def test_temp(self):
|
||||
from django.forms.models import modelform_factory, inlineformset_factory
|
||||
from ietf.meeting.forms import InterimSessionModelForm
|
||||
from django.utils.functional import curry
|
||||
|
||||
make_meeting_test_data()
|
||||
group = Group.objects.get(acronym='mars')
|
||||
date = datetime.date.today() + datetime.timedelta(days=30)
|
||||
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
|
||||
dt = datetime.datetime.combine(date, time)
|
||||
duration = datetime.timedelta(hours=3)
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'single',
|
||||
'city':'',
|
||||
'country':'',
|
||||
'time_zone':'UTC',
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':1,
|
||||
'session_set-INITIAL_FORMS':0,
|
||||
'session_set-MIN_NUM_FORMS':0,
|
||||
'session_set-MAX_NUM_FORMS':1000}
|
||||
|
||||
user = User.objects.get(username='secretary')
|
||||
is_approved = False
|
||||
meeting = Meeting.objects.order_by('id').last()
|
||||
SessionFormset = inlineformset_factory(Meeting, Session, form=InterimSessionModelForm, can_delete=False, extra=2)
|
||||
SessionFormset.form = staticmethod(curry(InterimSessionModelForm, user=user,group=group,is_approved=is_approved))
|
||||
formset = SessionFormset(instance=meeting, data=data)
|
||||
#assert False, (formset.management_form)
|
||||
formset.save()
|
||||
|
||||
def test_interim_request_single(self):
|
||||
make_meeting_test_data()
|
||||
group = Group.objects.get(acronym='mars')
|
||||
|
@ -481,17 +522,19 @@ class InterimTests(TestCase):
|
|||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'single',
|
||||
'form-0-date':date.strftime("%Y-%m-%d"),
|
||||
'form-0-time':time.strftime('%H:%M'),
|
||||
'form-0-duration':'03:00:00',
|
||||
'form-0-city':'',
|
||||
'form-0-country':'',
|
||||
'form-0-timezone':'UTC',
|
||||
'form-0-remote_instructions':remote_instructions,
|
||||
'form-0-agenda':agenda,
|
||||
'form-0-agenda_note':agenda_note,
|
||||
'form-TOTAL_FORMS':1,
|
||||
'form-INITIAL_FORMS':0}
|
||||
'city':'',
|
||||
'country':'',
|
||||
'time_zone':'UTC',
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':1,
|
||||
'session_set-INITIAL_FORMS':0,
|
||||
'session_set-MIN_NUM_FORMS':0,
|
||||
'session_set-MAX_NUM_FORMS':1000}
|
||||
|
||||
r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data)
|
||||
|
||||
|
@ -524,24 +567,24 @@ class InterimTests(TestCase):
|
|||
duration = datetime.timedelta(hours=3)
|
||||
city = 'San Francisco'
|
||||
country = 'US'
|
||||
timezone = 'US/Pacific'
|
||||
time_zone = 'US/Pacific'
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'single',
|
||||
'form-0-date':date.strftime("%Y-%m-%d"),
|
||||
'form-0-time':time.strftime('%H:%M'),
|
||||
'form-0-duration':'03:00:00',
|
||||
'form-0-city':city,
|
||||
'form-0-country':country,
|
||||
'form-0-timezone':timezone,
|
||||
'form-0-remote_instructions':remote_instructions,
|
||||
'form-0-agenda':agenda,
|
||||
'form-0-agenda_note':agenda_note,
|
||||
'form-TOTAL_FORMS':1,
|
||||
'form-INITIAL_FORMS':0}
|
||||
'city':city,
|
||||
'country':country,
|
||||
'time_zone':time_zone,
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':1,
|
||||
'session_set-INITIAL_FORMS':0}
|
||||
|
||||
r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data)
|
||||
|
||||
|
@ -552,7 +595,7 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,timezone)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
session = meeting.session_set.first()
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
|
@ -571,33 +614,30 @@ class InterimTests(TestCase):
|
|||
group = Group.objects.get(acronym='mars')
|
||||
city = 'San Francisco'
|
||||
country = 'US'
|
||||
timezone = 'US/Pacific'
|
||||
time_zone = 'US/Pacific'
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'multi-day',
|
||||
'form-0-date':date.strftime("%Y-%m-%d"),
|
||||
'form-0-time':time.strftime('%H:%M'),
|
||||
'form-0-duration':'03:00:00',
|
||||
'form-0-city':city,
|
||||
'form-0-country':country,
|
||||
'form-0-timezone':timezone,
|
||||
'form-0-remote_instructions':remote_instructions,
|
||||
'form-0-agenda':agenda,
|
||||
'form-0-agenda_note':agenda_note,
|
||||
'form-1-date':date2.strftime("%Y-%m-%d"),
|
||||
'form-1-time':time.strftime('%H:%M'),
|
||||
'form-1-duration':'03:00:00',
|
||||
'form-1-city':city,
|
||||
'form-1-country':country,
|
||||
'form-1-timezone':timezone,
|
||||
'form-1-remote_instructions':remote_instructions,
|
||||
'form-1-agenda':agenda,
|
||||
'form-1-agenda_note':agenda_note,
|
||||
'form-TOTAL_FORMS':2,
|
||||
'form-INITIAL_FORMS':0}
|
||||
'city':city,
|
||||
'country':country,
|
||||
'time_zone':time_zone,
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-1-date':date2.strftime("%Y-%m-%d"),
|
||||
'session_set-1-time':time.strftime('%H:%M'),
|
||||
'session_set-1-requested_duration':'03:00:00',
|
||||
'session_set-1-remote_instructions':remote_instructions,
|
||||
'session_set-1-agenda':agenda,
|
||||
'session_set-1-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':2,
|
||||
'session_set-INITIAL_FORMS':0}
|
||||
|
||||
r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data)
|
||||
|
||||
|
@ -608,7 +648,7 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,timezone)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
self.assertEqual(meeting.session_set.count(),2)
|
||||
# first sesstion
|
||||
session = meeting.session_set.all()[0]
|
||||
|
@ -625,6 +665,81 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(timeslot.duration,duration)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
|
||||
def test_interim_request_series(self):
|
||||
make_meeting_test_data()
|
||||
meeting_count_before = Meeting.objects.filter(type='interim').count()
|
||||
date = datetime.date.today() + datetime.timedelta(days=30)
|
||||
date2 = date + datetime.timedelta(days=1)
|
||||
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
|
||||
dt = datetime.datetime.combine(date, time)
|
||||
dt2 = datetime.datetime.combine(date2, time)
|
||||
duration = datetime.timedelta(hours=3)
|
||||
group = Group.objects.get(acronym='mars')
|
||||
city = ''
|
||||
country = ''
|
||||
time_zone = 'US/Pacific'
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'series',
|
||||
'city':city,
|
||||
'country':country,
|
||||
'time_zone':time_zone,
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-1-date':date2.strftime("%Y-%m-%d"),
|
||||
'session_set-1-time':time.strftime('%H:%M'),
|
||||
'session_set-1-requested_duration':'03:00:00',
|
||||
'session_set-1-remote_instructions':remote_instructions,
|
||||
'session_set-1-agenda':agenda,
|
||||
'session_set-1-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':2,
|
||||
'session_set-INITIAL_FORMS':0}
|
||||
|
||||
r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data)
|
||||
|
||||
self.assertRedirects(r,urlreverse('ietf.meeting.views.upcoming'))
|
||||
meeting_count_after = Meeting.objects.filter(type='interim').count()
|
||||
self.assertEqual(meeting_count_after,meeting_count_before + 2)
|
||||
meetings = Meeting.objects.order_by('-id')[:2]
|
||||
# first meeting
|
||||
meeting = meetings[1]
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
self.assertEqual(meeting.session_set.count(),1)
|
||||
session = meeting.session_set.first()
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
# second meeting
|
||||
meeting = meetings[0]
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date2)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,2))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
self.assertEqual(meeting.session_set.count(),1)
|
||||
session = meeting.session_set.first()
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt2)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
|
||||
|
||||
def test_interim_pending(self):
|
||||
make_meeting_test_data()
|
||||
url = urlreverse('ietf.meeting.views.interim_pending')
|
||||
|
@ -715,6 +830,14 @@ class InterimTests(TestCase):
|
|||
for session in meeting.session_set.all():
|
||||
self.assertEqual(session.status_id,'canceledpa')
|
||||
|
||||
def test_interim_request_edit(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
url = urlreverse('ietf.meeting.views.interim_request_edit',kwargs={'number':meeting.number})
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
def test_interim_request_details_permissions(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
|
|
|
@ -72,6 +72,7 @@ urlpatterns = [
|
|||
url(r'^interim/announce/(?P<number>[A-Za-z0-9._+-]+)/$', views.interim_send_announcement),
|
||||
url(r'^interim/request/$', views.interim_request),
|
||||
url(r'^interim/request/(?P<number>[A-Za-z0-9._+-]+)/$', views.interim_request_details),
|
||||
url(r'^interim/request/(?P<number>[A-Za-z0-9._+-]+)/edit/$', views.interim_request_edit),
|
||||
url(r'^interim/pending/$', views.interim_pending),
|
||||
url(r'^$', views.current_materials),
|
||||
]
|
||||
|
|
|
@ -20,8 +20,9 @@ from django.contrib import messages
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import Min, Max
|
||||
from django.conf import settings
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms.models import modelform_factory, inlineformset_factory
|
||||
from django.forms import ModelForm, formset_factory
|
||||
from django.utils.functional import curry
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
|
||||
from ietf.doc.models import Document, State
|
||||
|
@ -36,15 +37,16 @@ from ietf.meeting.helpers import get_modified_from_assignments
|
|||
from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting
|
||||
from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, get_meetings
|
||||
from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file
|
||||
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session
|
||||
from ietf.meeting.helpers import create_interim_meeting_from_forms
|
||||
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session_date
|
||||
from ietf.meeting.helpers import can_view_interim_request, can_approve_interim_request
|
||||
from ietf.meeting.helpers import can_request_interim_meeting, get_announcement_initial
|
||||
from ietf.meeting.helpers import get_interim_initial, get_interim_session_initial
|
||||
from ietf.meeting.helpers import sessions_post_save, is_meeting_approved
|
||||
from ietf.utils.mail import send_mail_message
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.pdf import pdf_pages
|
||||
|
||||
from .forms import InterimRequestForm, InterimSessionForm, InterimAnnounceForm
|
||||
from .forms import InterimMeetingModelForm, InterimSessionForm, InterimAnnounceForm, InterimSessionModelForm
|
||||
|
||||
|
||||
def get_menu_entries(request):
|
||||
|
@ -901,45 +903,49 @@ def session_details(request, num, acronym ):
|
|||
# Interim Views
|
||||
# -------------------------------------------------
|
||||
|
||||
|
||||
def ajax_get_utc(request):
|
||||
'''Ajax view that takes arguments time and timezone and returns UTC'''
|
||||
time = request.GET.get('time')
|
||||
timezone = request.GET.get('timezone')
|
||||
hour,minute = time.split(':')
|
||||
dt = datetime.datetime(2016,1,1,int(hour),int(minute))
|
||||
hour, minute = time.split(':')
|
||||
dt = datetime.datetime(2016, 1, 1, int(hour), int(minute))
|
||||
tz = pytz.timezone(timezone)
|
||||
aware_dt = tz.localize(dt, is_dst=None)
|
||||
utc_dt = aware_dt.astimezone(pytz.utc)
|
||||
utc = utc_dt.strftime('%H:%M')
|
||||
context_data = {'timezone':timezone,'time':time,'utc':utc}
|
||||
return HttpResponse(json.dumps(context_data),content_type='application/json')
|
||||
context_data = {'timezone': timezone, 'time': time, 'utc': utc}
|
||||
return HttpResponse(json.dumps(context_data),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def interim_announce(request):
|
||||
'''View which shows interim meeting requests awaiting announcement'''
|
||||
meetings = Meeting.objects.filter(type='interim',session__status='scheda')
|
||||
meetings = Meeting.objects.filter(type='interim', session__status='scheda')
|
||||
menu_entries = get_menu_entries(request)
|
||||
selected_menu_entry = 'announce'
|
||||
|
||||
return render(request, "meeting/interim_announce.html", {
|
||||
'menu_entries': menu_entries,
|
||||
'selected_menu_entry': selected_menu_entry,
|
||||
'meetings' :meetings})
|
||||
'meetings': meetings})
|
||||
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def interim_send_announcement(request,number):
|
||||
def interim_send_announcement(request, number):
|
||||
'''View for sending the announcement of a new interim meeting'''
|
||||
meeting = get_object_or_404(Meeting,number=number)
|
||||
meeting = get_object_or_404(Meeting, number=number)
|
||||
group = meeting.session_set.first().group
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
form = InterimAnnounceForm(request.POST, initial=get_announcement_initial(meeting))
|
||||
form = InterimAnnounceForm(request.POST,
|
||||
initial=get_announcement_initial(meeting))
|
||||
if form.is_valid():
|
||||
message = form.save(user=request.user)
|
||||
message.related_groups.add(group)
|
||||
meeting.session_set.update(status_id='sched')
|
||||
send_mail_message(request,message)
|
||||
send_mail_message(request, message)
|
||||
messages.success(request, 'Interim meeting announcement sent')
|
||||
return redirect(interim_announce)
|
||||
|
||||
|
@ -947,91 +953,178 @@ def interim_send_announcement(request,number):
|
|||
|
||||
return render(request, "meeting/interim_send_announcement.html", {
|
||||
'meeting': meeting,
|
||||
'form': form})
|
||||
'form': form})
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair','RG Chair')
|
||||
|
||||
@role_required('Area Director', 'Secretariat', 'IRTF Chair', 'WG Chair',
|
||||
'RG Chair')
|
||||
def interim_pending(request):
|
||||
'''View which shows interim meeting requests pending approval'''
|
||||
meetings = Meeting.objects.filter(type='interim',session__status='apprw')
|
||||
meetings = Meeting.objects.filter(type='interim', session__status='apprw')
|
||||
menu_entries = get_menu_entries(request)
|
||||
selected_menu_entry = 'pending'
|
||||
|
||||
meetings = [ m for m in meetings if can_view_interim_request(m,request.user)]
|
||||
|
||||
meetings = [m for m in meetings if can_view_interim_request(
|
||||
m, request.user)]
|
||||
for meeting in meetings:
|
||||
if can_approve_interim_request(meeting,request.user):
|
||||
if can_approve_interim_request(meeting, request.user):
|
||||
meeting.can_approve = True
|
||||
|
||||
|
||||
return render(request, "meeting/interim_pending.html", {
|
||||
'menu_entries': menu_entries,
|
||||
'selected_menu_entry':selected_menu_entry,
|
||||
'selected_menu_entry': selected_menu_entry,
|
||||
'meetings': meetings})
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair', 'RG Chair')
|
||||
|
||||
|
||||
@role_required('Area Director', 'Secretariat', 'IRTF Chair', 'WG Chair',
|
||||
'RG Chair')
|
||||
def interim_request(request):
|
||||
'''View for requesting an interim meeting'''
|
||||
SessionFormset = formset_factory(InterimSessionForm, extra=2)
|
||||
|
||||
SessionFormset = inlineformset_factory(
|
||||
Meeting,
|
||||
Session,
|
||||
form=InterimSessionModelForm,
|
||||
can_delete=False, extra=2)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = InterimRequestForm(request, data=request.POST)
|
||||
formset = SessionFormset(data=request.POST)
|
||||
#person = request.user.person
|
||||
form = InterimMeetingModelForm(request, data=request.POST)
|
||||
formset = SessionFormset(instance=Meeting(), data=request.POST)
|
||||
if form.is_valid() and formset.is_valid():
|
||||
group = form.cleaned_data.get('group')
|
||||
is_approved = form.cleaned_data.get('approved', False)
|
||||
meeting_type = form.cleaned_data.get('meeting_type')
|
||||
|
||||
|
||||
# pre create meeting
|
||||
if meeting_type in ('single','multi-day'):
|
||||
meeting = create_interim_meeting_from_forms(
|
||||
request_form=form,
|
||||
session_form=get_earliest_session(formset))
|
||||
|
||||
for f in formset.forms:
|
||||
if not f.has_changed():
|
||||
continue
|
||||
if meeting_type == 'series':
|
||||
meeting = create_interim_meeting_from_forms(form,f)
|
||||
f.save(request,group,meeting,is_approved)
|
||||
messages.success(request,'Interim meeting request submitted')
|
||||
if meeting_type in ('single', 'multi-day'):
|
||||
meeting = form.save(date=get_earliest_session_date(formset))
|
||||
|
||||
# need to use curry here to pass custom variable to form init
|
||||
SessionFormset.form = staticmethod(curry(
|
||||
InterimSessionModelForm,
|
||||
user=request.user,
|
||||
group=group,
|
||||
is_approved=is_approved))
|
||||
formset = SessionFormset(instance=meeting, data=request.POST)
|
||||
formset.is_valid()
|
||||
formset.save()
|
||||
|
||||
# post save
|
||||
sessions_post_save(formset)
|
||||
|
||||
# series require special handling, each session gets it's own
|
||||
# meeting object we won't see this on edit because series are
|
||||
# subsequently dealt with individually
|
||||
elif meeting_type == 'series':
|
||||
SessionFormset.form = staticmethod(curry(
|
||||
InterimSessionModelForm,
|
||||
user=request.user,
|
||||
group=group,
|
||||
is_approved=is_approved))
|
||||
formset = SessionFormset(instance=Meeting(), data=request.POST)
|
||||
formset.is_valid() # re-validate
|
||||
for session_form in formset.forms:
|
||||
if not session_form.has_changed():
|
||||
continue
|
||||
# create meeting
|
||||
form = InterimMeetingModelForm(request, data=request.POST)
|
||||
form.is_valid()
|
||||
meeting = form.save(date=session_form.cleaned_data['date'])
|
||||
# create save session
|
||||
session = session_form.save(commit=False)
|
||||
session.meeting = meeting
|
||||
session.save()
|
||||
|
||||
# post save
|
||||
sessions_post_save([session_form])
|
||||
|
||||
messages.success(request, 'Interim meeting request submitted')
|
||||
return redirect(upcoming)
|
||||
else:
|
||||
assert False, (form.errors, formset.errors)
|
||||
else:
|
||||
form = InterimRequestForm(request=request,initial={'meeting_type':'single','timezone':'UTC'})
|
||||
form = InterimMeetingModelForm(request=request,
|
||||
initial={'meeting_type': 'single'})
|
||||
formset = SessionFormset()
|
||||
|
||||
return render(request, "meeting/interim_request.html", {"form":form, "formset":formset})
|
||||
return render(request, "meeting/interim_request.html", {
|
||||
"form": form,
|
||||
"formset": formset})
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair', 'RG Chair')
|
||||
|
||||
@role_required('Area Director', 'Secretariat', 'IRTF Chair', 'WG Chair',
|
||||
'RG Chair')
|
||||
def interim_request_details(request, number):
|
||||
'''View details of an interim meeting reqeust'''
|
||||
meeting = get_object_or_404(Meeting,number=number)
|
||||
meeting = get_object_or_404(Meeting, number=number)
|
||||
sessions = meeting.session_set.all()
|
||||
can_edit = can_view_interim_request(meeting,request.user)
|
||||
can_approve = can_approve_interim_request(meeting,request.user)
|
||||
can_edit = can_view_interim_request(meeting, request.user)
|
||||
can_approve = can_approve_interim_request(meeting, request.user)
|
||||
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('approve'):
|
||||
meeting.session_set.update(status_id='scheda')
|
||||
messages.success(request,'Interim meeting approved')
|
||||
messages.success(request, 'Interim meeting approved')
|
||||
if has_role(request.user, 'Secretariat'):
|
||||
return redirect(interim_send_announcement, number=number)
|
||||
if request.POST.get('disapprove'):
|
||||
meeting.session_set.update(status_id='disappr')
|
||||
messages.success(request,'Interim meeting disapproved')
|
||||
messages.success(request, 'Interim meeting disapproved')
|
||||
if request.POST.get('cancel'):
|
||||
if meeting.session_set.first().status.slug == 'sched':
|
||||
meeting.session_set.update(status_id='canceled')
|
||||
else:
|
||||
meeting.session_set.update(status_id='canceledpa')
|
||||
messages.success(request,'Interim meeting cancelled')
|
||||
messages.success(request, 'Interim meeting cancelled')
|
||||
|
||||
return render(request, "meeting/interim_request_details.html", {
|
||||
"meeting": meeting,
|
||||
"sessions": sessions,
|
||||
"can_edit": can_edit,
|
||||
"can_approve": can_approve})
|
||||
|
||||
|
||||
@role_required('Area Director', 'Secretariat', 'IRTF Chair', 'WG Chair',
|
||||
'RG Chair')
|
||||
def interim_request_edit(request, number):
|
||||
'''Edit details of an interim meeting reqeust'''
|
||||
meeting = get_object_or_404(Meeting, number=number)
|
||||
SessionFormset = inlineformset_factory(
|
||||
Meeting,
|
||||
Session,
|
||||
form=InterimSessionModelForm,
|
||||
can_delete=False,
|
||||
extra=1)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = InterimMeetingModelForm(request=request, instance=meeting,
|
||||
data=request.POST)
|
||||
group = Group.objects.get(pk=form.data['group'])
|
||||
is_approved = is_meeting_approved(meeting)
|
||||
SessionFormset.form = staticmethod(curry(
|
||||
InterimSessionModelForm,
|
||||
user=request.user,
|
||||
group=group,
|
||||
is_approved=is_approved))
|
||||
formset = SessionFormset(instance=meeting,
|
||||
data=request.POST)
|
||||
if form.is_valid() and formset.is_valid():
|
||||
meeting = form.save(date=get_earliest_session_date(formset))
|
||||
formset.save()
|
||||
sessions_post_save(formset)
|
||||
|
||||
messages.success(request, 'Interim meeting request saved')
|
||||
return redirect(interim_request_details, number=number)
|
||||
else:
|
||||
assert False, (form.errors, formset.errors)
|
||||
else:
|
||||
form = InterimMeetingModelForm(request=request, instance=meeting)
|
||||
formset = SessionFormset(instance=meeting)
|
||||
|
||||
return render(request, "meeting/interim_request_edit.html", {
|
||||
"meeting": meeting,
|
||||
"form": form,
|
||||
"formset": formset})
|
||||
|
||||
return render(request, "meeting/interim_request_details.html",{
|
||||
"meeting":meeting,
|
||||
"sessions":sessions,
|
||||
"can_edit":can_edit,
|
||||
"can_approve":can_approve,
|
||||
})
|
||||
|
||||
def ical_upcoming(request):
|
||||
'''ICAL upcoming meetings'''
|
||||
|
@ -1042,14 +1135,18 @@ def ical_upcoming(request):
|
|||
"meetings": meetings,
|
||||
}, content_type="text/calendar")
|
||||
|
||||
|
||||
def upcoming(request):
|
||||
'''List of upcoming meetings'''
|
||||
today = datetime.datetime.today()
|
||||
meetings = Meeting.objects.filter(date__gte=today,session__status__in=('sched','canceled')).order_by('date')
|
||||
meetings = Meeting.objects.filter(
|
||||
date__gte=today,
|
||||
session__status__in=('sched', 'canceled')).order_by('date')
|
||||
|
||||
# extract groups hierarchy for display filter
|
||||
seen = set()
|
||||
groups = [ m.session_set.first().group for m in meetings.filter(type='interim') ]
|
||||
groups = [m.session_set.first().group for m
|
||||
in meetings.filter(type='interim')]
|
||||
group_parents = []
|
||||
for g in groups:
|
||||
if g.parent.acronym not in seen:
|
||||
|
@ -1069,16 +1166,16 @@ def upcoming(request):
|
|||
# add menu entries
|
||||
menu_entries = get_menu_entries(request)
|
||||
selected_menu_entry = 'upcoming'
|
||||
|
||||
|
||||
# add menu actions
|
||||
actions = []
|
||||
if can_request_interim_meeting(request.user):
|
||||
actions.append(("Request new interim meeting", reverse("ietf.meeting.views.interim_request")))
|
||||
|
||||
return render(request, "meeting/upcoming.html",
|
||||
{ 'meetings':meetings,
|
||||
'menu_actions': actions,
|
||||
'menu_entries': menu_entries,
|
||||
'selected_menu_entry':selected_menu_entry,
|
||||
'group_parents':group_parents,
|
||||
})
|
||||
actions.append(("Request new interim meeting",
|
||||
reverse("ietf.meeting.views.interim_request")))
|
||||
|
||||
return render(request, "meeting/upcoming.html", {
|
||||
'meetings': meetings,
|
||||
'menu_actions': actions,
|
||||
'menu_entries': menu_entries,
|
||||
'selected_menu_entry': selected_menu_entry,
|
||||
'group_parents': group_parents})
|
||||
|
|
|
@ -6,32 +6,32 @@ var interimRequest = {
|
|||
interimRequest.form = $(this);
|
||||
interimRequest.addButton = $('#add_session');
|
||||
interimRequest.inPerson = $('#id_in_person');
|
||||
interimRequest.timezone = $('#id_time_zone');
|
||||
// bind functions
|
||||
$('.select2-field').select2();
|
||||
interimRequest.addButton.click(interimRequest.addSession);
|
||||
$('.btn-delete').click(interimRequest.deleteSession);
|
||||
interimRequest.inPerson.change(interimRequest.toggleLocation);
|
||||
//$('input[name="meeting_type"]').change(interimRequest.checkAddButton);
|
||||
$('input[name="meeting_type"]').change(interimRequest.meetingTypeChanged);
|
||||
$('input[name$="-duration"]').blur(interimRequest.calculateEndTime);
|
||||
$('input[name$="-requested_duration"]').blur(interimRequest.calculateEndTime);
|
||||
$('input[name$="-time"]').blur(interimRequest.calculateEndTime);
|
||||
//$('input[name$="-time"]').blur(interimRequest.setUTC);
|
||||
$('input[name$="-time"]').blur(interimRequest.updateInfo);
|
||||
$('input[name$="-end_time"]').change(interimRequest.updateInfo);
|
||||
$('select[name$="-timezone"]').change(interimRequest.timezoneChange);
|
||||
//interimRequest.form.submit(interimRequest.onSubmit);
|
||||
interimRequest.timezone.change(interimRequest.timezoneChange);
|
||||
// init
|
||||
interimRequest.inPerson.each(interimRequest.toggleLocation);
|
||||
interimRequest.checkAddButton();
|
||||
interimRequest.checkHelpText();
|
||||
//interimRequest.showRequired();
|
||||
$('input[name$="-time"]').each(interimRequest.calculateEndTime);
|
||||
$('input[name$="-time"]').each(interimRequest.updateInfo);
|
||||
//interimRequest.form.submit(interimRequest.onSubmit);
|
||||
//$('input[name$="-time"]').blur(interimRequest.setUTC);
|
||||
},
|
||||
|
||||
addSession : function() {
|
||||
//var templateData = interimRequest.sessionTemplate.clone();
|
||||
var template = interimRequest.form.find('.fieldset.template');
|
||||
var el = template.clone(true);
|
||||
var totalField = $('#id_form-TOTAL_FORMS');
|
||||
var totalField = $('#id_session_set-TOTAL_FORMS');
|
||||
var total = +totalField.val();
|
||||
var meeting_type = $('input[name="meeting_type"]:checked').val();
|
||||
|
||||
|
@ -59,15 +59,8 @@ var interimRequest = {
|
|||
|
||||
// copy field contents
|
||||
var first_session = $(".fieldset:first");
|
||||
el.find("input[name$='city']").val(first_session.find("input[name$='city']").val());
|
||||
el.find("select[name$='country']").val(first_session.find("select[name$='country']").val());
|
||||
el.find("select[name$='timezone']").val(first_session.find("select[name$='timezone']").val());
|
||||
el.find("input[name$='remote_instructions']").val(first_session.find("input[name$='remote_instructions']").val());
|
||||
|
||||
if(meeting_type == 'multi-day'){
|
||||
el.find(".location").prop('disabled', true);
|
||||
}
|
||||
|
||||
$('.btn-delete').removeClass("hidden");
|
||||
},
|
||||
|
||||
|
@ -81,13 +74,14 @@ var interimRequest = {
|
|||
},
|
||||
|
||||
updateInfo : function() {
|
||||
//var url = liaisonForm.form.data("ajaxInfoUrl");
|
||||
//alert('called update');
|
||||
// makes ajax call to server and sets UTC field
|
||||
var time = $(this).val();
|
||||
if(!time){
|
||||
return;
|
||||
}
|
||||
var url = "/meeting/ajax/get-utc";
|
||||
var fieldset = $(this).parents(".fieldset");
|
||||
var time = $(this).val();
|
||||
var timezone_field = fieldset.find('[name$="timezone"]');
|
||||
var timezone = timezone_field.val();
|
||||
var timezone = interimRequest.timezone.val();
|
||||
var name = $(this).attr("id") + "_utc";
|
||||
var utc = fieldset.find("#" + name);
|
||||
$.ajax({
|
||||
|
@ -113,7 +107,7 @@ var interimRequest = {
|
|||
var fieldset = $(this).parents(".fieldset");
|
||||
var start_time = fieldset.find("input[name$='-time']");
|
||||
var end_time = fieldset.find("input[name$='-end_time']");
|
||||
var duration = fieldset.find("input[name$='-duration']");
|
||||
var duration = fieldset.find("input[name$='-requested_duration']");
|
||||
if(!start_time.val() || !duration.val()){
|
||||
return;
|
||||
}
|
||||
|
@ -125,8 +119,6 @@ var interimRequest = {
|
|||
end_time.val(interimRequest.get_formatted_time(d2));
|
||||
end_time.trigger('change');
|
||||
//interimRequest.updateInfo(end_time);
|
||||
//alert( d2 );
|
||||
//alert( end_time.attr("id") );
|
||||
},
|
||||
|
||||
checkAddButton : function() {
|
||||
|
@ -209,22 +201,11 @@ var interimRequest = {
|
|||
var utc = fieldset.find("#" + name);
|
||||
var d = new Date(2000,1,1,values[0],values[1]);
|
||||
utc.val(interimRequest.get_formatted_utc_time(d) + " UTC");
|
||||
//alert(utc.attr("id"));
|
||||
},
|
||||
|
||||
showRequired : function() {
|
||||
// add a required class on labels on forms that should have
|
||||
// explicit requirement asterisks
|
||||
//$("form.show-required").find("input[required],select[required],textarea[required]").closest(".form-group").find("label").first().addClass("required");
|
||||
},
|
||||
|
||||
timezoneChange : function() {
|
||||
//alert("tz change");
|
||||
var fieldset = $(this).parents(".fieldset");
|
||||
var start_time = fieldset.find("input[name$='-time']");
|
||||
var end_time = fieldset.find("input[name$='-end_time']");
|
||||
start_time.trigger('blur');
|
||||
end_time.trigger('change');
|
||||
$("input[name$='-time']").trigger('blur');
|
||||
$("input[name$='-end_time']").trigger('change');
|
||||
},
|
||||
|
||||
toggleLocation : function() {
|
||||
|
|
|
@ -61,30 +61,31 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset %}
|
||||
<div class="fieldset{% if forloop.last %} template{% endif %}" >
|
||||
<div class="form-group">
|
||||
<label for="id_form-{{ forloop.counter0 }}-city" class="col-md-2 control-label">Location</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-city" class="col-md-2 control-label">Location</label>
|
||||
<div class="col-md-10 form-inline">
|
||||
{% render_field form.city class="form-control location" placeholder="City" %}
|
||||
{% render_field form.country class="form-control location" placeholder="Country" %}
|
||||
{% render_field form.timezone class="form-control" %}
|
||||
{% render_field form.time_zone class="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset %}
|
||||
<div class="fieldset{% if forloop.last %} template{% endif %}" >
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_form-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<div class="col-md-2">{% render_field form.date class="form-control" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_form-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<div class="col-md-3 form-inline">
|
||||
{% render_field form.time class="form-control time-field" placeholder="HH:MM" %}
|
||||
{% render_field form.time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
</div>
|
||||
<label for="id_form-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<div class="col-md-4 form-inline">
|
||||
{% render_field form.end_time class="form-control time-field computed" placeholder="HH:MM" disabled="disabled" %}
|
||||
{% render_field form.end_time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
|
@ -92,25 +93,25 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-requested_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.requested_duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_remote_instructions" class="col-md-2 control-label">Remote Instructions</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="ie. Webex address" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="agenda" class="col-md-2 control-label">Agenda</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-agenda" class="col-md-2 control-label">Agenda</label>
|
||||
<div class="col-md-10">{% render_field form.agenda class="form-control" rows="6" placeholder="paste agenda here" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="agenda_note" class="col-md-2 control-label">Agenda Note</label>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-agenda_note" class="col-md-2 control-label">Agenda Note</label>
|
||||
<div class="col-md-10">{% render_field form.agenda_note class="form-control" placeholder="Note" %}</div>
|
||||
</div>
|
||||
<button name="id_form-{{ forloop.counter0 }}-delete-button" type="button" class="btn btn-default hidden btn-delete">Delete</button>
|
||||
<button name="id_session_set-{{ forloop.counter0 }}-delete-button" type="button" class="btn btn-default hidden btn-delete">Delete</button>
|
||||
</div> <!-- fieldset -->
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks ietf_filters %}
|
||||
|
||||
{% block title %}Interim Request Details{% endblock %}
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
<dt>Start Time</dt>
|
||||
<dd>{{ session.official_timeslotassignment.timeslot.time|date:"H:i" }}
|
||||
<dt>Duration</dt>
|
||||
<dd>{{ session.requested_duration }}
|
||||
<dd>{{ session.requested_duration|format_timedelta }}
|
||||
<dt>Remote Instructions</dt>
|
||||
<dd>{{ session.remote_instructions }}
|
||||
<dt>Agenda Note</dt>
|
||||
|
@ -44,7 +44,7 @@
|
|||
{% csrf_token %}
|
||||
{% if can_edit %}
|
||||
{% if sessions.0.status.slug == 'apprw' or sessions.0.status.slug == 'scheda' %}
|
||||
<a class="btn btn-default" href="">Edit</a>
|
||||
<a class="btn btn-default" href="{% url 'ietf.meeting.views.interim_request_edit' number=meeting.number %}">Edit</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if can_approve and sessions.0.status.slug == 'apprw' %}
|
||||
|
|
129
ietf/templates/meeting/interim_request_edit.html
Normal file
129
ietf/templates/meeting/interim_request_edit.html
Normal file
|
@ -0,0 +1,129 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks %}
|
||||
|
||||
{% block title %}Edit Interim Request{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-datepicker/css/bootstrap-datepicker3.min.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Edit Interim Meeting Request</h1>
|
||||
|
||||
<form id="interim-request-form" role="form" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
|
||||
{% bootstrap_field form.group layout='horizontal' %}
|
||||
|
||||
<input type="hidden" name="group" value="{{ form.group.value }}">
|
||||
|
||||
<div class="form-group form-inline">
|
||||
<div class="col-md-offset-2">
|
||||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.in_person %}<strong>In Person</strong></label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.approved %}<strong>Preapproved by AD</strong></label>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="col-md-2 radio-inline"><strong>Meeting Type:</strong></div>
|
||||
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="single" checked="checked" name="meeting_type">Single
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="multi-day" name="meeting_type">Multi-Day
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="series" name="meeting_type">Series
|
||||
</label>
|
||||
-->
|
||||
|
||||
</div> <!-- col-md-offset-2 -->
|
||||
</div> <!-- form-group form-inline -->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_form-{{ forloop.counter0 }}-city" class="col-md-2 control-label">Location</label>
|
||||
<div class="col-md-10 form-inline">
|
||||
{% render_field form.city class="form-control location" placeholder="City" %}
|
||||
{% render_field form.country class="form-control location" placeholder="Country" %}
|
||||
{% render_field form.time_zone class="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset %}
|
||||
<div class="fieldset{% if forloop.last %} template{% endif %}" >
|
||||
|
||||
<input id="id_session_set-{{ forloop.counter0 }}-id" name="session_set-{{ forloop.counter0 }}-id" type="hidden" value="{{ form.instance.pk|default_if_none:"" }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<div class="col-md-2">{% render_field form.date class="form-control" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<div class="col-md-3 form-inline">
|
||||
{% render_field form.time class="form-control time-field" placeholder="HH:MM" %}
|
||||
{% render_field form.time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
</div>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<div class="col-md-4 form-inline">
|
||||
{% render_field form.end_time class="form-control time-field computed" placeholder="HH:MM" disabled="disabled" %}
|
||||
{% render_field form.end_time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-requested_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.requested_duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="ie. Webex address" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-agenda" class="col-md-2 control-label">Agenda</label>
|
||||
<div class="col-md-10">{% render_field form.agenda class="form-control" rows="6" placeholder="paste agenda here" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-agenda_note" class="col-md-2 control-label">Agenda Note</label>
|
||||
<div class="col-md-10">{% render_field form.agenda_note class="form-control" placeholder="Note" %}</div>
|
||||
</div>
|
||||
<button name="id_session_set-{{ forloop.counter0 }}-delete-button" type="button" class="btn btn-default hidden btn-delete">Delete</button>
|
||||
</div> <!-- fieldset -->
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10">
|
||||
<button id="add_session" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span>Add Session</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group"
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upcoming' %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'bootstrap-datepicker/js/bootstrap-datepicker.min.js' %}"></script>
|
||||
<script src="{% static 'select2/select2.min.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/meeting-interim-request.js' %}"></script>
|
||||
{% endblock %}
|
Loading…
Reference in a new issue