Skip to content

Commit 9ef7bff

Browse files
authored
feat: Unify slide upload and proposal (ietf-tools#7787)
* attempt at optional approval * Update of meeting slides propose/upload * Fix tests and residual coding bugs * Remove gratuitous blank lines
1 parent 63d1307 commit 9ef7bff

8 files changed

Lines changed: 95 additions & 131 deletions

File tree

ietf/meeting/forms.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,12 @@ class UploadAgendaForm(ApplyToAllFileUploadForm):
489489
class UploadSlidesForm(ApplyToAllFileUploadForm):
490490
doc_type = 'slides'
491491
title = forms.CharField(max_length=255)
492+
approved = forms.BooleanField(label='Auto-approve', initial=True, required=False)
492493

493-
def __init__(self, session, *args, **kwargs):
494-
super().__init__(*args, **kwargs)
494+
def __init__(self, session, show_apply_to_all_checkbox, can_manage, *args, **kwargs):
495+
super().__init__(show_apply_to_all_checkbox, *args, **kwargs)
496+
if not can_manage:
497+
self.fields.pop('approved')
495498
self.session = session
496499

497500
def clean_title(self):

ietf/meeting/tests_views.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6454,7 +6454,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
64546454
self.assertFalse(session1.presentations.filter(document__type_id='slides'))
64556455
test_file = BytesIO(b'this is not really a slide')
64566456
test_file.name = 'not_really.txt'
6457-
r = self.client.post(url,dict(file=test_file,title='a test slide file',apply_to_all=True))
6457+
r = self.client.post(url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=True))
64586458
self.assertEqual(r.status_code, 302)
64596459
self.assertEqual(session1.presentations.count(),1)
64606460
self.assertEqual(session2.presentations.count(),1)
@@ -6477,7 +6477,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
64776477
url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session2.meeting.number,'session_id':session2.id})
64786478
test_file = BytesIO(b'some other thing still not slidelike')
64796479
test_file.name = 'also_not_really.txt'
6480-
r = self.client.post(url,dict(file=test_file,title='a different slide file',apply_to_all=False))
6480+
r = self.client.post(url,dict(file=test_file,title='a different slide file',apply_to_all=False,approved=True))
64816481
self.assertEqual(r.status_code, 302)
64826482
self.assertEqual(session1.presentations.count(),1)
64836483
self.assertEqual(session2.presentations.count(),2)
@@ -6501,7 +6501,7 @@ def test_upload_slides(self, mock_slides_manager_cls):
65016501
self.assertIn('Revise', str(q("title")))
65026502
test_file = BytesIO(b'new content for the second slide deck')
65036503
test_file.name = 'doesnotmatter.txt'
6504-
r = self.client.post(url,dict(file=test_file,title='rename the presentation',apply_to_all=False))
6504+
r = self.client.post(url,dict(file=test_file,title='rename the presentation',apply_to_all=False, approved=True))
65056505
self.assertEqual(r.status_code, 302)
65066506
self.assertEqual(session1.presentations.count(),1)
65076507
self.assertEqual(session2.presentations.count(),2)
@@ -6597,7 +6597,7 @@ def test_propose_session_slides(self):
65976597
newperson = PersonFactory()
65986598

65996599
session_overview_url = urlreverse('ietf.meeting.views.session_details',kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
6600-
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
6600+
upload_url = urlreverse('ietf.meeting.views.upload_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
66016601

66026602
r = self.client.get(session_overview_url)
66036603
self.assertEqual(r.status_code,200)
@@ -6612,13 +6612,13 @@ def test_propose_session_slides(self):
66126612
self.assertTrue(q('.proposeslides'))
66136613
self.client.logout()
66146614

6615-
login_testing_unauthorized(self,newperson.user.username,propose_url)
6616-
r = self.client.get(propose_url)
6615+
login_testing_unauthorized(self,newperson.user.username,upload_url)
6616+
r = self.client.get(upload_url)
66176617
self.assertEqual(r.status_code,200)
66186618
test_file = BytesIO(b'this is not really a slide')
66196619
test_file.name = 'not_really.txt'
66206620
empty_outbox()
6621-
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
6621+
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=False))
66226622
self.assertEqual(r.status_code, 302)
66236623
session = Session.objects.get(pk=session.pk)
66246624
self.assertEqual(session.slidesubmission_set.count(),1)
@@ -6639,6 +6639,25 @@ def test_propose_session_slides(self):
66396639
self.assertEqual(len(q('.proposedslidelist p')), 2)
66406640
self.client.logout()
66416641

6642+
login_testing_unauthorized(self,chair.user.username,upload_url)
6643+
r = self.client.get(upload_url)
6644+
self.assertEqual(r.status_code,200)
6645+
test_file = BytesIO(b'this is not really a slide either')
6646+
test_file.name = 'again_not_really.txt'
6647+
empty_outbox()
6648+
r = self.client.post(upload_url,dict(file=test_file,title='a selfapproved test slide file',apply_to_all=True,approved=True))
6649+
self.assertEqual(r.status_code, 302)
6650+
self.assertEqual(len(outbox),0)
6651+
self.assertEqual(session.slidesubmission_set.count(),2)
6652+
self.client.logout()
6653+
6654+
self.client.login(username=chair.user.username, password=chair.user.username+"+password")
6655+
r = self.client.get(session_overview_url)
6656+
self.assertEqual(r.status_code, 200)
6657+
q = PyQuery(r.content)
6658+
self.assertEqual(len(q('.uploadslidelist p')), 0)
6659+
self.client.logout()
6660+
66426661
def test_disapprove_proposed_slides(self):
66436662
submission = SlideSubmissionFactory()
66446663
submission.session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
@@ -6759,12 +6778,12 @@ def test_submit_and_approve_multiple_versions(self, mock_slides_manager_cls):
67596778
session.meeting.importantdate_set.create(name_id='revsub',date=date_today()+datetime.timedelta(days=20))
67606779
newperson = PersonFactory()
67616780

6762-
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
6781+
upload_url = urlreverse('ietf.meeting.views.upload_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
67636782

6764-
login_testing_unauthorized(self,newperson.user.username,propose_url)
6783+
login_testing_unauthorized(self,newperson.user.username,upload_url)
67656784
test_file = BytesIO(b'this is not really a slide')
67666785
test_file.name = 'not_really.txt'
6767-
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
6786+
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True,approved=False))
67686787
self.assertEqual(r.status_code, 302)
67696788
self.client.logout()
67706789

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

67886807
self.assertEqual(session.presentations.first().document.rev,'00')
67896808

6790-
login_testing_unauthorized(self,newperson.user.username,propose_url)
6809+
login_testing_unauthorized(self,newperson.user.username,upload_url)
67916810
test_file = BytesIO(b'this is not really a slide, but it is another version of it')
67926811
test_file.name = 'not_really.txt'
6793-
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
6812+
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
67946813
self.assertEqual(r.status_code, 302)
67956814

67966815
test_file = BytesIO(b'this is not really a slide, but it is third version of it')
67976816
test_file.name = 'not_really.txt'
6798-
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
6817+
r = self.client.post(upload_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
67996818
self.assertEqual(r.status_code, 302)
68006819
self.client.logout()
68016820

ietf/meeting/urls.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ def get_redirect_url(self, *args, **kwargs):
2222
url(r'^session/(?P<session_id>\d+)/narrativeminutes$', views.upload_session_narrativeminutes),
2323
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
2424
url(r'^session/(?P<session_id>\d+)/import/minutes$', views.import_session_minutes),
25-
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
2625
url(r'^session/(?P<session_id>\d+)/slides(?:/%(name)s)?$' % settings.URL_REGEXPS, views.upload_session_slides),
2726
url(r'^session/(?P<session_id>\d+)/add_to_session$', views.ajax_add_slides_to_session),
2827
url(r'^session/(?P<session_id>\d+)/remove_from_session$', views.ajax_remove_slides_from_session),

ietf/meeting/views.py

Lines changed: 47 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@ def api_get_session_materials(request, session_id=None):
17021702

17031703
minutes = session.minutes()
17041704
slides_actions = []
1705-
if can_manage_session_materials(request.user, session.group, session):
1705+
if can_manage_session_materials(request.user, session.group, session) or not session.is_material_submission_cutoff():
17061706
slides_actions.append(
17071707
{
17081708
"label": "Upload slides",
@@ -1712,16 +1712,6 @@ def api_get_session_materials(request, session_id=None):
17121712
),
17131713
}
17141714
)
1715-
elif not session.is_material_submission_cutoff():
1716-
slides_actions.append(
1717-
{
1718-
"label": "Propose slides",
1719-
"url": reverse(
1720-
"ietf.meeting.views.propose_session_slides",
1721-
kwargs={"num": session.meeting.number, "session_id": session.pk},
1722-
),
1723-
}
1724-
)
17251715
else:
17261716
pass # no action available if it's past cutoff
17271717

@@ -2920,17 +2910,15 @@ def upload_session_agenda(request, session_id, num):
29202910
})
29212911

29222912

2913+
@login_required
29232914
def upload_session_slides(request, session_id, num, name=None):
29242915
"""Upload new or replacement slides for a session
29252916
29262917
If name is None or "", expects a new set of slides. Otherwise, replaces the named slides with a new rev.
29272918
"""
29282919
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
29292920
session = get_object_or_404(Session, pk=session_id)
2930-
if not session.can_manage_materials(request.user):
2931-
permission_denied(
2932-
request, "You don't have permission to upload slides for this session."
2933-
)
2921+
can_manage = session.can_manage_materials(request.user)
29342922
if session.is_material_submission_cutoff() and not has_role(
29352923
request.user, "Secretariat"
29362924
):
@@ -2955,14 +2943,54 @@ def upload_session_slides(request, session_id, num, name=None):
29552943

29562944
if request.method == "POST":
29572945
form = UploadSlidesForm(
2958-
session, show_apply_to_all_checkbox, request.POST, request.FILES
2946+
session, show_apply_to_all_checkbox, can_manage, request.POST, request.FILES
29592947
)
29602948
if form.is_valid():
29612949
file = request.FILES["file"]
29622950
_, ext = os.path.splitext(file.name)
29632951
apply_to_all = session.type_id == "regular"
29642952
if show_apply_to_all_checkbox:
29652953
apply_to_all = form.cleaned_data["apply_to_all"]
2954+
if can_manage:
2955+
approved = form.cleaned_data["approved"]
2956+
else:
2957+
approved = False
2958+
2959+
# Propose slides if not auto-approved
2960+
if not approved:
2961+
title = form.cleaned_data['title']
2962+
submission = SlideSubmission.objects.create(session = session, title = title, filename = '', apply_to_all = apply_to_all, submitter=request.user.person)
2963+
2964+
if session.meeting.type_id=='ietf':
2965+
name = 'slides-%s-%s' % (session.meeting.number,
2966+
session.group.acronym)
2967+
if not apply_to_all:
2968+
name += '-%s' % (session.docname_token(),)
2969+
else:
2970+
name = 'slides-%s-%s' % (session.meeting.number, session.docname_token())
2971+
name = name + '-' + slugify(title).replace('_', '-')[:128]
2972+
filename = '%s-ss%d%s'% (name, submission.id, ext)
2973+
destination = io.open(os.path.join(settings.SLIDE_STAGING_PATH, filename),'wb+')
2974+
for chunk in file.chunks():
2975+
destination.write(chunk)
2976+
destination.close()
2977+
2978+
submission.filename = filename
2979+
submission.save()
2980+
2981+
(to, cc) = gather_address_lists('slides_proposed', group=session.group, proposer=request.user.person).as_strings()
2982+
msg_txt = render_to_string("meeting/slides_proposed.txt", {
2983+
"to": to,
2984+
"cc": cc,
2985+
"submission": submission,
2986+
"settings": settings,
2987+
})
2988+
msg = infer_message(msg_txt)
2989+
msg.by = request.user.person
2990+
msg.save()
2991+
send_mail_message(request, msg)
2992+
messages.success(request, 'Successfully submitted proposed slides.')
2993+
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
29662994

29672995
# Handle creation / update of the Document (but do not save yet)
29682996
if doc is not None:
@@ -3076,7 +3104,7 @@ def upload_session_slides(request, session_id, num, name=None):
30763104
initial = {}
30773105
if doc is not None:
30783106
initial = {"title": doc.title}
3079-
form = UploadSlidesForm(session, show_apply_to_all_checkbox, initial=initial)
3107+
form = UploadSlidesForm(session, show_apply_to_all_checkbox, can_manage, initial=initial)
30803108

30813109
return render(
30823110
request,
@@ -3085,77 +3113,12 @@ def upload_session_slides(request, session_id, num, name=None):
30853113
"session": session,
30863114
"session_number": session_number,
30873115
"slides_sp": session.presentations.filter(document=doc).first() if doc else None,
3116+
"manage": session.can_manage_materials(request.user),
30883117
"form": form,
30893118
},
30903119
)
30913120

30923121

3093-
@login_required
3094-
def propose_session_slides(request, session_id, num):
3095-
session = get_object_or_404(Session,pk=session_id)
3096-
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
3097-
permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
3098-
3099-
session_number = None
3100-
sessions = get_sessions(session.meeting.number,session.group.acronym)
3101-
show_apply_to_all_checkbox = len(sessions) > 1 if session.type_id == 'regular' else False
3102-
if len(sessions) > 1:
3103-
session_number = 1 + sessions.index(session)
3104-
3105-
3106-
if request.method == 'POST':
3107-
form = UploadSlidesForm(session, show_apply_to_all_checkbox,request.POST,request.FILES)
3108-
if form.is_valid():
3109-
file = request.FILES['file']
3110-
_, ext = os.path.splitext(file.name)
3111-
apply_to_all = session.type_id == 'regular'
3112-
if show_apply_to_all_checkbox:
3113-
apply_to_all = form.cleaned_data['apply_to_all']
3114-
title = form.cleaned_data['title']
3115-
3116-
submission = SlideSubmission.objects.create(session = session, title = title, filename = '', apply_to_all = apply_to_all, submitter=request.user.person)
3117-
3118-
if session.meeting.type_id=='ietf':
3119-
name = 'slides-%s-%s' % (session.meeting.number,
3120-
session.group.acronym)
3121-
if not apply_to_all:
3122-
name += '-%s' % (session.docname_token(),)
3123-
else:
3124-
name = 'slides-%s-%s' % (session.meeting.number, session.docname_token())
3125-
name = name + '-' + slugify(title).replace('_', '-')[:128]
3126-
filename = '%s-ss%d%s'% (name, submission.id, ext)
3127-
destination = io.open(os.path.join(settings.SLIDE_STAGING_PATH, filename),'wb+')
3128-
for chunk in file.chunks():
3129-
destination.write(chunk)
3130-
destination.close()
3131-
3132-
submission.filename = filename
3133-
submission.save()
3134-
3135-
(to, cc) = gather_address_lists('slides_proposed', group=session.group, proposer=request.user.person).as_strings()
3136-
msg_txt = render_to_string("meeting/slides_proposed.txt", {
3137-
"to": to,
3138-
"cc": cc,
3139-
"submission": submission,
3140-
"settings": settings,
3141-
})
3142-
msg = infer_message(msg_txt)
3143-
msg.by = request.user.person
3144-
msg.save()
3145-
send_mail_message(request, msg)
3146-
messages.success(request, 'Successfully submitted proposed slides.')
3147-
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
3148-
else:
3149-
initial = {}
3150-
form = UploadSlidesForm(session, show_apply_to_all_checkbox, initial=initial)
3151-
3152-
return render(request, "meeting/propose_session_slides.html",
3153-
{'session': session,
3154-
'session_number': session_number,
3155-
'form': form,
3156-
})
3157-
3158-
31593122
def remove_sessionpresentation(request, session_id, num, name):
31603123
sp = get_object_or_404(
31613124
SessionPresentation, session_id=session_id, document__name=name
@@ -5072,6 +5035,7 @@ def approve_proposed_slides(request, slidesubmission_id, num):
50725035
"cc": cc,
50735036
"submission": submission,
50745037
"settings": settings,
5038+
"approver": request.user.person
50755039
})
50765040
send_mail_text(request, to, None, subject, body, cc=cc)
50775041
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)

ietf/templates/meeting/propose_session_slides.html

Lines changed: 0 additions & 27 deletions
This file was deleted.

ietf/templates/meeting/session_details_panel.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ <h3 class="mt-4">Slides</h3>
187187
</a>
188188
{% elif request.user.is_authenticated and not session.is_material_submission_cutoff %}
189189
<a class="btn btn-primary proposeslides"
190-
href="{% url 'ietf.meeting.views.propose_session_slides' session_id=session.pk num=session.meeting.number %}">
190+
href="{% url 'ietf.meeting.views.upload_session_slides' session_id=session.pk num=session.meeting.number %}">
191191
Propose slides
192192
</a>
193193
{% endif %}

ietf/templates/meeting/slides_approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% 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 %}
1+
{% 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}}
22

33
Title: {{submission.title}}
44

0 commit comments

Comments
 (0)