Skip to content

Commit

Permalink
feat: Unify slide upload and proposal (#7787)
Browse files Browse the repository at this point in the history
* attempt at optional approval

* Update of meeting slides propose/upload

* Fix tests and residual coding bugs

* Remove gratuitous blank lines
  • Loading branch information
jimfenton authored Aug 6, 2024
1 parent 63d1307 commit 9ef7bff
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 131 deletions.
7 changes: 5 additions & 2 deletions ietf/meeting/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
45 changes: 32 additions & 13 deletions ietf/meeting/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6454,7 +6454,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
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)
Expand All @@ -6477,7 +6477,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
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)
Expand All @@ -6501,7 +6501,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
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)
Expand Down Expand Up @@ -6597,7 +6597,7 @@ def test_propose_session_slides(self):
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)
Expand All @@ -6612,13 +6612,13 @@ def test_propose_session_slides(self):
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)
Expand All @@ -6639,6 +6639,25 @@ def test_propose_session_slides(self):
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))
Expand Down Expand Up @@ -6759,12 +6778,12 @@ def test_submit_and_approve_multiple_versions(self, mock_slides_manager_cls):
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()

Expand All @@ -6787,15 +6806,15 @@ def test_submit_and_approve_multiple_versions(self, mock_slides_manager_cls):

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()

Expand Down
1 change: 0 additions & 1 deletion ietf/meeting/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def get_redirect_url(self, *args, **kwargs):
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),
Expand Down
130 changes: 47 additions & 83 deletions ietf/meeting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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

Expand Down Expand Up @@ -2920,17 +2910,15 @@ 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
If name is None or "", expects a new set of slides. Otherwise, replaces the named slides with a new rev.
"""
# 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"
):
Expand All @@ -2955,14 +2943,54 @@ 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"]
_, 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"]
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:
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
27 changes: 0 additions & 27 deletions ietf/templates/meeting/propose_session_slides.html

This file was deleted.

2 changes: 1 addition & 1 deletion ietf/templates/meeting/session_details_panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ <h3 class="mt-4">Slides</h3>
</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 %}
Expand Down
2 changes: 1 addition & 1 deletion ietf/templates/meeting/slides_approved.txt
Original file line number Diff line number Diff line change
@@ -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}}

Expand Down
Loading

0 comments on commit 9ef7bff

Please sign in to comment.