From 25dc9686a4b770d389dec58d504bfdfe1048b787 Mon Sep 17 00:00:00 2001 From: Awais Qureshi Date: Mon, 14 Jul 2025 13:18:47 -0400 Subject: [PATCH] feat!: upgrading get_problem_responses api to DRF ( 34 ) (#35614) * feat!: upgrading api to DRF. --- lms/djangoapps/instructor/views/api.py | 125 +++++++++++--------- lms/djangoapps/instructor/views/api_urls.py | 2 +- 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 4a666727e2..9c22277fd1 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -115,7 +115,7 @@ from lms.djangoapps.instructor.views.serializer import ( UserSerializer, UniqueStudentIdentifierSerializer, ProblemResetSerializer, - RescoreEntranceExamSerializer + RescoreEntranceExamSerializer, ) from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted @@ -1277,66 +1277,77 @@ class ProblemResponseReportInitiate(DeveloperErrorViewMixin, APIView): ) -@transaction.non_atomic_requests -@require_POST -@ensure_csrf_cookie -@require_course_permission(permissions.CAN_RESEARCH) -def get_problem_responses(request, course_id): +@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch') +@method_decorator(transaction.non_atomic_requests, name='dispatch') +class GetProblemResponses(DeveloperErrorViewMixin, APIView): """ Initiate generation of a CSV file containing all student answers to a given problem. - - **Example requests** - - POST /courses/{course_id}/instructor/api/get_problem_responses { - "problem_location": "{usage_key1},{usage_key2},{usage_key3}"" - } - POST /courses/{course_id}/instructor/api/get_problem_responses { - "problem_location": "{usage_key}", - "problem_types_filter": "problem" - } - - **POST Parameters** - - A POST request can include the following parameters: - - * problem_location: A comma-separated list of usage keys for the blocks - to include in the report. If the location is a block that contains - other blocks, (such as the course, section, subsection, or unit blocks) - then all blocks under that block will be included in the report. - * problem_types_filter: Optional. A comma-separated list of block types - to include in the repot. If set, only blocks of the specified types will - be included in the report. - - To get data on all the poll and survey blocks in a course, you could - POST the usage key of the course for `problem_location`, and - "poll, survey" as the value for `problem_types_filter`. - - - **Example Response:** - If initiation is successful (or generation task is already running): - ```json - { - "status": "The problem responses report is being created. ...", - "task_id": "4e49522f-31d9-431a-9cff-dd2a2bf4c85a" - } - ``` - - Responds with BadRequest if any of the provided problem locations are faulty. """ - # A comma-separated list of problem locations - # The name of the POST parameter is `problem_location` (not pluralised) in - # order to preserve backwards compatibility with existing third-party - # scripts. - problem_locations = request.POST.get('problem_location', '').split(',') - # A comma-separated list of block types - problem_types_filter = request.POST.get('problem_types_filter') - return _get_problem_responses( - request, - course_id=course_id, - problem_locations=problem_locations, - problem_types_filter=problem_types_filter, - ) + + permission_classes = (IsAuthenticated, permissions.InstructorPermission) + permission_name = permissions.CAN_RESEARCH + + @method_decorator(ensure_csrf_cookie) + @method_decorator(transaction.non_atomic_requests) + def post(self, request, course_id): + """ + Initiate generation of a CSV file containing all student answers + to a given problem. + + **Example requests** + + POST /courses/{course_id}/instructor/api/get_problem_responses { + "problem_location": "{usage_key1},{usage_key2},{usage_key3}"" + } + POST /courses/{course_id}/instructor/api/get_problem_responses { + "problem_location": "{usage_key}", + "problem_types_filter": "problem" + } + + **POST Parameters** + + A POST request can include the following parameters: + + * problem_location: A comma-separated list of usage keys for the blocks + to include in the report. If the location is a block that contains + other blocks, (such as the course, section, subsection, or unit blocks) + then all blocks under that block will be included in the report. + * problem_types_filter: Optional. A comma-separated list of block types + to include in the repot. If set, only blocks of the specified types will + be included in the report. + + To get data on all the poll and survey blocks in a course, you could + POST the usage key of the course for `problem_location`, and + "poll, survey" as the value for `problem_types_filter`. + + + **Example Response:** + If initiation is successful (or generation task is already running): + ```json + { + "status": "The problem responses report is being created. ...", + "task_id": "4e49522f-31d9-431a-9cff-dd2a2bf4c85a" + } + ``` + + Responds with BadRequest if any of the provided problem locations are faulty. + """ + # A comma-separated list of problem locations + # The name of the POST parameter is `problem_location` (not pluralised) in + # order to preserve backwards compatibility with existing third-party + # scripts. + + problem_locations = request.POST.get('problem_location', '').split(',') + # A comma-separated list of block types + problem_types_filter = request.POST.get('problem_types_filter') + + return _get_problem_responses( + request, + course_id=course_id, + problem_locations=problem_locations, + problem_types_filter=problem_types_filter, + ) @cache_control(no_cache=True, no_store=True, must_revalidate=True) diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py index 9a9b7f1607..d1e13807b4 100644 --- a/lms/djangoapps/instructor/views/api_urls.py +++ b/lms/djangoapps/instructor/views/api_urls.py @@ -26,7 +26,7 @@ urlpatterns = [ path('list_course_role_members', api.ListCourseRoleMembersView.as_view(), name='list_course_role_members'), path('modify_access', api.ModifyAccess.as_view(), name='modify_access'), path('bulk_beta_modify_access', api.BulkBetaModifyAccess.as_view(), name='bulk_beta_modify_access'), - path('get_problem_responses', api.get_problem_responses, name='get_problem_responses'), + path('get_problem_responses', api.GetProblemResponses.as_view(), name='get_problem_responses'), path('get_issued_certificates/', api.GetIssuedCertificates.as_view(), name='get_issued_certificates'), re_path(r'^get_students_features(?P/csv)?$', api.GetStudentsFeatures.as_view(), name='get_students_features'), path('get_grading_config', api.GetGradingConfig.as_view(), name='get_grading_config'),