def save_submission(self, data, suffix=""):
        """
        Save the current student's response submission.
        If the student already has a response saved, this will overwrite it.

        Args:
            data (dict): Data should have a single key 'submission' that contains
                the text of the student's response. Optionally, the data could
                have a 'file_url' key that is the path to an associated file for
                this submission.
            suffix (str): Not used.

        Returns:
            dict: Contains a bool 'success' and unicode string 'msg'.
        """
        if "submission" in data:
            student_sub_data = data["submission"]
            success, msg = validate_submission(student_sub_data, self.prompts, self._)
            if not success:
                return {"success": False, "msg": msg}
            try:
                self.saved_response = json.dumps(prepare_submission_for_serialization(student_sub_data))
                self.has_saved = True

                # Emit analytics event...
                self.runtime.publish(
                    self, "openassessmentblock.save_submission", {"saved_response": self.saved_response}
                )
            except:
                return {"success": False, "msg": self._(u"This response could not be saved.")}
            else:
                return {"success": True, "msg": u""}
        else:
            return {"success": False, "msg": self._(u"This response was not submitted.")}
    def save_submission(self, data, suffix=''):
        """
        Save the current student's response submission.
        If the student already has a response saved, this will overwrite it.

        Args:
            data (dict): Data should have a single key 'submission' that contains
                the text of the student's response. Optionally, the data could
                have a 'file_urls' key that is the path to an associated file for
                this submission.
            suffix (str): Not used.

        Returns:
            dict: Contains a bool 'success' and unicode string 'msg'.
        """
        if 'submission' in data:
            student_sub_data = data['submission']
            success, msg = validate_submission(student_sub_data, self.prompts,
                                               self._, self.text_response)
            if not success:
                return {'success': False, 'msg': msg}
            try:
                self.saved_response = json.dumps(
                    prepare_submission_for_serialization(student_sub_data))
                self.has_saved = True

                # Emit analytics event...
                self.runtime.publish(self,
                                     "openassessmentblock.save_submission",
                                     {"saved_response": self.saved_response})
            except:
                return {
                    'success': False,
                    'msg': self._(u"This response could not be saved.")
                }
            else:
                return {'success': True, 'msg': u''}
        else:
            return {
                'success': False,
                'msg': self._(u"This response was not submitted.")
            }
    def submit(self, data, suffix=''):
        """Place the submission text into Openassessment system

        Allows submission of new responses.  Performs basic workflow validation
        on any new submission to ensure it is acceptable to receive a new
        response at this time.

        Args:
            data (dict): Data may contain two attributes: submission and
                file_urls. submission is the response from the student which
                should be stored in the Open Assessment system. file_urls is the
                path to a related file for the submission. file_urls is optional.
            suffix (str): Not used in this handler.

        Returns:
            (tuple): Returns the status (boolean) of this request, the
                associated status tag (str), and status text (unicode).

        """
        if 'submission' not in data:
            return (False, 'EBADARGS',
                    self._(u'"submission" required to submit answer.'))

        status = False
        student_sub_data = data['submission']
        success, msg = validate_submission(student_sub_data, self.prompts,
                                           self._, self.text_response)
        if not success:
            return (False, 'EBADARGS', msg)

        student_item_dict = self.get_student_item_dict()

        # Short-circuit if no user is defined (as in Studio Preview mode)
        # Since students can't submit, they will never be able to progress in the workflow
        if self.in_studio_preview:
            return (
                False, 'ENOPREVIEW',
                self.
                _(u'To submit a response, view this component in Preview or Live mode.'
                  ))

        workflow = self.get_workflow_info()

        status_tag = 'ENOMULTI'  # It is an error to submit multiple times for the same item
        status_text = self._(u'Multiple submissions are not allowed.')
        if not workflow:
            try:
                try:
                    saved_files_descriptions = json.loads(
                        self.saved_files_descriptions)
                except ValueError:
                    saved_files_descriptions = None
                submission = self.create_submission(student_item_dict,
                                                    student_sub_data,
                                                    saved_files_descriptions)
            except api.SubmissionRequestError as err:

                # Handle the case of an answer that's too long as a special case,
                # so we can display a more specific error message.
                # Although we limit the number of characters the user can
                # enter on the client side, the submissions API uses the JSON-serialized
                # submission to calculate length.  If each character submitted
                # by the user takes more than 1 byte to encode (for example, double-escaped
                # newline characters or non-ASCII unicode), then the user might
                # exceed the limits set by the submissions API.  In that case,
                # we display an error message indicating that the answer is too long.
                answer_too_long = any(
                    "maximum answer size exceeded" in answer_err.lower()
                    for answer_err in err.field_errors.get('answer', []))
                if answer_too_long:
                    status_tag = 'EANSWERLENGTH'
                else:
                    msg = (
                        u"The submissions API reported an invalid request error "
                        u"when submitting a response for the user: {student_item}"
                    ).format(student_item=student_item_dict)
                    logger.exception(msg)
                    status_tag = 'EBADFORM'
            except (api.SubmissionError, AssessmentWorkflowError):
                msg = (u"An unknown error occurred while submitting "
                       u"a response for the user: {student_item}").format(
                           student_item=student_item_dict)
                logger.exception(msg)
                status_tag = 'EUNKNOWN'
                status_text = self._(u'API returned unclassified exception.')
            else:
                status = True
                status_tag = submission.get('student_item')
                status_text = submission.get('attempt_number')

        return status, status_tag, status_text
    def submit(self, data, suffix=""):
        """Place the submission text into Openassessment system

        Allows submission of new responses.  Performs basic workflow validation
        on any new submission to ensure it is acceptable to receive a new
        response at this time.

        Args:
            data (dict): Data may contain two attributes: submission and
                file_url. submission is the response from the student which
                should be stored in the Open Assessment system. file_url is the
                path to a related file for the submission. file_url is optional.
            suffix (str): Not used in this handler.

        Returns:
            (tuple): Returns the status (boolean) of this request, the
                associated status tag (str), and status text (unicode).

        """
        if "submission" not in data:
            return (False, "EBADARGS", self._(u'"submission" required to submit answer.'))

        status = False
        student_sub_data = data["submission"]
        success, msg = validate_submission(student_sub_data, self.prompts, self._)
        if not success:
            return (False, "EBADARGS", msg)

        student_item_dict = self.get_student_item_dict()

        # Short-circuit if no user is defined (as in Studio Preview mode)
        # Since students can't submit, they will never be able to progress in the workflow
        if self.in_studio_preview:
            return (False, "ENOPREVIEW", self._(u"To submit a response, view this component in Preview or Live mode."))

        workflow = self.get_workflow_info()

        status_tag = "ENOMULTI"  # It is an error to submit multiple times for the same item
        status_text = self._(u"Multiple submissions are not allowed.")
        if not workflow:
            try:
                submission = self.create_submission(student_item_dict, student_sub_data)
            except api.SubmissionRequestError as err:

                # Handle the case of an answer that's too long as a special case,
                # so we can display a more specific error message.
                # Although we limit the number of characters the user can
                # enter on the client side, the submissions API uses the JSON-serialized
                # submission to calculate length.  If each character submitted
                # by the user takes more than 1 byte to encode (for example, double-escaped
                # newline characters or non-ASCII unicode), then the user might
                # exceed the limits set by the submissions API.  In that case,
                # we display an error message indicating that the answer is too long.
                answer_too_long = any(
                    "maximum answer size exceeded" in answer_err.lower()
                    for answer_err in err.field_errors.get("answer", [])
                )
                if answer_too_long:
                    status_tag = "EANSWERLENGTH"
                else:
                    msg = (
                        u"The submissions API reported an invalid request error "
                        u"when submitting a response for the user: {student_item}"
                    ).format(student_item=student_item_dict)
                    logger.exception(msg)
                    status_tag = "EBADFORM"
            except (api.SubmissionError, AssessmentWorkflowError):
                msg = (
                    u"An unknown error occurred while submitting " u"a response for the user: {student_item}"
                ).format(student_item=student_item_dict)
                logger.exception(msg)
                status_tag = "EUNKNOWN"
                status_text = self._(u"API returned unclassified exception.")
            else:
                status = True
                status_tag = submission.get("student_item")
                status_text = submission.get("attempt_number")

        return status, status_tag, status_text