Added an order field to SessionPresentation.

Set up removing the order field from Document.
Modified the meeting materials, proceedings, and session details views to use the order field on SessionPresentation.
Added bootstrapped views to upload session materials (agendas, slides, minutes, and bluesheets). 
Integrated upload into the session details view.
Commit ready for merge.
 - Legacy-Id: 11989
This commit is contained in:
Robert Sparks 2016-09-14 19:11:31 +00:00
parent 89a3afe025
commit 53f41a6327
11 changed files with 631 additions and 111 deletions

View file

@ -59,7 +59,7 @@ class DocumentInfo(models.Model):
abstract = models.TextField(blank=True)
rev = models.CharField(verbose_name="revision", max_length=16, blank=True)
pages = models.IntegerField(blank=True, null=True)
order = models.IntegerField(default=1, blank=True)
order = models.IntegerField(default=1, blank=True) # This is probably obviated by SessionPresentaion.order
intended_std_level = models.ForeignKey(IntendedStdLevelName, verbose_name="Intended standardization level", blank=True, null=True)
std_level = models.ForeignKey(StdLevelName, verbose_name="Standardization level", blank=True, null=True)
ad = models.ForeignKey(Person, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True)

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('meeting', '0036_add_order_to_sessionpresentation'),
]
operations = [
migrations.AlterModelOptions(
name='sessionpresentation',
options={'ordering': ('order',)},
),
]

View file

@ -963,6 +963,7 @@ class SessionPresentation(models.Model):
class Meta:
db_table = 'meeting_session_materials'
ordering = ('order',)
def __unicode__(self):
return u"%s -> %s-%s" % (self.session, self.document.name, self.rev)
@ -1005,7 +1006,7 @@ class Session(models.Model):
for d in l:
d.meeting_related = lambda: True
else:
l = self.materials.filter(type=material_type).exclude(states__type=material_type, states__slug='deleted').order_by("order")
l = self.materials.filter(type=material_type).exclude(states__type=material_type, states__slug='deleted').order_by('sessionpresentation__order')
if only_one:
if l:

View file

@ -1271,7 +1271,7 @@ class FinalizeProceedingsTests(TestCase):
self.assertEqual(meeting.proceedings_final,True)
self.assertEqual(meeting.session_set.filter(group__acronym="mars").first().sessionpresentation_set.filter(document__type="draft").first().rev,'00')
class BluesheetsTests(TestCase):
class MaterialsTests(TestCase):
def setUp(self):
self.materials_dir = os.path.abspath(settings.TEST_MATERIALS_DIR)
@ -1284,14 +1284,14 @@ class BluesheetsTests(TestCase):
settings.AGENDA_PATH = self.saved_agenda_path
shutil.rmtree(self.materials_dir)
def test_upload_blusheets(self):
def test_upload_bluesheets(self):
session = SessionFactory(meeting__type_id='ietf')
url = urlreverse('ietf.meeting.views.upload_session_bluesheets',kwargs={'num':session.meeting.number,'session_id':session.id})
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertFalse(q("div.alert"))
self.assertTrue('Upload' in unicode(q("title")))
self.assertFalse(session.sessionpresentation_set.exists())
test_file = StringIO('this is some text for a test')
test_file.name = "not_really.pdf"
@ -1302,7 +1302,7 @@ class BluesheetsTests(TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q("div.alert"))
self.assertTrue('Revise' in unicode(q("title")))
test_file = StringIO('this is some different text for a test')
test_file.name = "also_not_really.pdf"
r = self.client.post(url,dict(file=test_file))
@ -1317,7 +1317,7 @@ class BluesheetsTests(TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertFalse(q("div.alert"))
self.assertTrue('Upload' in unicode(q("title")))
self.assertFalse(session.sessionpresentation_set.exists())
test_file = StringIO('this is some text for a test')
test_file.name = "not_really.pdf"
@ -1325,3 +1325,129 @@ class BluesheetsTests(TestCase):
self.assertEqual(r.status_code, 302)
bs_doc = session.sessionpresentation_set.filter(document__type_id='bluesheets').first().document
self.assertEqual(bs_doc.rev,'00')
def test_upload_minutes_agenda(self):
for doctype in ('minutes','agenda'):
session = SessionFactory(meeting__type_id='ietf')
if doctype == 'minutes':
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
else:
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
self.client.logout()
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('Upload' in unicode(q("Title")))
self.assertFalse(session.sessionpresentation_set.exists())
self.assertFalse(q('form input[type="checkbox"]'))
session2 = SessionFactory(meeting=session.meeting,group=session.group)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form input[type="checkbox"]'))
test_file = StringIO('this is some text for a test')
test_file.name = "not_really.json"
r = self.client.post(url,dict(file=test_file))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .has-error'))
test_file = StringIO('this is some text for a test'*1510000)
test_file.name = "not_really.pdf"
r = self.client.post(url,dict(file=test_file))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .has-error'))
test_file = StringIO('this is some text for a test')
test_file.name = "not_really.txt"
r = self.client.post(url,dict(file=test_file,apply_to_all=False))
self.assertEqual(r.status_code, 302)
doc = session.sessionpresentation_set.filter(document__type_id=doctype).first().document
self.assertEqual(doc.rev,'00')
self.assertFalse(session2.sessionpresentation_set.filter(document__type_id=doctype))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('Revise' in unicode(q("Title")))
test_file = StringIO('this is some different text for a test')
test_file.name = "also_not_really.txt"
r = self.client.post(url,dict(file=test_file,apply_to_all=True))
self.assertEqual(r.status_code, 302)
doc = Document.objects.get(pk=doc.pk)
self.assertEqual(doc.rev,'01')
self.assertTrue(session2.sessionpresentation_set.filter(document__type_id=doctype))
def test_upload_minutes_agenda_interim(self):
session=SessionFactory(meeting__type_id='interim')
for doctype in ('minutes','agenda'):
if doctype=='minutes':
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
else:
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
self.client.logout()
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('Upload' in unicode(q("title")))
self.assertFalse(session.sessionpresentation_set.filter(document__type_id=doctype))
test_file = StringIO('this is some text for a test')
test_file.name = "not_really.txt"
r = self.client.post(url,dict(file=test_file))
self.assertEqual(r.status_code, 302)
doc = session.sessionpresentation_set.filter(document__type_id=doctype).first().document
self.assertEqual(doc.rev,'00')
def test_upload_slides(self):
session1 = SessionFactory(meeting__type_id='ietf')
session2 = SessionFactory(meeting=session1.meeting,group=session1.group)
url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session1.meeting.number,'session_id':session1.id})
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('Upload' in unicode(q("title")))
self.assertFalse(session1.sessionpresentation_set.filter(document__type_id='slides'))
test_file = StringIO('this is not really a slide')
test_file.name = 'not_really.txt'
r = self.client.post(url,dict(file=test_file,title='a test slide file',apply_to_all=True))
self.assertEqual(r.status_code, 302)
self.assertEqual(session1.sessionpresentation_set.count(),1)
self.assertEqual(session2.sessionpresentation_set.count(),1)
sp = session2.sessionpresentation_set.first()
self.assertEqual(sp.document.name, 'slides-%s-%s-a-test-slide-file' % (session1.meeting.number,session1.group.acronym ) )
self.assertEqual(sp.order,1)
url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session2.meeting.number,'session_id':session2.id})
test_file = StringIO('some other thing still not slidelike')
test_file.name = 'also_not_really.txt'
r = self.client.post(url,dict(file=test_file,title='a different slide file',apply_to_all=False))
self.assertEqual(r.status_code, 302)
self.assertEqual(session1.sessionpresentation_set.count(),1)
self.assertEqual(session2.sessionpresentation_set.count(),2)
sp = session2.sessionpresentation_set.get(document__name__endswith='-a-different-slide-file')
self.assertEqual(sp.order,2)
self.assertEqual(sp.rev,u'00')
self.assertEqual(sp.document.rev,u'00')
url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session2.meeting.number,'session_id':session2.id,'name':session2.sessionpresentation_set.get(order=2).document.name})
r = self.client.get(url)
self.assertTrue(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('Revise' in unicode(q("title")))
test_file = StringIO('new content for the second slide deck')
test_file.name = 'doesnotmatter.txt'
r = self.client.post(url,dict(file=test_file,title='rename the presentation',apply_to_all=False))
self.assertEqual(r.status_code, 302)
self.assertEqual(session1.sessionpresentation_set.count(),1)
self.assertEqual(session2.sessionpresentation_set.count(),2)
sp = session2.sessionpresentation_set.get(order=2)
self.assertEqual(sp.rev,u'01')
self.assertEqual(sp.document.rev,u'01')

View file

@ -11,6 +11,9 @@ safe_for_all_meeting_types = [
url(r'^session/(?P<acronym>[-a-z0-9]+)/?$', views.session_details),
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
url(r'^session/(?P<session_id>\d+)/slides(?:/%(name)s)?$' % settings.URL_REGEXPS, views.upload_session_slides),
]

View file

@ -27,7 +27,9 @@ from django.forms.models import modelform_factory, inlineformset_factory
from django.forms import ModelForm
from django.template.loader import render_to_string
from django.utils.functional import curry
from django.utils.text import slugify
from django.views.decorators.csrf import ensure_csrf_cookie
from django.template.defaultfilters import filesizeformat
from ietf.doc.fields import SearchableDocumentsField
from ietf.doc.models import Document, State, DocEvent, NewRevisionDocEvent
@ -51,7 +53,6 @@ from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_announcement_request
from ietf.meeting.utils import finalize
from ietf.person.models import Person
from ietf.secr.proceedings.utils import handle_upload_file
from ietf.utils.mail import send_mail_message
from ietf.utils.pipe import pipe
@ -1006,10 +1007,9 @@ def session_details(request, num, acronym ):
if not sessions:
raise Http404
type_counter = Counter()
for session in sessions:
session.type_counter = Counter()
ss = session.timeslotassignments.filter(schedule=meeting.agenda).order_by('timeslot__time')
if ss:
session.time = ', '.join(x.timeslot.time.strftime("%A %b-%d-%Y %H%M") for x in ss)
@ -1028,10 +1028,7 @@ def session_details(request, num, acronym ):
# TODO FIXME Deleted materials shouldn't be in the sessionpresentation_set
for qs in [session.filtered_artifacts,session.filtered_slides,session.filtered_drafts]:
qs = [p for p in qs if p.document.get_state_slug(p.document.type_id)!='deleted']
type_counter.update([p.document.type.slug for p in qs])
#session.filtered_sessionpresentation_set = [p for p in session.sessionpresentation_set.all() if p.document.get_state_slug(p.document.type_id)!='deleted']
#type_counter.update([p.document.type.slug for p in session.filtered_sessionpresentation_set])
session.type_counter.update([p.document.type.slug for p in qs])
can_manage = can_manage_materials(request.user, Group.objects.get(acronym=acronym))
@ -1040,7 +1037,6 @@ def session_details(request, num, acronym ):
'meeting' :meeting ,
'acronym' :acronym,
'can_manage_materials' : can_manage,
'type_counter': type_counter,
})
class SessionDraftsForm(forms.Form):
@ -1114,6 +1110,8 @@ def upload_session_bluesheets(request, session_id, num):
if bluesheet_sp:
doc = bluesheet_sp.document
doc.rev = '%02d' % (int(doc.rev)+1)
bluesheet_sp.rev = doc.rev
bluesheet_sp.save()
else:
sess_time = session.official_timeslotassignment().timeslot.time
if session.meeting.type_id=='ietf':
@ -1138,7 +1136,7 @@ def upload_session_bluesheets(request, session_id, num):
session.sessionpresentation_set.create(document=doc,rev='00')
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
doc.external_url = filename
e = NewRevisionDocEvent.objects.create(doc=doc,time=doc.time,by=Person.objects.get(name='(System)'),type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
e = NewRevisionDocEvent.objects.create(doc=doc,time=doc.time,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
doc.save_with_history([e])
handle_upload_file(file, filename, session.meeting, 'bluesheets')
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
@ -1152,6 +1150,301 @@ def upload_session_bluesheets(request, session_id, num):
'form': form,
})
VALID_MINUTES_EXTENSIONS = ('.txt','.html','.htm','.pdf')
# FIXME: This form validation code (based on the secretariat upload code) only looks at filename extensions
# It should look at the contents of the files instead.
class UploadMinutesForm(forms.Form):
file = forms.FileField(label='Minutes file to upload. Note that you can only upload minutes in txt, html, or pdf formats.')
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=True,required=False)
def __init__(self, num_sessions, *args, **kwargs):
super(UploadMinutesForm, self).__init__(*args, **kwargs)
if num_sessions<2:
self.fields.pop('apply_to_all')
def clean_file(self):
file = self.cleaned_data['file']
if file._size > settings.SECR_MAX_UPLOAD_SIZE:
raise forms.ValidationError('Please keep filesize under %s. Requested upload size is %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE),filesizeformat(file._size)))
if os.path.splitext(file.name)[1].lower() not in VALID_MINUTES_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for minutes: %s' % ','.join(VALID_MINUTES_EXTENSIONS))
return file
def upload_session_minutes(request, session_id, num):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload minutes for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
num_sessions = len(sessions)
if len(sessions) > 1:
session_number = 1 + sessions.index(session)
minutes_sp = session.sessionpresentation_set.filter(document__type='minutes').first()
if request.method == 'POST':
form = UploadMinutesForm(num_sessions,request.POST,request.FILES)
if form.is_valid():
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
apply_to_all = True
if num_sessions > 1:
apply_to_all = form.cleaned_data['apply_to_all']
if minutes_sp:
doc = minutes_sp.document
doc.rev = '%02d' % (int(doc.rev)+1)
minutes_sp.rev = doc.rev
minutes_sp.save()
else:
sess_time = session.official_timeslotassignment().timeslot.time
if session.meeting.type_id=='ietf':
name = 'minutes-%s-%s' % (session.meeting.number,
session.group.acronym)
title = 'Minutes IETF%s: %s' % (session.meeting.number,
session.group.acronym)
if not apply_to_all:
name += '-%s' % (sess_time.strftime("%Y%m%d%H%M"),)
title += ': %s' % (sess_time.strftime("%a %H:%M"),)
else:
name = 'minutes-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
title = 'Minutes %s: %s' % (session.meeting.number, sess_time.strftime("%a %H:%M"))
doc = Document.objects.create(
name = name,
type_id = 'minutes',
title = title,
group = session.group,
rev = '00',
)
doc.states.add(State.objects.get(type_id='minutes',slug='active'))
doc.docalias_set.create(name=doc.name)
session.sessionpresentation_set.create(document=doc,rev='00')
if apply_to_all:
for other_session in sessions:
if other_session != session:
other_session.sessionpresentation_set.filter(document__type='minutes').delete()
other_session.sessionpresentation_set.create(document=doc,rev=doc.rev)
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
doc.external_url = filename
e = NewRevisionDocEvent.objects.create(doc=doc,time=doc.time,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
doc.save_with_history([e])
# The way this function builds the filename it will never trigger the file delete in handle_file_upload.
handle_upload_file(file, filename, session.meeting, 'minutes')
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
else:
form = UploadMinutesForm(num_sessions)
return render(request, "meeting/upload_session_minutes.html",
{'session': session,
'session_number': session_number,
'minutes_sp' : minutes_sp,
'form': form,
})
VALID_AGENDA_EXTENSIONS = ('.txt','.html','.htm',)
# FIXME: This form validation code (based on the secretariat upload code) only looks at filename extensions
# It should look at the contents of the files instead.
class UploadAgendaForm(forms.Form):
file = forms.FileField(label='Agenda file to upload. Note that you can only upload agendas in txt or html formats.')
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=True,required=False)
def __init__(self, num_sessions, *args, **kwargs):
super(UploadAgendaForm, self).__init__(*args, **kwargs)
if num_sessions<2:
self.fields.pop('apply_to_all')
def clean_file(self):
file = self.cleaned_data['file']
if file._size > settings.SECR_MAX_UPLOAD_SIZE:
raise forms.ValidationError('Please keep filesize under %s. Requested upload size is %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE),filesizeformat(file._size)))
if os.path.splitext(file.name)[1].lower() not in VALID_AGENDA_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for agendas: %s' % ','.join(VALID_AGENDA_EXTENSIONS))
return file
def upload_session_agenda(request, session_id, num):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload an agenda for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
num_sessions = len(sessions)
if len(sessions) > 1:
session_number = 1 + sessions.index(session)
agenda_sp = session.sessionpresentation_set.filter(document__type='agenda').first()
if request.method == 'POST':
form = UploadAgendaForm(num_sessions,request.POST,request.FILES)
if form.is_valid():
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
apply_to_all = True
if num_sessions > 1:
apply_to_all = form.cleaned_data['apply_to_all']
if agenda_sp:
doc = agenda_sp.document
doc.rev = '%02d' % (int(doc.rev)+1)
agenda_sp.rev = doc.rev
agenda_sp.save()
else:
sess_time = session.official_timeslotassignment().timeslot.time
if session.meeting.type_id=='ietf':
name = 'agenda-%s-%s' % (session.meeting.number,
session.group.acronym)
title = 'Agenda IETF%s: %s' % (session.meeting.number,
session.group.acronym)
if not apply_to_all:
name += '-%s' % (sess_time.strftime("%Y%m%d%H%M"),)
title += ': %s' % (sess_time.strftime("%a %H:%M"),)
else:
name = 'agenda-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
title = 'Agenda %s: %s' % (session.meeting.number, sess_time.strftime("%a %H:%M"))
doc = Document.objects.create(
name = name,
type_id = 'agenda',
title = title,
group = session.group,
rev = '00',
)
doc.states.add(State.objects.get(type_id='agenda',slug='active'))
doc.docalias_set.create(name=doc.name)
session.sessionpresentation_set.create(document=doc,rev='00')
if apply_to_all:
for other_session in sessions:
if other_session != session:
other_session.sessionpresentation_set.filter(document__type='agenda').delete()
other_session.sessionpresentation_set.create(document=doc,rev=doc.rev)
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
doc.external_url = filename
e = NewRevisionDocEvent.objects.create(doc=doc,time=doc.time,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
doc.save_with_history([e])
# The way this function builds the filename it will never trigger the file delete in handle_file_upload.
handle_upload_file(file, filename, session.meeting, 'agenda')
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
else:
form = UploadAgendaForm(num_sessions)
return render(request, "meeting/upload_session_agenda.html",
{'session': session,
'session_number': session_number,
'agenda_sp' : agenda_sp,
'form': form,
})
VALID_SLIDE_EXTENSIONS = ('.doc','.docx','.pdf','.ppt','.pptx','.txt') # Note the removal of .zip
# FIXME: This form validation code (based on the secretariat upload code) only looks at filename extensions
# It should look at the contents of the files instead.
class UploadSlidesForm(forms.Form):
title = forms.CharField(max_length=255)
file = forms.FileField(label='Slides file to upload.')
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=True,required=False)
def __init__(self, num_sessions, *args, **kwargs):
super(UploadSlidesForm, self).__init__(*args, **kwargs)
if num_sessions<2:
self.fields.pop('apply_to_all')
def clean_file(self):
file = self.cleaned_data['file']
if file._size > settings.SECR_MAX_UPLOAD_SIZE:
raise forms.ValidationError('Please keep filesize under %s. Requested upload size is %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE),filesizeformat(file._size)))
if os.path.splitext(file.name)[1].lower() not in VALID_SLIDE_EXTENSIONS:
raise forms.ValidationError('Only these file types supported for slides: %s' % ','.join(VALID_SLIDE_EXTENSIONS))
return file
def upload_session_slides(request, session_id, num, name):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
return HttpResponseForbidden("You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
num_sessions = len(sessions)
if len(sessions) > 1:
session_number = 1 + sessions.index(session)
slides = None
slides_sp = None
if name:
slides = Document.objects.filter(name=name).first()
if not (slides and slides.type_id=='slides'):
raise Http404
slides_sp = session.sessionpresentation_set.filter(document=slides).first()
if request.method == 'POST':
form = UploadSlidesForm(num_sessions,request.POST,request.FILES)
if form.is_valid():
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
apply_to_all = True
if num_sessions > 1:
apply_to_all = form.cleaned_data['apply_to_all']
if slides_sp:
doc = slides_sp.document
doc.rev = '%02d' % (int(doc.rev)+1)
doc.title = form.cleaned_data['title']
slides_sp.rev = doc.rev
slides_sp.save()
else:
title = form.cleaned_data['title']
sess_time = session.official_timeslotassignment().timeslot.time
if session.meeting.type_id=='ietf':
name = 'slides-%s-%s' % (session.meeting.number,
session.group.acronym)
if not apply_to_all:
name += '-%s' % (sess_time.strftime("%Y%m%d%H%M"),)
else:
name = 'slides-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
name = name + '-' + slugify(title)
doc = Document.objects.create(
name = name,
type_id = 'slides',
title = title,
group = session.group,
rev = '00',
)
doc.states.add(State.objects.get(type_id='slides',slug='active'))
doc.states.add(State.objects.get(type_id='reuse_policy',slug='single'))
doc.docalias_set.create(name=doc.name)
max_order = session.sessionpresentation_set.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0
session.sessionpresentation_set.create(document=doc,rev=doc.rev,order=max_order+1)
if apply_to_all:
for other_session in sessions:
if other_session != session:
max_order = other_session.sessionpresentation_set.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0
other_session.sessionpresentation_set.create(document=doc,rev=doc.rev,order=max_order+1)
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
doc.external_url = filename
e = NewRevisionDocEvent.objects.create(doc=doc,time=doc.time,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
doc.save_with_history([e])
# The way this function builds the filename it will never trigger the file delete in handle_file_upload.
handle_upload_file(file, filename, session.meeting, 'slides')
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
else:
initial = {}
if slides:
initial = {'title':slides.title}
form = UploadSlidesForm(num_sessions, initial=initial)
return render(request, "meeting/upload_session_slides.html",
{'session': session,
'session_number': session_number,
'slides_sp' : slides_sp,
'form': form,
})
@role_required('Secretariat')
def make_schedule_official(request, num, owner, name):

View file

@ -12,95 +12,115 @@
<h2>{% if sessions|length > 1 %}Session {{ forloop.counter }} : {% endif %}{{ session.time }}{% if session.name %} : {{ session.name }}{% endif %}</h2>
{% if session.agenda_note %}<h3>{{session.agenda_note}}</h3>{% endif %}
{% if can_manage_materials %}
{% if session.status.slug == 'sched' or session.status.slug == 'schedw' %}
<div class="buttonlist">
{% if meeting.type.slug == 'interim' and user|has_role:"Secretariat" %}
<a class="btn btn-default" href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">Meeting Details</a>
{% endif %}
<a class="btn btn-default" href="{% url 'ietf.secr.proceedings.views.upload_unified' meeting_num=session.meeting.number acronym=session.group.acronym %}">
Upload/Edit materials
</a>
<a class="btn btn-default" href="{% url 'ietf.meeting.views.add_session_drafts' session_id=session.pk num=session.meeting.number %}">
Link additional drafts to session
</a>
{% if user|has_role:"Secretariat" %}
<a class="btn btn-default" href="{% url 'ietf.meeting.views.upload_session_bluesheets' session_id=session.pk num=session.meeting.number %}">Upload Bluesheets</a>
{% endif %}
{% if not type_counter.agenda %}
<span class="label label-warning">This session does not yet have an agenda</span>
{% endif %}
</div>
{% endif %}
{% endif %}
{% if can_manage_materials %}
{% if session.status.slug == 'sched' or session.status.slug == 'schedw' %}
<div class="buttonlist">
{% if meeting.type.slug == 'interim' and user|has_role:"Secretariat" %}
<a class="btn btn-default" href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">Meeting Details</a>
{% endif %}
</div>
{% if not session.type_counter.agenda %}
<span class="label label-warning">This session does not yet have an agenda</span>
{% endif %}
{% endif %}
{% endif %}
{% if session.filtered_artifacts %}
<div class="panel panel-default">
<div class="panel-heading">Artifacts</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_artifacts %}
<tr>
<td>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Artifacts</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_artifacts %}
<tr>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<td>
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})</a>
</td>
{% if user|has_role:"Secretariat" or can_manage_materials %}
<td class="col-md-2">
{% if pres.document.type.slug == 'minutes' %}
{% url 'ietf.meeting.views.upload_session_minutes' session_id=session.pk num=session.meeting.number as upload_url %}
{% elif pres.document.type.slug == 'agenda' %}
{% url 'ietf.meeting.views.upload_session_agenda' session_id=session.pk num=session.meeting.number as upload_url %}
{% else %}
{% url 'ietf.meeting.views.upload_session_bluesheets' session_id=session.pk num=session.meeting.number as upload_url %}
{% endif %}
{% if pres.document.type.slug != 'bluesheets' or user|has_role:"Secretariat" %}
<a class="btn btn-default btn-sm pull-right" href="{{upload_url}}">Upload Revision</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% if can_manage_materials %}
{% if not session.type_counter.agenda %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_agenda' session_id=session.pk num=session.meeting.number %}">Upload Agenda</a>
{% endif %}
{% if not session.type_counter.minutes %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_minutes' session_id=session.pk num=session.meeting.number %}">Upload Minutes</a>
{% endif %}
{% endif %}
{% if session.filtered_slides %}
<div class="panel panel-default">
<div class="panel-heading">Slides</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_slides %}
<tr>
<td>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% if user|has_role:"Secretariat" and not session.type_counter.bluesheets %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_bluesheets' session_id=session.pk num=session.meeting.number %}">Upload Bluesheets</a>
{% endif %}
{% if session.filtered_drafts %}
<div class="panel panel-default">
<div class="panel-heading">Drafts</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_drafts %}
<tr>
<td>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Slides</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_slides %}
<tr>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<td>
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %}) </a>
</td>
{% if can_manage_materials %}
<td class="col-md-2">
<a class="btn btn-default btn-sm pull-right" href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number name=pres.document.name %}">Upload Revision</a>
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% if can_manage_materials %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number %}">Upload New Slides</a>
{% endif %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Drafts
</div>
<div class="panel-body">
<table class="table table-condensed table-striped">
{% for pres in session.filtered_drafts %}
<tr>
<td>
{% if pres.rev %}
{% url 'doc_view' name=pres.document.name rev=pres.rev as url %}
{% else %}
{% url 'doc_view' name=pres.document.name as url %}
{% endif %}
<a href="{{url}}">{{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})</a>
</td>
</tr>
{% endfor %}
</table>
{% if can_manage_materials %}
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.add_session_drafts' session_id=session.pk num=session.meeting.number %}">
Link additional drafts to session
</a>
{% endif %}
</div>
</div>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}{% if agenda_sp %}Revise{% else %}Upload{% endif %} Agenda for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>{% if agenda_sp %}Revise{% else %}Upload{% endif %} Agenda for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Upload</button>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -2,22 +2,14 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}Upload Bluesheets for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block title %}{% if bluesheet_sp %}Revise{% else %}Upload{% endif %} Bluesheets for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>Upload Bluesheets for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
<h1>{% if bluesheet_sp %}Revise{% else %}Upload{% endif %} Bluesheets for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
{% if bluesheet_sp %}
<div class="alert alert-warning">
Bluesheets have alrady been uploaded for this session.
See <a href="{% url 'ietf.doc.views_doc.document_main' name=bluesheet_sp.document.name %}">{{bluesheet_sp.document.name}}-{{bluesheet_sp.document.rev}}</a>.
Continue with this upload to provide an updated version of that document.
</div>
{% endif %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}

View file

@ -0,0 +1,22 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}{% if minutes_sp %}Revise{% else %}Upload{% endif %} Minutes for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>{% if minutes_sp %}Revise{% else %}Upload{% endif %} Minutes for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Upload</button>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -0,0 +1,23 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles bootstrap3 %}
{% block title %}{% if slides_sp %}Revise{% else %}Upload New{% endif %} Slides for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>{% if slides_sp %}Revise{% else %}Upload New{% endif %} Slides for {{ session.meeting }} : {{ session.group.acronym }}{% if session.name %} : {{session.name}}{% endif %}</h1>
{% if session_number %}<h2> Session {{session_number}} : {{session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi"}}</h2>{% endif %}
{% if slides_sp %}<h3>{{slides_sp.document.name}}</h3>{% endif %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Upload</button>
{% endbuttons %}
</form>
{% endblock %}