From e17d65f422b7d62984383926f004f43f17fb7e0d Mon Sep 17 00:00:00 2001 From: Zach Hancock Date: Fri, 3 Nov 2023 10:57:30 -0400 Subject: [PATCH] feat: roster endpoint POC --- edx_exams/apps/lti/urls.py | 1 + edx_exams/apps/lti/views.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/edx_exams/apps/lti/urls.py b/edx_exams/apps/lti/urls.py index 42a59b91..9c39053d 100644 --- a/edx_exams/apps/lti/urls.py +++ b/edx_exams/apps/lti/urls.py @@ -12,4 +12,5 @@ path('end_assessment/', views.end_assessment, name='end_assessment'), path('start_proctoring/', views.start_proctoring, name='start_proctoring'), path('exam//instructor_tool', views.launch_instructor_tool, name='instructor_tool'), + path('exam//instructor_tool/roster', views.exam_roster, name='exam_roster'), ] diff --git a/edx_exams/apps/lti/views.py b/edx_exams/apps/lti/views.py index 7742e0a9..a58a3fdf 100644 --- a/edx_exams/apps/lti/views.py +++ b/edx_exams/apps/lti/views.py @@ -7,9 +7,12 @@ from decimal import Decimal from urllib.parse import urljoin +from django.conf import settings from django.contrib.auth import login +from django.http import JsonResponse from django.shortcuts import redirect from django.urls import reverse +from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from lti_consumer.api import get_end_assessment_return, get_lti_1p3_launch_start_url @@ -25,6 +28,7 @@ from edx_exams.apps.core.api import ( get_attempt_by_id, get_attempt_for_user_with_attempt_number_and_resource_id, + get_exam_attempts, get_exam_by_id, get_exam_url_path, update_attempt_status @@ -337,6 +341,7 @@ def end_assessment(request, attempt_id): @authentication_classes((JwtAuthentication,)) @permission_classes((IsAuthenticated,)) def launch_instructor_tool(request, exam_id): + # pragma: no cover """ View to initiate an LTI launch of the Instructor Tool for an exam. """ @@ -360,6 +365,37 @@ def launch_instructor_tool(request, exam_id): resource_link_id=exam.resource_id, external_user_id=str(user.anonymous_user_id), context_id=exam.course_id, + custom_parameters={ + 'roster_url': settings.LTI_API_BASE + reverse('lti:exam_roster', kwargs={'exam_id': exam.id}), + } ) + # user is authenticated via JWT so use that to create a + # session with this service's authentication backend + request.user.backend = EDX_OAUTH_BACKEND + login(request, user) + return redirect(get_lti_1p3_launch_start_url(launch_data)) + + +@csrf_exempt +@require_http_methods(['GET']) +def exam_roster(request, exam_id): + """ + Temporary endpoint to prove we can authenticate this request properly + """ + user = request.user + exam = get_exam_by_id(exam_id) + if not user.is_staff and not user.has_course_staff_permission(exam.course_id): + return Response(status=status.HTTP_403_FORBIDDEN) + + attempts = get_exam_attempts(exam_id) + attempts.select_related('user') + + users = set(attempt.user for attempt in attempts) + roster = [ + (user.anonymous_user_id, user.username) + for user in users + ] + + return JsonResponse(roster, safe=False)