Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: update assessments formats #2106

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 79 additions & 8 deletions openassessment/xblock/ui_mixins/mfe/assessment_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
SerializerMethodField,
URLField,
Serializer,
DictField,
BooleanField,
)
from openassessment.xblock.ui_mixins.mfe.serializer_utils import NullField
Expand All @@ -30,7 +29,9 @@ class AssessmentScoreSerializer(Serializer):

class AssessmentCriterionSerializer(Serializer):
"""
returns:
Serialize assessment criterion values from DB representation to frontend data structure.

Returns:
{
selectedOption: (Int) Order of the selected option
feedback: (String) Feedback for the selected option
Expand All @@ -42,10 +43,16 @@ class AssessmentCriterionSerializer(Serializer):

class AssessmentDataSerializer(Serializer):
"""
Assessment data serializer
Serialize assessment data from DB representation to frontend data structure.

Returns:
{
criteria: [ AssessmentCriterionSerializer ]
overallFeedback: (String / Empty)
}
"""
overallFeedback = CharField(source="feedback")
assessmentCriterions = AssessmentCriterionSerializer(source="parts", many=True)
criteria = AssessmentCriterionSerializer(source="parts", many=True)


class AssessmentStepSerializer(Serializer):
Expand Down Expand Up @@ -167,9 +174,73 @@ def get_uploadedFiles(self, instance):
return [SubmissionFileSerializer(file).data for file in files]


class AssessmentSubmitRequestSerializer(Serializer):
"""" Serializer for validating request shape """
optionsSelected = DictField(child=CharField(), allow_empty=True)
criterionFeedback = DictField(child=CharField(), allow_empty=True)
class MfeAssessmentCriterionSerializer(Serializer):
"""
Frontend's view of rubric criterion values for an assessment

{
selectedOption: (Int) Order of the selected option
feedback: (String / Empty) Feedback for the selected option
}
"""
selectedOption = IntegerField()
feedback = CharField()


class MfeAssessmentDataSerializer(Serializer):
"""
Frontend's view of Assessment Data.

criteria: [ MfeAssessmentCriterion ],
overallFeedback: (String / Empty) Feedback for the Assessment
"""
criteria = MfeAssessmentCriterionSerializer(many=True)
overallFeedback = CharField(allow_blank=True)


class AssessmentSubmitRequestSerializer(MfeAssessmentDataSerializer):
""""
Serializer for validating request shape and unpacking data for assessment APIs.

Args: Data in the form
{
criteria: [
// Rubric criterion
{
selectedOption: (Int) Order of the selected option
feedback: (String / Empty) Feedback for the selected option
}
...
],
overallFeedback: (String / Empty)
}
"""

continueGrading = BooleanField(required=False, default=False)

def to_legacy_format(self, xblock):
"""
Converts given assessment format to format needed for submitting an assessment:

>>> options_selected = {"clarity": "Very clear", "precision": "Somewhat precise"}
>>> criterion_feedback = {"clarity": "I thought this essay was very clear."}
>>> feedback = "Your submission was thrilling."
"""
options_selected = {}
criterion_feedback = {}

for i, criterion_data in enumerate(self.data['criteria']):

# Look up the name and value for each given rubric selection
criterion_name = xblock.rubric_criteria[i]['name']
selected_value = xblock.rubric_criteria[i]['options'][criterion_data['selectedOption']]['name']
options_selected[criterion_name] = selected_value

# Attach feedback for the criterion
criterion_feedback[criterion_name] = criterion_data['feedback']

return {
"options_selected": options_selected,
"criterion_feedback": criterion_feedback,
"feedback": self.data["overallFeedback"]
}
26 changes: 16 additions & 10 deletions openassessment/xblock/ui_mixins/mfe/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
SubmitInternalError,
UnsupportedFileTypeException
)
from openassessment.xblock.ui_mixins.mfe.assessment_serializers import AssessmentSubmitRequestSerializer
from openassessment.xblock.ui_mixins.mfe.assessment_serializers import (
AssessmentSubmitRequestSerializer,
MfeAssessmentDataSerializer,
)
from openassessment.xblock.ui_mixins.mfe.constants import error_codes, handler_suffixes
from openassessment.xblock.ui_mixins.mfe.ora_config_serializer import OraBlockInfoSerializer
from openassessment.xblock.ui_mixins.mfe.page_context_serializer import PageDataSerializer
Expand Down Expand Up @@ -307,30 +310,30 @@ def _assessment_submit_handler(self, data):
serializer = AssessmentSubmitRequestSerializer(data=data)
if not serializer.is_valid():
raise OraApiException(400, error_codes.INCORRECT_PARAMETERS, serializer.errors)
data = serializer.validated_data
peer_data = self.peer_assessment_data(data['continueGrading'])
assessment_data = serializer.to_legacy_format(self)
peer_data = self.peer_assessment_data(serializer.data['continueGrading'])
try:
if peer_data.continue_grading or self.workflow_data.is_peer:
peer_assess(
data['optionsSelected'],
data['overallFeedback'],
data['criterionFeedback'],
assessment_data['options_selected'],
assessment_data['feedback'],
assessment_data['criterion_feedback'],
self.config_data,
self.workflow_data,
peer_data,
)
elif self.workflow_data.is_self:
self_assess(
data['optionsSelected'],
data['criterionFeedback'],
data['overallFeedback'],
assessment_data['options_selected'],
assessment_data['criterion_feedback'],
assessment_data['feedback'],
self.config_data,
self.workflow_data,
self.self_data
)
elif self.workflow_data.is_training:
corrections = training_assess(
data['optionsSelected'],
assessment_data['options_selected'],
self.config_data,
self.workflow_data,
)
Expand All @@ -349,6 +352,9 @@ def _assessment_submit_handler(self, data):
except (AssessmentError, AssessmentWorkflowError) as e:
raise OraApiException(500, error_codes.INTERNAL_EXCEPTION, str(e)) from e

# Return assessment data for the frontend
return MfeAssessmentDataSerializer(data).data

def _assessment_get_peer_handler(self):
# Call get_peer_submission to grab a new peer submission
self.peer_assessment_data().get_peer_submission()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
AssessmentScoreSerializer,
AssessmentDataSerializer,
AssessmentCriterionSerializer,
AssessmentSubmitRequestSerializer,
)


Expand Down Expand Up @@ -347,4 +348,26 @@ def test_assessment_data(self):
}).data

self.assertEqual(assessment_data["overallFeedback"], "Foo")
self.assertEqual(len(assessment_data["assessmentCriterions"]), 1)
self.assertEqual(len(assessment_data["criteria"]), 1)


class TestAssessmentSubmitRequestSerializer(TestCase):
"""
Test for AssessmentSubmitRequestSerializer
"""

def test_assessment_data(self):
assessment_submit_request_data = AssessmentSubmitRequestSerializer({
"criteria": [
{
"selectedOption": 3,
"feedback": "Baz",
}
],
"overallFeedback": "Foo",
"continueGrading": True,
}).data

self.assertEqual(assessment_submit_request_data["overallFeedback"], "Foo")
self.assertEqual(len(assessment_submit_request_data["criteria"]), 1)
self.assertTrue(assessment_submit_request_data["continueGrading"])
19 changes: 16 additions & 3 deletions openassessment/xblock/ui_mixins/mfe/test_mfe_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,23 @@ def mock_get_url(self, expected_file_urls=None):
DEFAULT_DRAFT_VALUE = {'response': {'text_responses': ['hi']}}
DEFAULT_SUBMIT_VALUE = {'response': {'text_responses': ['Hello World', 'Goodbye World']}}
DEFAULT_DELETE_FILE_VALUE = {'fileIndex': 1}

DEFAULT_ASSESSMENT_SUBMIT_VALUE = {
'optionsSelected': {'ferocity': 'fine', 'color': 'blue', 'element': 'volcano'},
'criterionFeedback': {'ferocity': 'rawr!', 'color': ':)', 'element': 'i prefer lead'},
'overallFeedback': 'i have no strong feelings',
"criteria": [
{
"selectedOption": 2,
"feedback": "rawr!",
},
{
"selectedOption": 0,
"feedback": ":)",
},
{
"selectedOption": 1,
"feedback": "i prefer lead",
}
],
"overallFeedback": "i have no strong feelings",
}

def request_create_submission(self, xblock, payload=None):
Expand Down