feat: Unify slide upload and proposal (#7787)
* attempt at optional approval * Update of meeting slides propose/upload * Fix tests and residual coding bugs * Remove gratuitous blank lines
This commit is contained in:
parent
63d13074d1
commit
9ef7bff77c
|
@ -489,9 +489,12 @@ class UploadAgendaForm(ApplyToAllFileUploadForm):
|
|||
class UploadSlidesForm(ApplyToAllFileUploadForm):
|
||||
doc_type = 'slides'
|
||||
title = forms.CharField(max_length=255)
|
||||
approved = forms.BooleanField(label='Auto-approve', initial=True, required=False)
|
||||
|
||||
def __init__(self, session, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, session, show_apply_to_all_checkbox, can_manage, *args, **kwargs):
|
||||
super().__init__(show_apply_to_all_checkbox, *args, **kwargs)
|
||||
if not can_manage:
|
||||
self.fields.pop('approved')
|
||||
self.session = session
|
||||
|
||||
def clean_title(self):
|
||||
|
|
|
@ -6454,7 +6454,7 @@ class MaterialsTests(TestCase):
|
|||
self.assertFalse(session1.presentations.filter(document__type_id='slides'))
|
||||
test_file = BytesIO(b'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))
|
||||
r = self.client.post(url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(session1.presentations.count(),1)
|
||||
self.assertEqual(session2.presentations.count(),1)
|
||||
|
@ -6477,7 +6477,7 @@ class MaterialsTests(TestCase):
|
|||
url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session2.meeting.number,'session_id':session2.id})
|
||||
test_file = BytesIO(b'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))
|
||||
r = self.client.post(url,dict(file=test_file,title='a different slide file',apply_to_all=False,approved=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(session1.presentations.count(),1)
|
||||
self.assertEqual(session2.presentations.count(),2)
|
||||
|
@ -6501,7 +6501,7 @@ class MaterialsTests(TestCase):
|
|||
self.assertIn('Revise', str(q("title")))
|
||||
test_file = BytesIO(b'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))
|
||||
r = self.client.post(url,dict(file=test_file,title='rename the presentation',apply_to_all=False, approved=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(session1.presentations.count(),1)
|
||||
self.assertEqual(session2.presentations.count(),2)
|
||||
|
@ -6597,7 +6597,7 @@ class MaterialsTests(TestCase):
|
|||
newperson = PersonFactory()
|
||||
|
||||
session_overview_url = urlreverse('ietf.meeting.views.session_details',kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
|
||||
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
|
||||
upload_url = urlreverse('ietf.meeting.views.upload_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
|
||||
|
||||
r = self.client.get(session_overview_url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
|
@ -6612,13 +6612,13 @@ class MaterialsTests(TestCase):
|
|||
self.assertTrue(q('.proposeslides'))
|
||||
self.client.logout()
|
||||
|
||||
login_testing_unauthorized(self,newperson.user.username,propose_url)
|
||||
r = self.client.get(propose_url)
|
||||
login_testing_unauthorized(self,newperson.user.username,upload_url)
|
||||
r = self.client.get(upload_url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
test_file = BytesIO(b'this is not really a slide')
|
||||
test_file.name = 'not_really.txt'
|
||||
empty_outbox()
|
||||
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=False))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
session = Session.objects.get(pk=session.pk)
|
||||
self.assertEqual(session.slidesubmission_set.count(),1)
|
||||
|
@ -6639,6 +6639,25 @@ class MaterialsTests(TestCase):
|
|||
self.assertEqual(len(q('.proposedslidelist p')), 2)
|
||||
self.client.logout()
|
||||
|
||||
login_testing_unauthorized(self,chair.user.username,upload_url)
|
||||
r = self.client.get(upload_url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
test_file = BytesIO(b'this is not really a slide either')
|
||||
test_file.name = 'again_not_really.txt'
|
||||
empty_outbox()
|
||||
r = self.client.post(upload_url,dict(file=test_file,title='a selfapproved test slide file',apply_to_all=True,approved=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(len(outbox),0)
|
||||
self.assertEqual(session.slidesubmission_set.count(),2)
|
||||
self.client.logout()
|
||||
|
||||
self.client.login(username=chair.user.username, password=chair.user.username+"+password")
|
||||
r = self.client.get(session_overview_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('.uploadslidelist p')), 0)
|
||||
self.client.logout()
|
||||
|
||||
def test_disapprove_proposed_slides(self):
|
||||
submission = SlideSubmissionFactory()
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
|
@ -6759,12 +6778,12 @@ class MaterialsTests(TestCase):
|
|||
session.meeting.importantdate_set.create(name_id='revsub',date=date_today()+datetime.timedelta(days=20))
|
||||
newperson = PersonFactory()
|
||||
|
||||
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
|
||||
upload_url = urlreverse('ietf.meeting.views.upload_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
|
||||
|
||||
login_testing_unauthorized(self,newperson.user.username,propose_url)
|
||||
login_testing_unauthorized(self,newperson.user.username,upload_url)
|
||||
test_file = BytesIO(b'this is not really a slide')
|
||||
test_file.name = 'not_really.txt'
|
||||
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=False))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.client.logout()
|
||||
|
||||
|
@ -6787,15 +6806,15 @@ class MaterialsTests(TestCase):
|
|||
|
||||
self.assertEqual(session.presentations.first().document.rev,'00')
|
||||
|
||||
login_testing_unauthorized(self,newperson.user.username,propose_url)
|
||||
login_testing_unauthorized(self,newperson.user.username,upload_url)
|
||||
test_file = BytesIO(b'this is not really a slide, but it is another version of it')
|
||||
test_file.name = 'not_really.txt'
|
||||
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
|
||||
test_file = BytesIO(b'this is not really a slide, but it is third version of it')
|
||||
test_file.name = 'not_really.txt'
|
||||
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.client.logout()
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ safe_for_all_meeting_types = [
|
|||
url(r'^session/(?P<session_id>\d+)/narrativeminutes$', views.upload_session_narrativeminutes),
|
||||
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
|
||||
url(r'^session/(?P<session_id>\d+)/import/minutes$', views.import_session_minutes),
|
||||
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
|
||||
url(r'^session/(?P<session_id>\d+)/slides(?:/%(name)s)?$' % settings.URL_REGEXPS, views.upload_session_slides),
|
||||
url(r'^session/(?P<session_id>\d+)/add_to_session$', views.ajax_add_slides_to_session),
|
||||
url(r'^session/(?P<session_id>\d+)/remove_from_session$', views.ajax_remove_slides_from_session),
|
||||
|
|
|
@ -1702,7 +1702,7 @@ def api_get_session_materials(request, session_id=None):
|
|||
|
||||
minutes = session.minutes()
|
||||
slides_actions = []
|
||||
if can_manage_session_materials(request.user, session.group, session):
|
||||
if can_manage_session_materials(request.user, session.group, session) or not session.is_material_submission_cutoff():
|
||||
slides_actions.append(
|
||||
{
|
||||
"label": "Upload slides",
|
||||
|
@ -1712,16 +1712,6 @@ def api_get_session_materials(request, session_id=None):
|
|||
),
|
||||
}
|
||||
)
|
||||
elif not session.is_material_submission_cutoff():
|
||||
slides_actions.append(
|
||||
{
|
||||
"label": "Propose slides",
|
||||
"url": reverse(
|
||||
"ietf.meeting.views.propose_session_slides",
|
||||
kwargs={"num": session.meeting.number, "session_id": session.pk},
|
||||
),
|
||||
}
|
||||
)
|
||||
else:
|
||||
pass # no action available if it's past cutoff
|
||||
|
||||
|
@ -2920,6 +2910,7 @@ def upload_session_agenda(request, session_id, num):
|
|||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def upload_session_slides(request, session_id, num, name=None):
|
||||
"""Upload new or replacement slides for a session
|
||||
|
||||
|
@ -2927,10 +2918,7 @@ def upload_session_slides(request, session_id, num, name=None):
|
|||
"""
|
||||
# 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):
|
||||
permission_denied(
|
||||
request, "You don't have permission to upload slides for this session."
|
||||
)
|
||||
can_manage = session.can_manage_materials(request.user)
|
||||
if session.is_material_submission_cutoff() and not has_role(
|
||||
request.user, "Secretariat"
|
||||
):
|
||||
|
@ -2955,7 +2943,7 @@ def upload_session_slides(request, session_id, num, name=None):
|
|||
|
||||
if request.method == "POST":
|
||||
form = UploadSlidesForm(
|
||||
session, show_apply_to_all_checkbox, request.POST, request.FILES
|
||||
session, show_apply_to_all_checkbox, can_manage, request.POST, request.FILES
|
||||
)
|
||||
if form.is_valid():
|
||||
file = request.FILES["file"]
|
||||
|
@ -2963,6 +2951,46 @@ def upload_session_slides(request, session_id, num, name=None):
|
|||
apply_to_all = session.type_id == "regular"
|
||||
if show_apply_to_all_checkbox:
|
||||
apply_to_all = form.cleaned_data["apply_to_all"]
|
||||
if can_manage:
|
||||
approved = form.cleaned_data["approved"]
|
||||
else:
|
||||
approved = False
|
||||
|
||||
# Propose slides if not auto-approved
|
||||
if not approved:
|
||||
title = form.cleaned_data['title']
|
||||
submission = SlideSubmission.objects.create(session = session, title = title, filename = '', apply_to_all = apply_to_all, submitter=request.user.person)
|
||||
|
||||
if session.meeting.type_id=='ietf':
|
||||
name = 'slides-%s-%s' % (session.meeting.number,
|
||||
session.group.acronym)
|
||||
if not apply_to_all:
|
||||
name += '-%s' % (session.docname_token(),)
|
||||
else:
|
||||
name = 'slides-%s-%s' % (session.meeting.number, session.docname_token())
|
||||
name = name + '-' + slugify(title).replace('_', '-')[:128]
|
||||
filename = '%s-ss%d%s'% (name, submission.id, ext)
|
||||
destination = io.open(os.path.join(settings.SLIDE_STAGING_PATH, filename),'wb+')
|
||||
for chunk in file.chunks():
|
||||
destination.write(chunk)
|
||||
destination.close()
|
||||
|
||||
submission.filename = filename
|
||||
submission.save()
|
||||
|
||||
(to, cc) = gather_address_lists('slides_proposed', group=session.group, proposer=request.user.person).as_strings()
|
||||
msg_txt = render_to_string("meeting/slides_proposed.txt", {
|
||||
"to": to,
|
||||
"cc": cc,
|
||||
"submission": submission,
|
||||
"settings": settings,
|
||||
})
|
||||
msg = infer_message(msg_txt)
|
||||
msg.by = request.user.person
|
||||
msg.save()
|
||||
send_mail_message(request, msg)
|
||||
messages.success(request, 'Successfully submitted proposed slides.')
|
||||
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
|
||||
|
||||
# Handle creation / update of the Document (but do not save yet)
|
||||
if doc is not None:
|
||||
|
@ -3076,7 +3104,7 @@ def upload_session_slides(request, session_id, num, name=None):
|
|||
initial = {}
|
||||
if doc is not None:
|
||||
initial = {"title": doc.title}
|
||||
form = UploadSlidesForm(session, show_apply_to_all_checkbox, initial=initial)
|
||||
form = UploadSlidesForm(session, show_apply_to_all_checkbox, can_manage, initial=initial)
|
||||
|
||||
return render(
|
||||
request,
|
||||
|
@ -3085,77 +3113,12 @@ def upload_session_slides(request, session_id, num, name=None):
|
|||
"session": session,
|
||||
"session_number": session_number,
|
||||
"slides_sp": session.presentations.filter(document=doc).first() if doc else None,
|
||||
"manage": session.can_manage_materials(request.user),
|
||||
"form": form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def propose_session_slides(request, session_id, num):
|
||||
session = get_object_or_404(Session,pk=session_id)
|
||||
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
|
||||
permission_denied(request, "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)
|
||||
show_apply_to_all_checkbox = len(sessions) > 1 if session.type_id == 'regular' else False
|
||||
if len(sessions) > 1:
|
||||
session_number = 1 + sessions.index(session)
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
form = UploadSlidesForm(session, show_apply_to_all_checkbox,request.POST,request.FILES)
|
||||
if form.is_valid():
|
||||
file = request.FILES['file']
|
||||
_, ext = os.path.splitext(file.name)
|
||||
apply_to_all = session.type_id == 'regular'
|
||||
if show_apply_to_all_checkbox:
|
||||
apply_to_all = form.cleaned_data['apply_to_all']
|
||||
title = form.cleaned_data['title']
|
||||
|
||||
submission = SlideSubmission.objects.create(session = session, title = title, filename = '', apply_to_all = apply_to_all, submitter=request.user.person)
|
||||
|
||||
if session.meeting.type_id=='ietf':
|
||||
name = 'slides-%s-%s' % (session.meeting.number,
|
||||
session.group.acronym)
|
||||
if not apply_to_all:
|
||||
name += '-%s' % (session.docname_token(),)
|
||||
else:
|
||||
name = 'slides-%s-%s' % (session.meeting.number, session.docname_token())
|
||||
name = name + '-' + slugify(title).replace('_', '-')[:128]
|
||||
filename = '%s-ss%d%s'% (name, submission.id, ext)
|
||||
destination = io.open(os.path.join(settings.SLIDE_STAGING_PATH, filename),'wb+')
|
||||
for chunk in file.chunks():
|
||||
destination.write(chunk)
|
||||
destination.close()
|
||||
|
||||
submission.filename = filename
|
||||
submission.save()
|
||||
|
||||
(to, cc) = gather_address_lists('slides_proposed', group=session.group, proposer=request.user.person).as_strings()
|
||||
msg_txt = render_to_string("meeting/slides_proposed.txt", {
|
||||
"to": to,
|
||||
"cc": cc,
|
||||
"submission": submission,
|
||||
"settings": settings,
|
||||
})
|
||||
msg = infer_message(msg_txt)
|
||||
msg.by = request.user.person
|
||||
msg.save()
|
||||
send_mail_message(request, msg)
|
||||
messages.success(request, 'Successfully submitted proposed slides.')
|
||||
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
|
||||
else:
|
||||
initial = {}
|
||||
form = UploadSlidesForm(session, show_apply_to_all_checkbox, initial=initial)
|
||||
|
||||
return render(request, "meeting/propose_session_slides.html",
|
||||
{'session': session,
|
||||
'session_number': session_number,
|
||||
'form': form,
|
||||
})
|
||||
|
||||
|
||||
def remove_sessionpresentation(request, session_id, num, name):
|
||||
sp = get_object_or_404(
|
||||
SessionPresentation, session_id=session_id, document__name=name
|
||||
|
@ -5072,6 +5035,7 @@ def approve_proposed_slides(request, slidesubmission_id, num):
|
|||
"cc": cc,
|
||||
"submission": submission,
|
||||
"settings": settings,
|
||||
"approver": request.user.person
|
||||
})
|
||||
send_mail_text(request, to, None, subject, body, cc=cc)
|
||||
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}Propose Slides for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>
|
||||
Propose Slides for {{ session.meeting }}
|
||||
<br>
|
||||
<small class="text-body-secondary">{{ session.group.acronym }}
|
||||
{% if session.name %}: {{ session.name }}{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2 class="mt-3">
|
||||
Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}
|
||||
</h2>
|
||||
{% endif %}
|
||||
<p class="alert alert-info my-3">
|
||||
This form will allow you to propose a slide deck to the session chairs. After you upload your proposal, mail will be sent to the session chairs asking for their approval.
|
||||
</p>
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -187,7 +187,7 @@
|
|||
</a>
|
||||
{% elif request.user.is_authenticated and not session.is_material_submission_cutoff %}
|
||||
<a class="btn btn-primary proposeslides"
|
||||
href="{% url 'ietf.meeting.views.propose_session_slides' session_id=session.pk num=session.meeting.number %}">
|
||||
href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number %}">
|
||||
Propose slides
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load ietf_filters %}{% autoescape off %}Your proposed slides have been approved for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% if submission.session.name %} : {{submission.session.name}}{% endif %}
|
||||
{% load ietf_filters %}{% autoescape off %}Your proposed slides have been approved for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}{% if submission.session.name %} : {{submission.session.name}}{% endif %} by {{approver}}
|
||||
|
||||
Title: {{submission.title}}
|
||||
|
||||
|
|
|
@ -17,15 +17,21 @@
|
|||
{% else %}
|
||||
Upload new
|
||||
{% endif %}
|
||||
slides for {{ session.meeting }}
|
||||
<br>
|
||||
slides for {{ session.meeting }} <br>
|
||||
<small class="text-body-secondary">
|
||||
{{ session.group.acronym }}
|
||||
{% if session.name %}: {{ session.name }}{% endif %}
|
||||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||
<h2 class="mt-3">
|
||||
Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}
|
||||
</h2>
|
||||
{% endif %}
|
||||
{% if not manage %}
|
||||
<p class="alert alert-info my-3">
|
||||
This form will allow you to propose a slide deck to the session chairs. After you upload your proposal, mail will be sent to the session chairs asking for their approval.
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if slides_sp %}<h3>{{ slides_sp.document.name }}</h3>{% endif %}
|
||||
<form class="my-3" enctype="multipart/form-data" method="post">
|
||||
|
|
Loading…
Reference in a new issue