diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index d483772895..5c62226ea7 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -6405,7 +6405,9 @@ def test_enter_agenda(self): doc = Document.objects.get(pk=doc.pk) self.assertEqual(doc.rev,'02') - def test_upload_slides(self): + @override_settings(MEETECHO_API_CONFIG="fake settings") # enough to trigger API calls + @patch("ietf.meeting.views.SlidesManager") + def test_upload_slides(self, mock_slides_manager_cls): session1 = SessionFactory(meeting__type_id='ietf') session2 = SessionFactory(meeting=session1.meeting,group=session1.group) @@ -6413,6 +6415,7 @@ def test_upload_slides(self): login_testing_unauthorized(self,"secretary",url) r = self.client.get(url) self.assertEqual(r.status_code, 200) + self.assertFalse(mock_slides_manager_cls.called) q = PyQuery(r.content) self.assertIn('Upload', str(q("title"))) self.assertFalse(session1.presentations.filter(document__type_id='slides')) @@ -6425,6 +6428,18 @@ def test_upload_slides(self): sp = session2.presentations.first() self.assertEqual(sp.document.name, 'slides-%s-%s-a-test-slide-file' % (session1.meeting.number,session1.group.acronym ) ) self.assertEqual(sp.order,1) + self.assertEqual(mock_slides_manager_cls.call_count, 1) + self.assertEqual(mock_slides_manager_cls.call_args, call(api_config="fake settings")) + self.assertEqual(mock_slides_manager_cls.return_value.add.call_count, 2) + # don't care which order they were called in, just that both sessions were updated + self.assertCountEqual( + mock_slides_manager_cls.return_value.add.call_args_list, + [ + call(session=session1, slides=sp.document, order=1), + call(session=session2, slides=sp.document, order=1), + ], + ) + mock_slides_manager_cls.reset_mock() 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') @@ -6437,6 +6452,14 @@ def test_upload_slides(self): self.assertEqual(sp.order,2) self.assertEqual(sp.rev,'00') self.assertEqual(sp.document.rev,'00') + self.assertEqual(mock_slides_manager_cls.call_count, 1) + self.assertEqual(mock_slides_manager_cls.call_args, call(api_config="fake settings")) + self.assertEqual(mock_slides_manager_cls.return_value.add.call_count, 1) + self.assertEqual( + mock_slides_manager_cls.return_value.add.call_args, + call(session=session2, slides=sp.document, order=2), + ) + mock_slides_manager_cls.reset_mock() url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session2.meeting.number,'session_id':session2.id,'name':session2.presentations.get(order=2).document.name}) r = self.client.get(url) @@ -6449,10 +6472,22 @@ def test_upload_slides(self): self.assertEqual(r.status_code, 302) self.assertEqual(session1.presentations.count(),1) self.assertEqual(session2.presentations.count(),2) - sp = session2.presentations.get(order=2) - self.assertEqual(sp.rev,'01') - self.assertEqual(sp.document.rev,'01') - + replacement_sp = session2.presentations.get(order=2) + self.assertEqual(replacement_sp.rev,'01') + self.assertEqual(replacement_sp.document.rev,'01') + self.assertEqual(mock_slides_manager_cls.call_count, 1) + self.assertEqual(mock_slides_manager_cls.call_args, call(api_config="fake settings")) + self.assertEqual(mock_slides_manager_cls.return_value.delete.call_count, 1) + self.assertEqual( + mock_slides_manager_cls.return_value.delete.call_args, + call(session=session2, slides=sp.document), + ) + self.assertEqual(mock_slides_manager_cls.return_value.add.call_count, 1) + self.assertEqual( + mock_slides_manager_cls.return_value.add.call_args, + call(session=session2, slides=replacement_sp.document, order=2), + ) + def test_upload_slide_title_bad_unicode(self): session1 = SessionFactory(meeting__type_id='ietf') url = urlreverse('ietf.meeting.views.upload_session_slides',kwargs={'num':session1.meeting.number,'session_id':session1.id}) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 778bfcee4c..280e3ca313 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2864,82 +2864,131 @@ def upload_session_agenda(request, session_id, num): 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) + 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.") - 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.") + permission_denied( + request, "You don't have permission to upload slides for this session." + ) + 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 + 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) + session_number = 1 + sessions.index(session) - slides = None - slides_sp = None + doc = None if name: - slides = Document.objects.filter(name=name).first() - if not (slides and slides.type_id=='slides'): - raise Http404 - slides_sp = session.presentations.filter(document=slides).first() - - if request.method == 'POST': - form = UploadSlidesForm(session, show_apply_to_all_checkbox,request.POST,request.FILES) + doc = get_object_or_404( + session.presentations, document__name=name, document__type_id="slides" + ).document + + if request.method == "POST": + form = UploadSlidesForm( + session, show_apply_to_all_checkbox, request.POST, request.FILES + ) if form.is_valid(): - file = request.FILES['file'] + file = request.FILES["file"] _, ext = os.path.splitext(file.name) - apply_to_all = session.type_id == 'regular' + apply_to_all = session.type_id == "regular" if show_apply_to_all_checkbox: - 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() + apply_to_all = form.cleaned_data["apply_to_all"] + + # Handle creation / update of the Document (but do not save yet) + if doc is not None: + # This is a revision - bump the version and update the title. + doc.rev = "%02d" % (int(doc.rev) + 1) + doc.title = form.cleaned_data["title"] else: - title = form.cleaned_data['title'] - if session.meeting.type_id=='ietf': - name = 'slides-%s-%s' % (session.meeting.number, - session.group.acronym) + # This is a new slide deck - create a new doc unless one exists with that name + title = form.cleaned_data["title"] + 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(),) + name += "-%s" % (session.docname_token(),) else: - name = 'slides-%s-%s' % (session.meeting.number, session.docname_token()) - name = name + '-' + slugify(title).replace('_', '-')[:128] + name = "slides-%s-%s" % ( + session.meeting.number, + session.docname_token(), + ) + name = name + "-" + slugify(title).replace("_", "-")[:128] if Document.objects.filter(name=name).exists(): - doc = Document.objects.get(name=name) - doc.rev = '%02d' % (int(doc.rev)+1) - doc.title = form.cleaned_data['title'] + doc = Document.objects.get(name=name) + doc.rev = "%02d" % (int(doc.rev) + 1) + doc.title = form.cleaned_data["title"] else: 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')) - if session.presentations.filter(document=doc).exists(): - sp = session.presentations.get(document=doc) - sp.rev = doc.rev - sp.save() + 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")) + + # Now handle creation / update of the SessionPresentation(s) + sessions_to_apply = sessions if apply_to_all else [session] + if hasattr(settings, "MEETECHO_API_CONFIG"): + sm = SlidesManager(api_config=settings.MEETECHO_API_CONFIG) else: - max_order = session.presentations.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0 - session.presentations.create(document=doc,rev=doc.rev,order=max_order+1) - if apply_to_all: - for other_session in sessions: - if other_session != session and not other_session.presentations.filter(document=doc).exists(): - max_order = other_session.presentations.filter(document__type='slides').aggregate(Max('order'))['order__max'] or 0 - other_session.presentations.create(document=doc,rev=doc.rev,order=max_order+1) - filename = '%s-%s%s'% ( doc.name, doc.rev, ext) + sm = None + for sess in sessions_to_apply: + sp = sess.presentations.filter(document=doc).first() + if sp is not None: + sp.rev = doc.rev + sp.save() + if sm is not None: + sm.delete(session=sess, slides=doc) # removes the existing deck with previous revision + sm.add(session=sess, slides=doc, order=sp.order) + else: + max_order = ( + sess.presentations.filter(document__type="slides").aggregate( + Max("order") + )["order__max"] + or 0 + ) + sp = sess.presentations.create( + document=doc, rev=doc.rev, order=max_order + 1 + ) + if sm is not None: + sm.add(session=sess, slides=doc, order=sp.order) + + # Now handle the uploaded file + filename = "%s-%s%s" % (doc.name, doc.rev, ext) doc.uploaded_filename = filename - e = NewRevisionDocEvent.objects.create(doc=doc,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev) + e = NewRevisionDocEvent.objects.create( + doc=doc, + by=request.user.person, + type="new_revision", + desc="New revision available: %s" % doc.rev, + rev=doc.rev, + ) # The way this function builds the filename it will never trigger the file delete in handle_file_upload. - save_error = handle_upload_file(file, filename, session.meeting, 'slides', request=request, encoding=form.file_encoding[file.name]) + save_error = handle_upload_file( + file, + filename, + session.meeting, + "slides", + request=request, + encoding=form.file_encoding[file.name], + ) if save_error: form.add_error(None, save_error) else: @@ -2947,20 +2996,31 @@ def upload_session_slides(request, session_id, num, name=None): post_process(doc) messages.success( request, - f'Successfully uploaded slides as revision {doc.rev} of {doc.name}.') - return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym) - else: + f"Successfully uploaded slides as revision {doc.rev} of {doc.name}.", + ) + return redirect( + "ietf.meeting.views.session_details", + num=num, + acronym=session.group.acronym, + ) + else: initial = {} - if slides: - initial = {'title':slides.title} + if doc is not None: + initial = {"title": doc.title} form = UploadSlidesForm(session, show_apply_to_all_checkbox, initial=initial) - return render(request, "meeting/upload_session_slides.html", - {'session': session, - 'session_number': session_number, - 'slides_sp' : slides_sp, - 'form': form, - }) + return render( + request, + "meeting/upload_session_slides.html", + { + "session": session, + "session_number": session_number, + "slides_sp": session.presentations.filter(document=doc).first() if doc else None, + "form": form, + }, + ) + + @login_required def propose_session_slides(request, session_id, num): session = get_object_or_404(Session,pk=session_id) @@ -3026,146 +3086,263 @@ def propose_session_slides(request, session_id, num): 'form': form, }) + def remove_sessionpresentation(request, session_id, num, name): - sp = get_object_or_404(SessionPresentation,session_id=session_id,document__name=name) + sp = get_object_or_404( + SessionPresentation, session_id=session_id, document__name=name + ) session = sp.session if not session.can_manage_materials(request.user): - permission_denied(request, "You don't have permission to manage materials for this session.") - 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.") - if request.method == 'POST': + permission_denied( + request, "You don't have permission to manage materials for this session." + ) + 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.", + ) + if request.method == "POST": session.presentations.filter(pk=sp.pk).delete() - c = DocEvent(type="added_comment", doc=sp.document, rev=sp.document.rev, by=request.user.person) + c = DocEvent( + type="added_comment", + doc=sp.document, + rev=sp.document.rev, + by=request.user.person, + ) c.desc = "Removed from session: %s" % (session) c.save() - messages.success(request, f'Successfully removed {name}.') + messages.success(request, f"Successfully removed {name}.") if sp.document.type_id == "slides" and hasattr(settings, "MEETECHO_API_CONFIG"): sm = SlidesManager(api_config=settings.MEETECHO_API_CONFIG) sm.delete(session=session, slides=sp.document) - - return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) - return render(request,'meeting/remove_sessionpresentation.html', {'sp': sp }) + return redirect( + "ietf.meeting.views.session_details", + num=session.meeting.number, + acronym=session.group.acronym, + ) + + return render(request, "meeting/remove_sessionpresentation.html", {"sp": sp}) + def ajax_add_slides_to_session(request, session_id, num): - session = get_object_or_404(Session,pk=session_id) + 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.") - 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.") + permission_denied( + request, "You don't have permission to upload slides for this session." + ) + 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.", + ) - if request.method != 'POST' or not request.POST: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json') + if request.method != "POST" or not request.POST: + return HttpResponse( + json.dumps({"success": False, "error": "No data submitted or not POST"}), + content_type="application/json", + ) - order_str = request.POST.get('order', None) + order_str = request.POST.get("order", None) try: order = int(order_str) except (ValueError, TypeError): - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied order is not valid' }),content_type='application/json') - if order < 1 or order > session.presentations.filter(document__type_id='slides').count() + 1 : - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied order is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied order is not valid"}), + content_type="application/json", + ) + if ( + order < 1 + or order > session.presentations.filter(document__type_id="slides").count() + 1 + ): + return HttpResponse( + json.dumps({"success": False, "error": "Supplied order is not valid"}), + content_type="application/json", + ) - name = request.POST.get('name', None) + name = request.POST.get("name", None) doc = Document.objects.filter(name=name).first() if not doc: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied name is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied name is not valid"}), + content_type="application/json", + ) if not session.presentations.filter(document=doc).exists(): condition_slide_order(session) - session.presentations.filter(document__type_id='slides', order__gte=order).update(order=F('order')+1) - session.presentations.create(document=doc,rev=doc.rev,order=order) - DocEvent.objects.create(type="added_comment", doc=doc, rev=doc.rev, by=request.user.person, desc="Added to session: %s" % session) + session.presentations.filter( + document__type_id="slides", order__gte=order + ).update(order=F("order") + 1) + session.presentations.create(document=doc, rev=doc.rev, order=order) + DocEvent.objects.create( + type="added_comment", + doc=doc, + rev=doc.rev, + by=request.user.person, + desc="Added to session: %s" % session, + ) # Notify Meetecho of new slides if the API is configured if hasattr(settings, "MEETECHO_API_CONFIG"): sm = SlidesManager(api_config=settings.MEETECHO_API_CONFIG) sm.add(session=session, slides=doc, order=order) - return HttpResponse(json.dumps({'success':True}), content_type='application/json') + return HttpResponse(json.dumps({"success": True}), content_type="application/json") def ajax_remove_slides_from_session(request, session_id, num): - session = get_object_or_404(Session,pk=session_id) + 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.") - 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.") + permission_denied( + request, "You don't have permission to upload slides for this session." + ) + 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.", + ) - if request.method != 'POST' or not request.POST: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json') + if request.method != "POST" or not request.POST: + return HttpResponse( + json.dumps({"success": False, "error": "No data submitted or not POST"}), + content_type="application/json", + ) - oldIndex_str = request.POST.get('oldIndex', None) + oldIndex_str = request.POST.get("oldIndex", None) try: oldIndex = int(oldIndex_str) except (ValueError, TypeError): - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') - if oldIndex < 1 or oldIndex > session.presentations.filter(document__type_id='slides').count() : - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) + if ( + oldIndex < 1 + or oldIndex > session.presentations.filter(document__type_id="slides").count() + ): + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) - name = request.POST.get('name', None) + name = request.POST.get("name", None) doc = Document.objects.filter(name=name).first() if not doc: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied name is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied name is not valid"}), + content_type="application/json", + ) condition_slide_order(session) affected_presentations = session.presentations.filter(document=doc).first() if affected_presentations: if affected_presentations.order == oldIndex: affected_presentations.delete() - session.presentations.filter(document__type_id='slides', order__gt=oldIndex).update(order=F('order')-1) - DocEvent.objects.create(type="added_comment", doc=doc, rev=doc.rev, by=request.user.person, desc="Removed from session: %s" % session) + session.presentations.filter( + document__type_id="slides", order__gt=oldIndex + ).update(order=F("order") - 1) + DocEvent.objects.create( + type="added_comment", + doc=doc, + rev=doc.rev, + by=request.user.person, + desc="Removed from session: %s" % session, + ) # Notify Meetecho of removed slides if the API is configured if hasattr(settings, "MEETECHO_API_CONFIG"): sm = SlidesManager(api_config=settings.MEETECHO_API_CONFIG) sm.delete(session=session, slides=doc) # Report success - return HttpResponse(json.dumps({'success':True}), content_type='application/json') + return HttpResponse( + json.dumps({"success": True}), content_type="application/json" + ) else: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Name does not match index' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Name does not match index"}), + content_type="application/json", + ) else: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'SessionPresentation not found' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "SessionPresentation not found"}), + content_type="application/json", + ) def ajax_reorder_slides_in_session(request, session_id, num): - session = get_object_or_404(Session,pk=session_id) + 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.") - 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.") + permission_denied( + request, "You don't have permission to upload slides for this session." + ) + 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.", + ) - if request.method != 'POST' or not request.POST: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json') + if request.method != "POST" or not request.POST: + return HttpResponse( + json.dumps({"success": False, "error": "No data submitted or not POST"}), + content_type="application/json", + ) session_slides = session.presentations.filter(document__type_id="slides") num_slides_in_session = session_slides.count() - oldIndex_str = request.POST.get('oldIndex', None) + oldIndex_str = request.POST.get("oldIndex", None) try: oldIndex = int(oldIndex_str) except (ValueError, TypeError): - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') - if oldIndex < 1 or oldIndex > num_slides_in_session : - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) + if oldIndex < 1 or oldIndex > num_slides_in_session: + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) - newIndex_str = request.POST.get('newIndex', None) + newIndex_str = request.POST.get("newIndex", None) try: newIndex = int(newIndex_str) except (ValueError, TypeError): - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') - if newIndex < 1 or newIndex > num_slides_in_session : - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) + if newIndex < 1 or newIndex > num_slides_in_session: + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) if newIndex == oldIndex: - return HttpResponse(json.dumps({ 'success' : False, 'error' : 'Supplied index is not valid' }),content_type='application/json') + return HttpResponse( + json.dumps({"success": False, "error": "Supplied index is not valid"}), + content_type="application/json", + ) condition_slide_order(session) sp = session_slides.get(order=oldIndex) if oldIndex < newIndex: - session_slides.filter(order__gt=oldIndex, order__lte=newIndex).update(order=F('order')-1) + session_slides.filter(order__gt=oldIndex, order__lte=newIndex).update( + order=F("order") - 1 + ) else: - session_slides.filter(order__gte=newIndex, order__lt=oldIndex).update(order=F('order')+1) + session_slides.filter(order__gte=newIndex, order__lt=oldIndex).update( + order=F("order") + 1 + ) sp.order = newIndex sp.save() @@ -3174,7 +3351,7 @@ def ajax_reorder_slides_in_session(request, session_id, num): mgr = SlidesManager(api_config=settings.MEETECHO_API_CONFIG) mgr.send_update(session) - return HttpResponse(json.dumps({'success':True}), content_type='application/json') + return HttpResponse(json.dumps({"success": True}), content_type="application/json") @role_required('Secretariat') diff --git a/ietf/utils/meetecho.py b/ietf/utils/meetecho.py index a1829bbc5a..8c83c051e5 100644 --- a/ietf/utils/meetecho.py +++ b/ietf/utils/meetecho.py @@ -17,7 +17,7 @@ import datetime from json import JSONDecodeError from pprint import pformat -from typing import Dict, Optional, Sequence, TypedDict, TYPE_CHECKING, Union +from typing import Sequence, TypedDict, TYPE_CHECKING, Union from urllib.parse import urljoin # Guard against hypothetical cyclical import problems @@ -335,12 +335,12 @@ def _request(self, method, url, api_token=None, json=None): f">> MeetechoAPI: request(method={method},", f">> MeetechoAPI: url={url},", f">> MeetechoAPI: api_token={api_token},", - f">> MeetechoAPI: json=" + json_lines[0], + ">> MeetechoAPI: json=" + json_lines[0], ( ">> MeetechoAPI: " + - f"\n>> MeetechoAPI: ".join(l for l in json_lines[1:]) + "\n>> MeetechoAPI: ".join(l for l in json_lines[1:]) ), - f">> MeetechoAPI: )" + ">> MeetechoAPI: )" ] ) ) @@ -426,23 +426,15 @@ def delete(self): self._manager.delete_conference(self) -class APIConfigDict(TypedDict): - api_base: str # url - client_id: str - client_secret: str - request_timeout: Union[float, int] - debug: Optional[bool] - - class Manager: - def __init__(self, api_config: APIConfigDict): - debug = api_config.get("debug", False) - api_config = {k: v for k, v in api_config.items() if k != "debug"} + def __init__(self, api_config): + api_config_copy = api_config.copy() + debug = api_config_copy.pop("debug", False) if debug: - self.api = DebugMeetechoAPI(**api_config) + self.api = DebugMeetechoAPI(**api_config_copy) else: - self.api = MeetechoAPI(**api_config) - self.wg_tokens: Dict[str, str] = {} + self.api = MeetechoAPI(**api_config_copy) + self.wg_tokens = {} def wg_token(self, group): group_acronym = group.acronym if hasattr(group, "acronym") else group