From 7c0485e43ff8050e064ac83e9f9e4971c106fd92 Mon Sep 17 00:00:00 2001 From: Fernando Date: Mon, 16 Oct 2023 16:26:20 -0400 Subject: [PATCH 1/7] feat: /api/ora_staff_grader/initialize upgraded feat: generate_assessment_data handler created fix: style and functions rename fix: test submission metadata serializers upgraded fix: style and some test and docs improvements fix: HTTPStatus conditional chore: update assessment_filter argument and update docstring chore: fix quality errors chore: remove inline comments and improve docstring of serializers refactor: rename assessment_filter to assessment_type --- lms/djangoapps/ora_staff_grader/constants.py | 1 + lms/djangoapps/ora_staff_grader/ora_api.py | 18 ++++- .../ora_staff_grader/serializers.py | 67 ++++++++++++++++++- .../tests/test_serializers.py | 10 +++ lms/djangoapps/ora_staff_grader/urls.py | 2 + lms/djangoapps/ora_staff_grader/views.py | 61 +++++++++++++++++ 6 files changed, 157 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/constants.py b/lms/djangoapps/ora_staff_grader/constants.py index d1ef62287b65..1ff67d79d26e 100644 --- a/lms/djangoapps/ora_staff_grader/constants.py +++ b/lms/djangoapps/ora_staff_grader/constants.py @@ -3,6 +3,7 @@ # Query params PARAM_ORA_LOCATION = "oraLocation" PARAM_SUBMISSION_ID = "submissionUUID" +PARAM_ASSESSMENT_TYPE = "assessmentType" # Error codes ERR_UNKNOWN = "ERR_UNKNOWN" diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index 852f1a707bcf..432b6b817c11 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -12,6 +12,7 @@ These are checked (usually by checking for a {"success":false} response) and raise errors, possibly with extra context. """ import json +from http import HTTPStatus from lms.djangoapps.ora_staff_grader.errors import ( LockContestedError, XBlockInternalError, @@ -33,6 +34,21 @@ def get_submissions(request, usage_id): return json.loads(response.content) +def get_assessments(request, usage_id, submission_uuid, assessment_type): + """ + Get a list of assessments from the ORA's 'list_assessments_grades' XBlock.json_handler + """ + handler_name = "list_assessments" + body = {"item_id": usage_id, "submission_uuid": submission_uuid, "assessment_type": assessment_type} + + response = call_xblock_json_handler(request, usage_id, handler_name, body) + + if response.status_code != HTTPStatus.OK: + raise XBlockInternalError(context={"handler": handler_name}) + + return json.loads(response.content) + + def get_submission_info(request, usage_id, submission_uuid): """ Get submission content from ORA 'get_submission_info' XBlock.json_handler @@ -168,5 +184,5 @@ def batch_delete_submission_locks(request, usage_id, submission_uuids): response = call_xblock_json_handler(request, usage_id, handler_name, body) # Errors should raise a blanket exception. Otherwise body is empty, 200 is implicit success - if response.status_code != 200: + if response.status_code != HTTPStatus.OK: raise XBlockInternalError(context={"handler": handler_name}) diff --git a/lms/djangoapps/ora_staff_grader/serializers.py b/lms/djangoapps/ora_staff_grader/serializers.py index d3ffb46f1933..a6db93200154 100644 --- a/lms/djangoapps/ora_staff_grader/serializers.py +++ b/lms/djangoapps/ora_staff_grader/serializers.py @@ -135,11 +135,13 @@ class ScoreSerializer(serializers.Serializer): class SubmissionMetadataSerializer(serializers.Serializer): """ - Submission metadata for displaying submissions table in ESG + Submission metadata for displaying submissions table in Enhanced Staff Grader (ESG) """ submissionUUID = serializers.CharField(source="submissionUuid") username = serializers.CharField(allow_null=True) + email = serializers.CharField(allow_null=True) + fullname = serializers.CharField(allow_null=True) teamName = serializers.CharField(allow_null=True) dateSubmitted = serializers.DateTimeField() dateGraded = serializers.DateTimeField(allow_null=True) @@ -159,6 +161,56 @@ class Meta: "gradeStatus", "lockStatus", "score", + "email", + "fullname", + ] + read_only_fields = fields + + +class AssessmentScoresSerializer(serializers.Serializer): + """ + Score information associated with a specific assessment. + + This serializer was intended for displaying assessment information in the Enhanced Staff Grader (ESG). + """ + criterion_name = serializers.CharField() + score_earned = serializers.IntegerField() + score_type = serializers.CharField() + + class Meta: + fields = [ + "criterion_name", + "score_earned", + "score_type", + ] + read_only_fields = fields + + +class AssessmentSerializer(serializers.Serializer): + """ + Data for displaying Assessment objects for the response from the assessment + feedback endpoint in Enhanced Staff Grader (ESG) + This serializer is included in the AssessmentFeedbackSerializer as a ListField + """ + assessment_id = serializers.CharField() + scorer_name = serializers.CharField(allow_null=True) + scorer_username = serializers.CharField(allow_null=True) + scorer_email = serializers.CharField(allow_null=True) + assesment_date = serializers.DateTimeField() + assesment_scores = serializers.ListField(child=AssessmentScoresSerializer()) + problem_step = serializers.CharField(allow_null=True) + feedback = serializers.CharField(allow_null=True) + + class Meta: + fields = [ + "assessment_id", + "scorer_name", + "scorer_username", + "scorer_email", + "assesment_date", + "assesment_scores", + "problem_step", + "feedback", ] read_only_fields = fields @@ -190,6 +242,19 @@ def get_isEnabled(self, obj): return obj['isEnabled'] and not obj['oraMetadata'].teams_enabled +class AssessmentFeedbackSerializer(serializers.Serializer): + """ + Serialize info for every assessment for the response from the assessment + feedback endpoint in Enhanced Staff Grader (ESG) + """ + + assessments = serializers.ListField(child=AssessmentSerializer()) + + class Meta: + fields = ["assessments", ] + read_only_fields = fields + + class UploadedFileSerializer(serializers.Serializer): """Serializer for a file uploaded as a part of a response""" diff --git a/lms/djangoapps/ora_staff_grader/tests/test_serializers.py b/lms/djangoapps/ora_staff_grader/tests/test_serializers.py index d105cd649e89..d6f4abcd5b83 100644 --- a/lms/djangoapps/ora_staff_grader/tests/test_serializers.py +++ b/lms/djangoapps/ora_staff_grader/tests/test_serializers.py @@ -241,6 +241,8 @@ class TestSubmissionMetadataSerializer(TestCase): "submissionUuid": "a", "username": "foo", "teamName": "", + 'email': "jondoes@example.com", + 'fullname': "", "dateSubmitted": "1969-07-16 13:32:00", "dateGraded": "None", "gradedBy": "", @@ -251,6 +253,8 @@ class TestSubmissionMetadataSerializer(TestCase): "b": { "submissionUuid": "b", "username": "", + 'email': "jondoes@example.com", + 'fullname': "Jhon Does", "teamName": "bar", "dateSubmitted": "1969-07-20 20:17:40", "dateGraded": "None", @@ -262,6 +266,8 @@ class TestSubmissionMetadataSerializer(TestCase): "c": { "submissionUuid": "c", "username": "baz", + 'email': "jondoes@example.com", + 'fullname': "Jhon Does", "teamName": "", "dateSubmitted": "1969-07-21 21:35:00", "dateGraded": "1969-07-24 16:44:00", @@ -293,6 +299,8 @@ def test_empty_score(self): submission = { "submissionUuid": "empty-score", "username": "WOPR", + 'email': "jhondoes@example.com", + 'fullname': "", "dateSubmitted": "1983-06-03 00:00:00", "dateGraded": None, "gradedBy": None, @@ -304,6 +312,8 @@ def test_empty_score(self): expected_output = { "submissionUUID": "empty-score", "username": "WOPR", + 'email': "jhondoes@example.com", + 'fullname': "", "teamName": None, "dateSubmitted": "1983-06-03 00:00:00", "dateGraded": None, diff --git a/lms/djangoapps/ora_staff_grader/urls.py b/lms/djangoapps/ora_staff_grader/urls.py index a678009ccfbf..fd21bd2e42b6 100644 --- a/lms/djangoapps/ora_staff_grader/urls.py +++ b/lms/djangoapps/ora_staff_grader/urls.py @@ -13,6 +13,7 @@ SubmissionLockView, SubmissionStatusFetchView, UpdateGradeView, + AssessmentFeedbackView, ) @@ -22,6 +23,7 @@ urlpatterns += [ path("mock/", include("lms.djangoapps.ora_staff_grader.mock.urls")), path("initialize", InitializeView.as_view(), name="initialize"), + path("assessments/feedback", AssessmentFeedbackView.as_view(), name="assessment-feedback"), path("submission/batch/unlock", SubmissionBatchUnlockView.as_view(), name="batch-unlock"), path("submission/files", SubmissionFilesFetchView.as_view(), name="fetch-files"), path( diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index c68bb65a3068..90d09ed4cbf7 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -24,6 +24,7 @@ from lms.djangoapps.ora_staff_grader.constants import ( PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID, + PARAM_ASSESSMENT_TYPE, ) from lms.djangoapps.ora_staff_grader.errors import ( BadOraLocationResponse, @@ -43,11 +44,13 @@ get_assessment_info, get_submission_info, get_submissions, + get_assessments, submit_grade, ) from lms.djangoapps.ora_staff_grader.serializers import ( FileListSerializer, InitializeSerializer, + AssessmentFeedbackSerializer, LockStatusSerializer, StaffAssessSerializer, SubmissionFetchSerializer, @@ -147,6 +150,64 @@ def get(self, request, ora_location, *args, **kwargs): return UnknownErrorResponse() +class AssessmentFeedbackView(StaffGraderBaseView): + """ + GET data about Assessments by submission_uuid and ora_location + + Response: { + assessments (list of dict): [ + { + "assessment_id: (string) assessment id + "scorer_name: (string) scorer name + "scorer_username: (string) scorer username + "scorer_email: (string) scorer email + "assessment_date: (string) assessment date + "assessment_scores (list of dict) [ + { + "criterion_name: (string) criterion name + "score_earned: (int) score earned + "score_type: (string) score type + } + ] + "problem_step (string) problem step (Self, Peer, or Staff) + "feedback: (string) feedback + } + ] + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - BadOraLocationResponse (HTTP 400) for bad ORA location + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID, PARAM_ASSESSMENT_TYPE]) + def get(self, request, ora_location, submission_uuid, assessment_type, *args, **kwargs): + + try: + assessments_data = { + "assessments": get_assessments( + request, ora_location, submission_uuid, assessment_type + ) + } + + response_data = AssessmentFeedbackSerializer(assessments_data).data + return Response(response_data) + + except (InvalidKeyError, ItemNotFoundError): + log.error(f"Bad ORA location provided: {ora_location}") + return BadOraLocationResponse() + + except XBlockInternalError as ex: + log.error(ex) + return InternalErrorResponse(context=ex.context) + + except Exception as ex: + log.exception(ex) + return UnknownErrorResponse() + + class SubmissionFetchView(StaffGraderBaseView): """ GET submission contents and assessment info, if any From 945dd6e65d2527e55dc82909952ba024b37e2077 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 14 Feb 2024 18:12:31 -0500 Subject: [PATCH 2/7] refactor: separate feedback endpoint --- lms/djangoapps/ora_staff_grader/constants.py | 1 - lms/djangoapps/ora_staff_grader/ora_api.py | 27 ++++-- lms/djangoapps/ora_staff_grader/urls.py | 6 +- lms/djangoapps/ora_staff_grader/views.py | 91 +++++++++++++++----- 4 files changed, 94 insertions(+), 31 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/constants.py b/lms/djangoapps/ora_staff_grader/constants.py index 1ff67d79d26e..d1ef62287b65 100644 --- a/lms/djangoapps/ora_staff_grader/constants.py +++ b/lms/djangoapps/ora_staff_grader/constants.py @@ -3,7 +3,6 @@ # Query params PARAM_ORA_LOCATION = "oraLocation" PARAM_SUBMISSION_ID = "submissionUUID" -PARAM_ASSESSMENT_TYPE = "assessmentType" # Error codes ERR_UNKNOWN = "ERR_UNKNOWN" diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index 432b6b817c11..ea0868caee31 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -34,14 +34,29 @@ def get_submissions(request, usage_id): return json.loads(response.content) -def get_assessments(request, usage_id, submission_uuid, assessment_type): +def get_given_assessments(request, usage_id, submission_uuid): """ - Get a list of assessments from the ORA's 'list_assessments_grades' XBlock.json_handler + Get a list of given assessments from the ORA's 'list_given_assessments' XBlock.json_handler """ - handler_name = "list_assessments" - body = {"item_id": usage_id, "submission_uuid": submission_uuid, "assessment_type": assessment_type} + handler_name = "list_given_assessments" + data = {"item_id": usage_id, "submission_uuid": submission_uuid} + + response = call_xblock_json_handler(request, usage_id, handler_name, data) + + if response.status_code != HTTPStatus.OK: + raise XBlockInternalError(context={"handler": handler_name}) + + return json.loads(response.content) - response = call_xblock_json_handler(request, usage_id, handler_name, body) + +def get_received_assessments(request, usage_id, submission_uuid): + """ + Get a list of received assessments from the ORA's 'list_received_assessments' XBlock.json_handler + """ + handler_name = "list_received_assessments" + data = {"submission_uuid": submission_uuid} + + response = call_xblock_json_handler(request, usage_id, handler_name, data) if response.status_code != HTTPStatus.OK: raise XBlockInternalError(context={"handler": handler_name}) @@ -184,5 +199,5 @@ def batch_delete_submission_locks(request, usage_id, submission_uuids): response = call_xblock_json_handler(request, usage_id, handler_name, body) # Errors should raise a blanket exception. Otherwise body is empty, 200 is implicit success - if response.status_code != HTTPStatus.OK: + if response.status_code != 200: raise XBlockInternalError(context={"handler": handler_name}) diff --git a/lms/djangoapps/ora_staff_grader/urls.py b/lms/djangoapps/ora_staff_grader/urls.py index fd21bd2e42b6..c7ec2e19fe2a 100644 --- a/lms/djangoapps/ora_staff_grader/urls.py +++ b/lms/djangoapps/ora_staff_grader/urls.py @@ -13,7 +13,8 @@ SubmissionLockView, SubmissionStatusFetchView, UpdateGradeView, - AssessmentFeedbackView, + AssessmentGivenFeedbackView, + AssessmentReceivedFeedbackView, ) @@ -23,7 +24,8 @@ urlpatterns += [ path("mock/", include("lms.djangoapps.ora_staff_grader.mock.urls")), path("initialize", InitializeView.as_view(), name="initialize"), - path("assessments/feedback", AssessmentFeedbackView.as_view(), name="assessment-feedback"), + path("assessments/feedback/from", AssessmentReceivedFeedbackView.as_view(), name="assessment-received-feedback"), + path("assessments/feedback/to", AssessmentGivenFeedbackView.as_view(), name="assessment-given-feedback"), path("submission/batch/unlock", SubmissionBatchUnlockView.as_view(), name="batch-unlock"), path("submission/files", SubmissionFilesFetchView.as_view(), name="fetch-files"), path( diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index 90d09ed4cbf7..7d7733f68482 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -24,7 +24,6 @@ from lms.djangoapps.ora_staff_grader.constants import ( PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID, - PARAM_ASSESSMENT_TYPE, ) from lms.djangoapps.ora_staff_grader.errors import ( BadOraLocationResponse, @@ -44,7 +43,8 @@ get_assessment_info, get_submission_info, get_submissions, - get_assessments, + get_given_assessments, + get_received_assessments, submit_grade, ) from lms.djangoapps.ora_staff_grader.serializers import ( @@ -150,27 +150,27 @@ def get(self, request, ora_location, *args, **kwargs): return UnknownErrorResponse() -class AssessmentFeedbackView(StaffGraderBaseView): +class AssessmentGivenFeedbackView(StaffGraderBaseView): """ - GET data about Assessments by submission_uuid and ora_location + GET data about assessments given by a user. Response: { - assessments (list of dict): [ + assessments (List[dict]): [ { - "assessment_id: (string) assessment id - "scorer_name: (string) scorer name - "scorer_username: (string) scorer username - "scorer_email: (string) scorer email - "assessment_date: (string) assessment date - "assessment_scores (list of dict) [ + "assessment_id: (str) assessment id + "scorer_name: (str) scorer name + "scorer_username: (str) scorer username + "scorer_email: (str) scorer email + "assessment_date: (str) assessment date + "assessment_scores (List[dict]) [ { - "criterion_name: (string) criterion name + "criterion_name: (str) criterion name "score_earned: (int) score earned - "score_type: (string) score type + "score_type: (str) score type } ] - "problem_step (string) problem step (Self, Peer, or Staff) - "feedback: (string) feedback + "problem_step (str) problem step (Self, Peer, or Staff) + "feedback: (str) feedback } ] } @@ -181,17 +181,64 @@ class AssessmentFeedbackView(StaffGraderBaseView): - XBlockInternalError (HTTP 500) for an issue with ORA - UnknownError (HTTP 500) for other errors """ + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def get(self, request, ora_location, submission_uuid, *args, **kwargs): + """ Get data about assessments given by a user""" + try: + assessments_data = {"assessments": get_given_assessments(request, ora_location, submission_uuid)} + response_data = AssessmentFeedbackSerializer(assessments_data).data + return Response(response_data) - @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID, PARAM_ASSESSMENT_TYPE]) - def get(self, request, ora_location, submission_uuid, assessment_type, *args, **kwargs): + except (InvalidKeyError, ItemNotFoundError): + log.error(f"Bad ORA location provided: {ora_location}") + return BadOraLocationResponse() - try: - assessments_data = { - "assessments": get_assessments( - request, ora_location, submission_uuid, assessment_type - ) + except XBlockInternalError as ex: + log.error(ex) + return InternalErrorResponse(context=ex.context) + + except Exception as ex: + log.exception(ex) + return UnknownErrorResponse() + + +class AssessmentReceivedFeedbackView(StaffGraderBaseView): + """ + GET data about assessments received by a user. + + Response: { + assessments (List[dict]): [ + { + "assessment_id: (str) assessment id + "scorer_name: (str) scorer name + "scorer_username: (str) scorer username + "scorer_email: (str) scorer email + "assessment_date: (str) assessment date + "assessment_scores (List[dict]) [ + { + "criterion_name: (str) criterion name + "score_earned: (int) score earned + "score_type: (str) score type + } + ] + "problem_step (str) problem step (Self, Peer, or Staff) + "feedback: (str) feedback } + ] + } + Errors: + - MissingParamResponse (HTTP 400) for missing params + - BadOraLocationResponse (HTTP 400) for bad ORA location + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def get(self, request, ora_location, submission_uuid, *args, **kwargs): + """Get data about assessments received by a user""" + try: + assessments_data = {"assessments": get_received_assessments(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) From 8bbdaec7ff17aff8a52b61f954eef612372a702b Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 15 Feb 2024 09:44:28 -0500 Subject: [PATCH 3/7] chore: rename functions --- lms/djangoapps/ora_staff_grader/ora_api.py | 12 ++++++------ lms/djangoapps/ora_staff_grader/views.py | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index ea0868caee31..36b15b5f99d4 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -34,11 +34,11 @@ def get_submissions(request, usage_id): return json.loads(response.content) -def get_given_assessments(request, usage_id, submission_uuid): +def get_assessments_given(request, usage_id, submission_uuid): """ - Get a list of given assessments from the ORA's 'list_given_assessments' XBlock.json_handler + Get a list of assessments given from the ORA's 'list_assessments_given' XBlock.json_handler """ - handler_name = "list_given_assessments" + handler_name = "list_assessments_given" data = {"item_id": usage_id, "submission_uuid": submission_uuid} response = call_xblock_json_handler(request, usage_id, handler_name, data) @@ -49,11 +49,11 @@ def get_given_assessments(request, usage_id, submission_uuid): return json.loads(response.content) -def get_received_assessments(request, usage_id, submission_uuid): +def get_assessments_received(request, usage_id, submission_uuid): """ - Get a list of received assessments from the ORA's 'list_received_assessments' XBlock.json_handler + Get a list of assessments received from the ORA's 'list_assessments_received' XBlock.json_handler """ - handler_name = "list_received_assessments" + handler_name = "list_assessments_received" data = {"submission_uuid": submission_uuid} response = call_xblock_json_handler(request, usage_id, handler_name, data) diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index 7d7733f68482..de9b98775c65 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -43,8 +43,8 @@ get_assessment_info, get_submission_info, get_submissions, - get_given_assessments, - get_received_assessments, + get_assessments_given, + get_assessments_received, submit_grade, ) from lms.djangoapps.ora_staff_grader.serializers import ( @@ -152,7 +152,11 @@ def get(self, request, ora_location, *args, **kwargs): class AssessmentGivenFeedbackView(StaffGraderBaseView): """ - GET data about assessments given by a user. + GET data about assessments given by a user in a submission + + * Query Params: + - oraLocation (str): ORA location for XBlock handling + - submissionUUID (str): A submission to get assessments for Response: { assessments (List[dict]): [ @@ -185,7 +189,7 @@ class AssessmentGivenFeedbackView(StaffGraderBaseView): def get(self, request, ora_location, submission_uuid, *args, **kwargs): """ Get data about assessments given by a user""" try: - assessments_data = {"assessments": get_given_assessments(request, ora_location, submission_uuid)} + assessments_data = {"assessments": get_assessments_given(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) @@ -204,7 +208,11 @@ def get(self, request, ora_location, submission_uuid, *args, **kwargs): class AssessmentReceivedFeedbackView(StaffGraderBaseView): """ - GET data about assessments received by a user. + GET data about assessments received by a user in a submission + + * Query Params: + - oraLocation (str): ORA location for XBlock handling + - submissionUUID (str): A submission to get assessments for Response: { assessments (List[dict]): [ @@ -238,7 +246,7 @@ class AssessmentReceivedFeedbackView(StaffGraderBaseView): def get(self, request, ora_location, submission_uuid, *args, **kwargs): """Get data about assessments received by a user""" try: - assessments_data = {"assessments": get_received_assessments(request, ora_location, submission_uuid)} + assessments_data = {"assessments": get_assessments_received(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) From f9d671ef996e14327f9529418c74ea8684f5bc7f Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 15 Feb 2024 16:14:01 -0500 Subject: [PATCH 4/7] chore: address PR review --- lms/djangoapps/ora_staff_grader/ora_api.py | 40 ++++++++--- .../ora_staff_grader/serializers.py | 25 +++---- lms/djangoapps/ora_staff_grader/urls.py | 8 +-- lms/djangoapps/ora_staff_grader/views.py | 70 ++++++++++--------- 4 files changed, 85 insertions(+), 58 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index 36b15b5f99d4..aac71663e892 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -13,6 +13,9 @@ """ import json from http import HTTPStatus + +from rest_framework.request import Request + from lms.djangoapps.ora_staff_grader.errors import ( LockContestedError, XBlockInternalError, @@ -34,12 +37,23 @@ def get_submissions(request, usage_id): return json.loads(response.content) -def get_assessments_given(request, usage_id, submission_uuid): +def get_assessments_to(request: Request, usage_id: str, submission_uuid: str): """ - Get a list of assessments given from the ORA's 'list_assessments_given' XBlock.json_handler + Get a list of assessments given from ORA `list_assessments_to` XBlock.json_handler. + + Lists all assessments given by a user (according to their submissionUUID) + in an ORA assignment. Assessments can be Self, Peer and Staff. + + Args: + request (Request): The request object + usage_id (str): Usage ID of the XBlock for running the handler + submission_uuid (str): The ORA submission UUID """ - handler_name = "list_assessments_given" - data = {"item_id": usage_id, "submission_uuid": submission_uuid} + handler_name = "list_assessments_to" + data = { + "item_id": usage_id, + "submission_uuid": submission_uuid, + } response = call_xblock_json_handler(request, usage_id, handler_name, data) @@ -49,12 +63,22 @@ def get_assessments_given(request, usage_id, submission_uuid): return json.loads(response.content) -def get_assessments_received(request, usage_id, submission_uuid): +def get_assessments_from(request: Request, usage_id: str, submission_uuid: str): """ - Get a list of assessments received from the ORA's 'list_assessments_received' XBlock.json_handler + Get a list of assessments received from ORA `list_assessments_from` XBlock.json_handler + + Lists all assessments received by a user (according to their submissionUUID) + in an ORA assignment. Assessments can be Self, Peer and Staff. + + Args: + request (Request): The request object + usage_id (str): Usage ID of the XBlock for running the handler + submission_uuid (str): The ORA submission UUID """ - handler_name = "list_assessments_received" - data = {"submission_uuid": submission_uuid} + handler_name = "list_assessments_from" + data = { + "submission_uuid": submission_uuid, + } response = call_xblock_json_handler(request, usage_id, handler_name, data) diff --git a/lms/djangoapps/ora_staff_grader/serializers.py b/lms/djangoapps/ora_staff_grader/serializers.py index a6db93200154..525fe7cc393e 100644 --- a/lms/djangoapps/ora_staff_grader/serializers.py +++ b/lms/djangoapps/ora_staff_grader/serializers.py @@ -169,9 +169,9 @@ class Meta: class AssessmentScoresSerializer(serializers.Serializer): """ - Score information associated with a specific assessment. + Serializer for score information associated with a specific assessment. - This serializer was intended for displaying assessment information in the Enhanced Staff Grader (ESG). + This serializer is included in the `AssessmentSerializer` as a `ListField` """ criterion_name = serializers.CharField() score_earned = serializers.IntegerField() @@ -188,16 +188,17 @@ class Meta: class AssessmentSerializer(serializers.Serializer): """ - Data for displaying Assessment objects for the response from the assessment - feedback endpoint in Enhanced Staff Grader (ESG) - This serializer is included in the AssessmentFeedbackSerializer as a ListField + Serializer for the each assessment metadata in the response from the assessments + feedback endpoints (from/to) in Enhanced Staff Grader (ESG) + + This serializer is included in the `AssessmentFeedbackSerializer` as a `ListField` """ assessment_id = serializers.CharField() scorer_name = serializers.CharField(allow_null=True) scorer_username = serializers.CharField(allow_null=True) scorer_email = serializers.CharField(allow_null=True) - assesment_date = serializers.DateTimeField() - assesment_scores = serializers.ListField(child=AssessmentScoresSerializer()) + assessment_date = serializers.DateTimeField() + assessment_scores = serializers.ListField(child=AssessmentScoresSerializer()) problem_step = serializers.CharField(allow_null=True) feedback = serializers.CharField(allow_null=True) @@ -207,8 +208,8 @@ class Meta: "scorer_name", "scorer_username", "scorer_email", - "assesment_date", - "assesment_scores", + "assessment_date", + "assessment_scores", "problem_step", "feedback", ] @@ -244,14 +245,14 @@ def get_isEnabled(self, obj): class AssessmentFeedbackSerializer(serializers.Serializer): """ - Serialize info for every assessment for the response from the assessment - feedback endpoint in Enhanced Staff Grader (ESG) + Serializer for a list of assessments for the response from the assessments + feedback endpoints (from/to) in Enhanced Staff Grader (ESG) """ assessments = serializers.ListField(child=AssessmentSerializer()) class Meta: - fields = ["assessments", ] + fields = ["assessments"] read_only_fields = fields diff --git a/lms/djangoapps/ora_staff_grader/urls.py b/lms/djangoapps/ora_staff_grader/urls.py index c7ec2e19fe2a..8d0b4e11eca1 100644 --- a/lms/djangoapps/ora_staff_grader/urls.py +++ b/lms/djangoapps/ora_staff_grader/urls.py @@ -13,8 +13,8 @@ SubmissionLockView, SubmissionStatusFetchView, UpdateGradeView, - AssessmentGivenFeedbackView, - AssessmentReceivedFeedbackView, + AssessmentFeedbackToView, + AssessmentFeedbackFromView, ) @@ -24,8 +24,8 @@ urlpatterns += [ path("mock/", include("lms.djangoapps.ora_staff_grader.mock.urls")), path("initialize", InitializeView.as_view(), name="initialize"), - path("assessments/feedback/from", AssessmentReceivedFeedbackView.as_view(), name="assessment-received-feedback"), - path("assessments/feedback/to", AssessmentGivenFeedbackView.as_view(), name="assessment-given-feedback"), + path("assessments/feedback/from", AssessmentFeedbackFromView.as_view(), name="assessment-feedback-from"), + path("assessments/feedback/to", AssessmentFeedbackToView.as_view(), name="assessment-feedback-to"), path("submission/batch/unlock", SubmissionBatchUnlockView.as_view(), name="batch-unlock"), path("submission/files", SubmissionFilesFetchView.as_view(), name="fetch-files"), path( diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index de9b98775c65..b6b3f01be6d7 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -43,8 +43,8 @@ get_assessment_info, get_submission_info, get_submissions, - get_assessments_given, - get_assessments_received, + get_assessments_to, + get_assessments_from, submit_grade, ) from lms.djangoapps.ora_staff_grader.serializers import ( @@ -150,31 +150,32 @@ def get(self, request, ora_location, *args, **kwargs): return UnknownErrorResponse() -class AssessmentGivenFeedbackView(StaffGraderBaseView): +class AssessmentFeedbackToView(StaffGraderBaseView): """ - GET data about assessments given by a user in a submission + (GET) List all assessments given by a user (according to + their submissionUUID) in an ORA assignment. - * Query Params: + **Query Params**: - oraLocation (str): ORA location for XBlock handling - - submissionUUID (str): A submission to get assessments for + - submissionUUID (str): The ORA submission UUID Response: { assessments (List[dict]): [ { - "assessment_id: (str) assessment id - "scorer_name: (str) scorer name - "scorer_username: (str) scorer username - "scorer_email: (str) scorer email - "assessment_date: (str) assessment date + "assessment_id: (str) Assessment id + "scorer_name: (str) Scorer name + "scorer_username: (str) Scorer username + "scorer_email: (str) Scorer email + "assessment_date: (str) Assessment date "assessment_scores (List[dict]) [ { - "criterion_name: (str) criterion name - "score_earned: (int) score earned - "score_type: (str) score type + "criterion_name: (str) Criterion name + "score_earned: (int) Score earned + "score_type: (str) Score type } ] - "problem_step (str) problem step (Self, Peer, or Staff) - "feedback: (str) feedback + "problem_step (str) Problem step (Self, Peer, or Staff) + "feedback: (str) Feedback of the assessment } ] } @@ -187,9 +188,9 @@ class AssessmentGivenFeedbackView(StaffGraderBaseView): """ @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) def get(self, request, ora_location, submission_uuid, *args, **kwargs): - """ Get data about assessments given by a user""" + """Get assessments given by a user in an ORA assignment""" try: - assessments_data = {"assessments": get_assessments_given(request, ora_location, submission_uuid)} + assessments_data = {"assessments": get_assessments_to(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) @@ -206,31 +207,32 @@ def get(self, request, ora_location, submission_uuid, *args, **kwargs): return UnknownErrorResponse() -class AssessmentReceivedFeedbackView(StaffGraderBaseView): +class AssessmentFeedbackFromView(StaffGraderBaseView): """ - GET data about assessments received by a user in a submission + (GET) List all assessments received by a user (according to + their submissionUUID) in an ORA assignment. - * Query Params: + **Query Params**: - oraLocation (str): ORA location for XBlock handling - - submissionUUID (str): A submission to get assessments for + - submissionUUID (str): The ORA submission UUID Response: { assessments (List[dict]): [ { - "assessment_id: (str) assessment id - "scorer_name: (str) scorer name - "scorer_username: (str) scorer username - "scorer_email: (str) scorer email - "assessment_date: (str) assessment date + "assessment_id: (str) Assessment id + "scorer_name: (str) Scorer name + "scorer_username: (str) Scorer username + "scorer_email: (str) Scorer email + "assessment_date: (str) Assessment date "assessment_scores (List[dict]) [ { - "criterion_name: (str) criterion name - "score_earned: (int) score earned - "score_type: (str) score type + "criterion_name: (str) Criterion name + "score_earned: (int) Score earned + "score_type: (str) Score type } ] - "problem_step (str) problem step (Self, Peer, or Staff) - "feedback: (str) feedback + "problem_step (str) Problem step (Self, Peer, or Staff) + "feedback: (str) Feedback of the assessment } ] } @@ -244,9 +246,9 @@ class AssessmentReceivedFeedbackView(StaffGraderBaseView): @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) def get(self, request, ora_location, submission_uuid, *args, **kwargs): - """Get data about assessments received by a user""" + """Get assessments received by a user in an ORA assignment""" try: - assessments_data = {"assessments": get_assessments_received(request, ora_location, submission_uuid)} + assessments_data = {"assessments": get_assessments_from(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) From ecb3fe1de4aa7538d11a6117b157bdfdfdd8a3a8 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 16 Feb 2024 18:32:54 -0500 Subject: [PATCH 5/7] refactor: use a single view --- lms/djangoapps/ora_staff_grader/urls.py | 13 +- lms/djangoapps/ora_staff_grader/views.py | 149 ++++++++++------------- 2 files changed, 71 insertions(+), 91 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/urls.py b/lms/djangoapps/ora_staff_grader/urls.py index 8d0b4e11eca1..1acfcfa5dcf8 100644 --- a/lms/djangoapps/ora_staff_grader/urls.py +++ b/lms/djangoapps/ora_staff_grader/urls.py @@ -3,7 +3,7 @@ """ from django.urls import include from django.urls import path - +from rest_framework.routers import DefaultRouter from lms.djangoapps.ora_staff_grader.views import ( InitializeView, @@ -13,19 +13,18 @@ SubmissionLockView, SubmissionStatusFetchView, UpdateGradeView, - AssessmentFeedbackToView, - AssessmentFeedbackFromView, + AssessmentFeedbackView, ) - urlpatterns = [] app_name = "ora-staff-grader" +router = DefaultRouter() +router.register("assessments/feedback", AssessmentFeedbackView, basename="assessment-feedback") + urlpatterns += [ path("mock/", include("lms.djangoapps.ora_staff_grader.mock.urls")), path("initialize", InitializeView.as_view(), name="initialize"), - path("assessments/feedback/from", AssessmentFeedbackFromView.as_view(), name="assessment-feedback-from"), - path("assessments/feedback/to", AssessmentFeedbackToView.as_view(), name="assessment-feedback-to"), path("submission/batch/unlock", SubmissionBatchUnlockView.as_view(), name="batch-unlock"), path("submission/files", SubmissionFilesFetchView.as_view(), name="fetch-files"), path( @@ -37,3 +36,5 @@ path("submission/grade", UpdateGradeView.as_view(), name="update-grade"), path("submission", SubmissionFetchView.as_view(), name="fetch-submission"), ] + +urlpatterns += router.urls diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index b6b3f01be6d7..8ab71116bb26 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -7,6 +7,7 @@ # NOTE: we intentionally add extra args using @require_params # pylint: disable=arguments-differ import logging +from typing import Callable from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import ( @@ -15,9 +16,12 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import UsageKey from openassessment.xblock.config_mixin import WAFFLE_NAMESPACE, ENHANCED_STAFF_GRADER +from rest_framework.decorators import action from rest_framework.generics import RetrieveAPIView from rest_framework.permissions import IsAuthenticated +from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.viewsets import ViewSet from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -150,105 +154,80 @@ def get(self, request, ora_location, *args, **kwargs): return UnknownErrorResponse() -class AssessmentFeedbackToView(StaffGraderBaseView): +class AssessmentFeedbackView(StaffGraderBaseView, ViewSet): """ - (GET) List all assessments given by a user (according to - their submissionUUID) in an ORA assignment. + View for fetching assessment feedback for a submission. - **Query Params**: - - oraLocation (str): ORA location for XBlock handling - - submissionUUID (str): The ORA submission UUID - - Response: { - assessments (List[dict]): [ - { - "assessment_id: (str) Assessment id - "scorer_name: (str) Scorer name - "scorer_username: (str) Scorer username - "scorer_email: (str) Scorer email - "assessment_date: (str) Assessment date - "assessment_scores (List[dict]) [ - { - "criterion_name: (str) Criterion name - "score_earned: (int) Score earned - "score_type: (str) Score type - } - ] - "problem_step (str) Problem step (Self, Peer, or Staff) - "feedback: (str) Feedback of the assessment - } - ] - } - - Errors: - - MissingParamResponse (HTTP 400) for missing params - - BadOraLocationResponse (HTTP 400) for bad ORA location - - XBlockInternalError (HTTP 500) for an issue with ORA - - UnknownError (HTTP 500) for other errors - """ - @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) - def get(self, request, ora_location, submission_uuid, *args, **kwargs): - """Get assessments given by a user in an ORA assignment""" - try: - assessments_data = {"assessments": get_assessments_to(request, ora_location, submission_uuid)} - response_data = AssessmentFeedbackSerializer(assessments_data).data - return Response(response_data) - - except (InvalidKeyError, ItemNotFoundError): - log.error(f"Bad ORA location provided: {ora_location}") - return BadOraLocationResponse() + **Methods** - except XBlockInternalError as ex: - log.error(ex) - return InternalErrorResponse(context=ex.context) - - except Exception as ex: - log.exception(ex) - return UnknownErrorResponse() + * (GET) api/ora_staff_grader/assessments/feedback/from + List all assessments received by a user (according to + their submissionUUID) in an ORA assignment. - -class AssessmentFeedbackFromView(StaffGraderBaseView): - """ - (GET) List all assessments received by a user (according to - their submissionUUID) in an ORA assignment. + * (GET) api/ora_staff_grader/assessments/feedback/to + List all assessments given by a user (according to + their submissionUUID) in an ORA assignment. **Query Params**: - oraLocation (str): ORA location for XBlock handling - submissionUUID (str): The ORA submission UUID - Response: { - assessments (List[dict]): [ - { - "assessment_id: (str) Assessment id - "scorer_name: (str) Scorer name - "scorer_username: (str) Scorer username - "scorer_email: (str) Scorer email - "assessment_date: (str) Assessment date - "assessment_scores (List[dict]) [ - { - "criterion_name: (str) Criterion name - "score_earned: (int) Score earned - "score_type: (str) Score type - } - ] - "problem_step (str) Problem step (Self, Peer, or Staff) - "feedback: (str) Feedback of the assessment - } - ] - } + **Response**: - Errors: - - MissingParamResponse (HTTP 400) for missing params - - BadOraLocationResponse (HTTP 400) for bad ORA location - - XBlockInternalError (HTTP 500) for an issue with ORA - - UnknownError (HTTP 500) for other errors + { + assessments (List[dict]): [ + { + "assessment_id: (str) Assessment id + "scorer_name: (str) Scorer name + "scorer_username: (str) Scorer username + "scorer_email: (str) Scorer email + "assessment_date: (str) Assessment date + "assessment_scores (List[dict]) [ + { + "criterion_name: (str) Criterion name + "score_earned: (int) Score earned + "score_type: (str) Score type + } + ] + "problem_step (str) Problem step (Self, Peer, or Staff) + "feedback: (str) Feedback of the assessment + } + ] + } + + **Errors**: + - MissingParamResponse (HTTP 400) for missing params + - BadOraLocationResponse (HTTP 400) for bad ORA location + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors """ + @action(methods=['get'], detail=False, url_path='from') + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def get_from(self, request: Request, ora_location: str, submission_uuid: str, *args, **kwargs): + return self._get_assessments(request, get_assessments_from, ora_location, submission_uuid) + @action(methods=['get'], detail=False, url_path='to') @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) - def get(self, request, ora_location, submission_uuid, *args, **kwargs): - """Get assessments received by a user in an ORA assignment""" + def get_to(self, request: Request, ora_location: str, submission_uuid: str, *args, **kwargs): + return self._get_assessments(request, get_assessments_to, ora_location, submission_uuid) + + def _get_assessments( + self, request: Request, getter_func: Callable, ora_location: str, submission_uuid: str + ): + """ + Fetches assessment data using the provided assessment getter function. + + Args: + request (Request): The Django request object. + getter_func (Callable): A function that retrieves assessments based on criteria. + ora_location (str): The ORA location for XBlock handling. + submission_uuid (str): The ORA submission UUID. + + Returns: + A Django response object containing serialized assessment data or an error response. + """ try: - assessments_data = {"assessments": get_assessments_from(request, ora_location, submission_uuid)} + assessments_data = {"assessments": getter_func(request, ora_location, submission_uuid)} response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) From b006691545a5df9a1a525e0d2d27ef02a43c4b41 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 20 Feb 2024 12:01:48 -0500 Subject: [PATCH 6/7] chore: address PR review --- lms/djangoapps/ora_staff_grader/ora_api.py | 42 ++++++---------------- lms/djangoapps/ora_staff_grader/views.py | 42 ++++++++++++---------- 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index aac71663e892..2351f6fe3751 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -37,46 +37,26 @@ def get_submissions(request, usage_id): return json.loads(response.content) -def get_assessments_to(request: Request, usage_id: str, submission_uuid: str): +def get_assessments(request: Request, usage_id: str, handler_name: str, submission_uuid: str): """ - Get a list of assessments given from ORA `list_assessments_to` XBlock.json_handler. + Get a list of assessments according to the handler name from ORA XBlock.json_handler - Lists all assessments given by a user (according to their submissionUUID) - in an ORA assignment. Assessments can be Self, Peer and Staff. + * `list_assessments_to` handler + Lists all assessments given by a user (according to their submissionUUID) + in an ORA assignment. Assessments can be Self, Peer and Staff. - Args: - request (Request): The request object - usage_id (str): Usage ID of the XBlock for running the handler - submission_uuid (str): The ORA submission UUID - """ - handler_name = "list_assessments_to" - data = { - "item_id": usage_id, - "submission_uuid": submission_uuid, - } - - response = call_xblock_json_handler(request, usage_id, handler_name, data) - - if response.status_code != HTTPStatus.OK: - raise XBlockInternalError(context={"handler": handler_name}) - - return json.loads(response.content) - - -def get_assessments_from(request: Request, usage_id: str, submission_uuid: str): - """ - Get a list of assessments received from ORA `list_assessments_from` XBlock.json_handler - - Lists all assessments received by a user (according to their submissionUUID) - in an ORA assignment. Assessments can be Self, Peer and Staff. + * `list_assessments_from` handler + Lists all assessments received by a user (according to their submissionUUID) + in an ORA assignment. Assessments can be Self, Peer and Staff. Args: - request (Request): The request object + request (Request): The request object. usage_id (str): Usage ID of the XBlock for running the handler + handler_name (str): The name of the handler to call submission_uuid (str): The ORA submission UUID """ - handler_name = "list_assessments_from" data = { + "item_id": usage_id, "submission_uuid": submission_uuid, } diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py index 8ab71116bb26..16c84ca27a9a 100644 --- a/lms/djangoapps/ora_staff_grader/views.py +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -7,7 +7,6 @@ # NOTE: we intentionally add extra args using @require_params # pylint: disable=arguments-differ import logging -from typing import Callable from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import ( @@ -47,8 +46,7 @@ get_assessment_info, get_submission_info, get_submissions, - get_assessments_to, - get_assessments_from, + get_assessments, submit_grade, ) from lms.djangoapps.ora_staff_grader.serializers import ( @@ -160,17 +158,18 @@ class AssessmentFeedbackView(StaffGraderBaseView, ViewSet): **Methods** - * (GET) api/ora_staff_grader/assessments/feedback/from + * (GET) `api/ora_staff_grader/assessments/feedback/from` List all assessments received by a user (according to their submissionUUID) in an ORA assignment. - * (GET) api/ora_staff_grader/assessments/feedback/to + * (GET) `api/ora_staff_grader/assessments/feedback/to` List all assessments given by a user (according to their submissionUUID) in an ORA assignment. **Query Params**: - - oraLocation (str): ORA location for XBlock handling - - submissionUUID (str): The ORA submission UUID + + * `oraLocation` (str): ORA location for XBlock handling + * `submissionUUID` (str): The ORA submission UUID **Response**: @@ -196,38 +195,43 @@ class AssessmentFeedbackView(StaffGraderBaseView, ViewSet): } **Errors**: - - MissingParamResponse (HTTP 400) for missing params - - BadOraLocationResponse (HTTP 400) for bad ORA location - - XBlockInternalError (HTTP 500) for an issue with ORA - - UnknownError (HTTP 500) for other errors + + * `MissingParamResponse` (HTTP 400) for missing params + * `BadOraLocationResponse` (HTTP 400) for bad ORA location + * `XBlockInternalError` (HTTP 500) for an issue with ORA + * `UnknownError` (HTTP 500) for other errors """ - @action(methods=['get'], detail=False, url_path='from') + @action(methods=["get"], detail=False, url_path="from") @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) def get_from(self, request: Request, ora_location: str, submission_uuid: str, *args, **kwargs): - return self._get_assessments(request, get_assessments_from, ora_location, submission_uuid) + return self._get_assessments(request, ora_location, "list_assessments_from", submission_uuid) - @action(methods=['get'], detail=False, url_path='to') + @action(methods=["get"], detail=False, url_path="to") @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) def get_to(self, request: Request, ora_location: str, submission_uuid: str, *args, **kwargs): - return self._get_assessments(request, get_assessments_to, ora_location, submission_uuid) + return self._get_assessments(request, ora_location, "list_assessments_to", submission_uuid) def _get_assessments( - self, request: Request, getter_func: Callable, ora_location: str, submission_uuid: str + self, request: Request, ora_location: str, handler_name: str, submission_uuid: str ): """ - Fetches assessment data using the provided assessment getter function. + Fetches assessment data using the given handler name. Args: request (Request): The Django request object. - getter_func (Callable): A function that retrieves assessments based on criteria. ora_location (str): The ORA location for XBlock handling. + handler_name (str): The name of the XBlock handler to use. submission_uuid (str): The ORA submission UUID. Returns: A Django response object containing serialized assessment data or an error response. """ try: - assessments_data = {"assessments": getter_func(request, ora_location, submission_uuid)} + assessments_data = { + "assessments": get_assessments( + request, ora_location, handler_name, submission_uuid + ) + } response_data = AssessmentFeedbackSerializer(assessments_data).data return Response(response_data) From 0dd2338a611ab177b59f3cbd061d5886ceccb7d7 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 20 Feb 2024 19:59:17 -0500 Subject: [PATCH 7/7] fix: handle exception when an error is returned from xblock handler --- lms/djangoapps/ora_staff_grader/ora_api.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/ora_staff_grader/ora_api.py b/lms/djangoapps/ora_staff_grader/ora_api.py index 2351f6fe3751..07849c505332 100644 --- a/lms/djangoapps/ora_staff_grader/ora_api.py +++ b/lms/djangoapps/ora_staff_grader/ora_api.py @@ -65,7 +65,12 @@ def get_assessments(request: Request, usage_id: str, handler_name: str, submissi if response.status_code != HTTPStatus.OK: raise XBlockInternalError(context={"handler": handler_name}) - return json.loads(response.content) + try: + return json.loads(response.content) + except json.JSONDecodeError as exc: + raise XBlockInternalError( + context={"handler": handler_name, "details": response.content} + ) from exc def get_submission_info(request, usage_id, submission_uuid):