コード例 #1
0
ファイル: staff_area_mixin.py プロジェクト: s0b0lev/edx-ora2
    def clear_student_state(self, user_id, course_id, item_id, requesting_user_id):
        """
        This xblock method is called (from our LMS runtime, which defines this method signature) to clear student state
        for a given problem. It will cancel the workflow using traditional methods to remove it from the grading pools,
        and pass through to the submissions API to orphan the submission so that the user can create a new one.
        """
        # Import is placed here to avoid model import at project startup.
        from submissions import api as submission_api
        # Note that student_item cannot be constructed using get_student_item_dict, since we're in a staff context
        student_item = {
            'course_id': course_id,
            'student_id': user_id,
            'item_id': item_id,
            'item_type': 'openassessment',
        }
        # There *should* only be one submission, but the logic is easy to extend for multiples so we may as well do it
        submissions = submission_api.get_submissions(student_item)
        for sub in submissions:
            # Remove the submission from grading pools
            self._cancel_workflow(sub['uuid'], "Student state cleared", requesting_user_id=requesting_user_id)

            # Tell the submissions API to orphan the submission to prevent it from being accessed
            submission_api.reset_score(
                user_id,
                course_id,
                item_id,
                clear_state=True
            )
コード例 #2
0
    def clear_student_state(self, user_id, course_id, item_id):
        """
        This xblock method is called (from our LMS runtime, which defines this method signature) to clear student state
        for a given problem. It will cancel the workflow using traditional methods to remove it from the grading pools,
        and pass through to the submissions API to orphan the submission so that the user can create a new one.
        """
        # Note that student_item cannot be constructed using get_student_item_dict, since we're in a staff context
        student_item = {
            'course_id': course_id,
            'student_id': user_id,
            'item_id': item_id,
            'item_type': 'openassessment',
        }
        # There *should* only be one submission, but the logic is easy to extend for multiples so we may as well do it
        submissions = submission_api.get_submissions(student_item)
        for sub in submissions:
            # Remove the submission from grading pools
            self._cancel_workflow(sub['uuid'], "Student state cleared")

            # Tell the submissions API to orphan the submission to prevent it from being accessed
            submission_api.reset_score(
                user_id,
                course_id,
                item_id,
                clear_state=True  # pylint: disable=unexpected-keyword-arg
            )
コード例 #3
0
ファイル: models.py プロジェクト: Stanford-Online/edx-ora2
    def set_staff_score(self, score, reason=None):
        """
        Set a staff score for the workflow.

        Allows for staff scores to be set on a submission, with annotations to provide an audit trail if needed.
        This method can be used for both required staff grading, and staff overrides.

        Args:
            score (dict): A dict containing 'points_earned', 'points_possible', and 'staff_id'.
            is_override (bool): Optionally True if staff is overriding a previous score.
            reason (string): An optional parameter specifying the reason for the staff grade. A default value
                will be used in the event that this parameter is not provided.

        """
        if reason is None:
            reason = "A staff member has defined the score for this submission"
        sub_dict = sub_api.get_submission_and_student(self.submission_uuid)
        sub_api.reset_score(
            sub_dict['student_item']['student_id'],
            self.course_id,
            self.item_id,
            emit_signal=False
        )
        sub_api.set_score(
            self.submission_uuid,
            score["points_earned"],
            score["points_possible"],
            annotation_creator=score["staff_id"],
            annotation_type=self.STAFF_ANNOTATION_TYPE,
            annotation_reason=reason
        )
コード例 #4
0
ファイル: sga.py プロジェクト: eduStack/edx-sga
 def remove_grade(self, request, suffix=''):
     # pylint: disable=unused-argument
     """
     Reset a students score request by staff.
     """
     require(self.is_course_staff())
     student_id = request.params['student_id']
     submissions_api.reset_score(student_id, self.course_id, self.block_id)
     module = StudentModule.objects.get(pk=request.params['module_id'])
     state = json.loads(module.state)
     state['staff_score'] = None
     state['comment'] = ''
     state['annotated_sha1'] = None
     state['annotated_filename'] = None
     state['annotated_mimetype'] = None
     state['annotated_timestamp'] = None
     module.state = json.dumps(state)
     module.save()
     log.info(
         "remove_grade for course:%s module:%s student:%s",
         module.course_id,
         module.module_state_key,
         module.student.username
     )
     return Response(json_body=self.staff_grading_data())
コード例 #5
0
ファイル: sga.py プロジェクト: mamigot/edx-sga
 def remove_grade(self, request, suffix=''):
     # pylint: disable=unused-argument
     """
     Reset a students score request by staff.
     """
     require(self.is_course_staff())
     student_id = request.params['student_id']
     submissions_api.reset_score(student_id, unicode(self.course_id), unicode(self.block_id))
     module = StudentModule.objects.get(pk=request.params['module_id'])
     state = json.loads(module.state)
     state['staff_score'] = None
     state['comment'] = ''
     state['annotated_sha1'] = None
     state['annotated_filename'] = None
     state['annotated_mimetype'] = None
     state['annotated_timestamp'] = None
     module.state = json.dumps(state)
     module.save()
     log.info(
         "remove_grade for course:%s module:%s student:%s",
         module.course_id,
         module.module_state_key,
         module.student.username
     )
     return Response(json_body=self.staff_grading_data())
コード例 #6
0
    def test_reset_different_student_item(self, changed):
        # Create a submissions for two students
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        other_student = copy.copy(self.STUDENT_ITEM)
        other_student.update(changed)
        submission = sub_api.create_submission(other_student,
                                               'other test answer')
        sub_api.set_score(submission['uuid'], 3, 4)

        # Reset the score for the first student
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # The first student's scores should be reset
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)
        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'],
                                    self.STUDENT_ITEM['student_id'])
        self.assertNotIn(self.STUDENT_ITEM['item_id'], scores)

        # But the second student should still have a score
        score = sub_api.get_score(other_student)
        self.assertEqual(score['points_earned'], 3)
        self.assertEqual(score['points_possible'], 4)
        scores = sub_api.get_scores(other_student['course_id'],
                                    other_student['student_id'])
        self.assertIn(other_student['item_id'], scores)
コード例 #7
0
ファイル: sga.py プロジェクト: moocitfrance/edx-sga
    def clear_student_state(self, *args, **kwargs):
        # pylint: disable=unused-argument
        """
        For a given user, clears submissions and uploaded files for this XBlock.

        Staff users are able to delete a learner's state for a block in LMS. When that capability is
        used, the block's "clear_student_state" function is called if it exists.
        """
        student_id = kwargs['user_id']
        for submission in submissions_api.get_submissions(
                self.get_student_item_dict(student_id)
        ):
            submission_file_sha1 = submission['answer'].get('sha1')
            submission_filename = submission['answer'].get('filename', None)

            if submission_filtename:
                submission_file_path = self.file_storage_path(submission_file_sha1, submission_filename)
                if default_storage.exists(submission_file_path):
                    default_storage.delete(submission_file_path)

            submissions_api.reset_score(
                student_id,
                self.block_course_id,
                self.block_id,
                clear_state=True
            )
コード例 #8
0
    def test_reset_different_student_item(self, changed):
        # Create a submissions for two students
        submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        other_student = copy.copy(self.STUDENT_ITEM)
        other_student.update(changed)
        submission = sub_api.create_submission(other_student, "other test answer")
        sub_api.set_score(submission["uuid"], 3, 4)

        # Reset the score for the first student
        sub_api.reset_score(
            self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
        )

        # The first student's scores should be reset
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)
        scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"])
        self.assertNotIn(self.STUDENT_ITEM["item_id"], scores)

        # But the second student should still have a score
        score = sub_api.get_score(other_student)
        self.assertEqual(score["points_earned"], 3)
        self.assertEqual(score["points_possible"], 4)
        scores = sub_api.get_scores(other_student["course_id"], other_student["student_id"])
        self.assertIn(other_student["item_id"], scores)
コード例 #9
0
    def test_reset_then_add_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Score the student again
        sub_api.set_score(submission['uuid'], 3, 4)

        # Expect that the new score is available
        score = sub_api.get_score(self.STUDENT_ITEM)
        self.assertEqual(score['points_earned'], 3)
        self.assertEqual(score['points_possible'], 4)

        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'],
                                    self.STUDENT_ITEM['student_id'])
        self.assertIn(self.STUDENT_ITEM['item_id'], scores)
        self.assertEqual(scores[self.STUDENT_ITEM['item_id']], (3, 4))
コード例 #10
0
    def set_staff_score(self, score, reason=None):
        """
        Set a staff score for the workflow.

        Allows for staff scores to be set on a submission, with annotations to provide an audit trail if needed.
        This method can be used for both required staff grading, and staff overrides.

        Args:
            score (dict): A dict containing 'points_earned', 'points_possible', and 'staff_id'.
            is_override (bool): Optionally True if staff is overriding a previous score.
            reason (string): An optional parameter specifying the reason for the staff grade. A default value
                will be used in the event that this parameter is not provided.

        """
        if reason is None:
            reason = "A staff member has defined the score for this submission"
        sub_dict = sub_api.get_submission_and_student(self.submission_uuid)
        sub_api.reset_score(
            sub_dict['student_item']['student_id'],
            self.course_id,
            self.item_id,
            emit_signal=False
        )
        sub_api.set_score(
            self.submission_uuid,
            score["points_earned"],
            score["points_possible"],
            annotation_creator=score["staff_id"],
            annotation_type=self.STAFF_ANNOTATION_TYPE,
            annotation_reason=reason
        )
コード例 #11
0
    def test_reset_with_no_scores(self):
        sub_api.reset_score(
            self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
        )
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)

        scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"])
        self.assertEqual(len(scores), 0)
コード例 #12
0
def reset_student_attempts(course_id,
                           student,
                           module_state_key,
                           delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id,
                                           student,
                                           child,
                                           delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
    except ItemNotFoundError:
        log.warning(
            "Could not find %s in modulestore when attempting to reset attempts.",
            module_state_key)

    # Reset the student's score in the submissions API
    # Currently this is used only by open assessment (ORA 2)
    # We need to do this *before* retrieving the `StudentModule` model,
    # because it's possible for a score to exist even if no student module exists.
    if delete_module:
        sub_api.reset_score(
            anonymous_id_for_user(student, course_id),
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key)

    if delete_module:
        module_to_reset.delete()
    else:
        _reset_module_attempts(module_to_reset)
コード例 #13
0
    def test_database_error(self, create_mock):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        # Simulate a database error when creating the reset score
        create_mock.side_effect = DatabaseError("Test error")
        with self.assertRaises(sub_api.SubmissionInternalError):
            sub_api.reset_score(
                self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
            )
コード例 #14
0
ファイル: enrollment.py プロジェクト: Endika/edx-platform
def reset_student_attempts(course_id, student, module_state_key, delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    user_id = anonymous_id_for_user(student, course_id)
    submission_cleared = False
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id, student, child, delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
        if delete_module:
            # Some blocks (openassessment) use StudentModule data as a key for internal submission data.
            # Inform these blocks of the reset and allow them to handle their data.
            clear_student_state = getattr(block, "clear_student_state", None)
            if callable(clear_student_state):
                clear_student_state(user_id=user_id, course_id=unicode(course_id), item_id=unicode(module_state_key))
                submission_cleared = True
    except ItemNotFoundError:
        log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key)

    # Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already.
    # TODO: Remove this once we've finalized and communicated how xblocks should handle clear_student_state
    # and made sure that other xblocks relying on the submission api understand this is going away.
    # We need to do this before retrieving the `StudentModule` model, because a score may exist with no student module.
    if delete_module and not submission_cleared:
        sub_api.reset_score(user_id, course_id.to_deprecated_string(), module_state_key.to_deprecated_string())

    module_to_reset = StudentModule.objects.get(
        student_id=student.id, course_id=course_id, module_state_key=module_state_key
    )

    if delete_module:
        module_to_reset.delete()
    else:
        _reset_module_attempts(module_to_reset)
コード例 #15
0
    def test_reset_with_no_scores(self):
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)

        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'],
                                    self.STUDENT_ITEM['student_id'])
        self.assertEqual(len(scores), 0)
コード例 #16
0
    def test_reset_then_get_score_for_submission(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
        )

        # If we're retrieving the score for a particular submission,
        # instead of a student item, then we should STILL get a score.
        self.assertIsNot(sub_api.get_latest_score_for_submission(submission["uuid"]), None)
コード例 #17
0
ファイル: enrollment.py プロジェクト: LiaoPan/edx-platform
def reset_student_attempts(course_id, student, module_state_key, delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id, student, child, delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
    except ItemNotFoundError:
        log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key)

    # Reset the student's score in the submissions API
    # Currently this is used only by open assessment (ORA 2)
    # We need to do this *before* retrieving the `StudentModule` model,
    # because it's possible for a score to exist even if no student module exists.
    if delete_module:
        sub_api.reset_score(
            anonymous_id_for_user(student, course_id),
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key
    )

    if delete_module:
        module_to_reset.delete()
    else:
        _reset_module_attempts(module_to_reset)
コード例 #18
0
    def test_reset_with_one_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
        )

        # Expect that no scores are available for the student
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)
        scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"])
        self.assertEqual(len(scores), 0)
コード例 #19
0
    def test_database_error(self, create_mock):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Simulate a database error when creating the reset score
        create_mock.side_effect = DatabaseError("Test error")
        with self.assertRaises(sub_api.SubmissionInternalError):
            sub_api.reset_score(
                self.STUDENT_ITEM['student_id'],
                self.STUDENT_ITEM['course_id'],
                self.STUDENT_ITEM['item_id'],
            )
コード例 #20
0
ファイル: sga.py プロジェクト: eduNEXT/edunext-sga
 def remove_grade(self, request, suffix=''):
     require(self.is_course_staff())
     student_id = request.params['student_id']
     submissions_api.reset_score(student_id, self.course_id, self.block_id)
     module = StudentModule.objects.get(pk=request.params['module_id'])
     state = json.loads(module.state)
     state['staff_score'] = None
     state['comment'] = ''
     state['annotated_sha1'] = None
     state['annotated_filename'] = None
     state['annotated_mimetype'] = None
     state['annotated_timestamp'] = None
     module.state = json.dumps(state)
     module.save()
     return Response(json_body=self.staff_grading_data())
コード例 #21
0
    def clear_student_state(self, *args, **kwargs):
        # pylint: disable=unused-argument
        """
        For a given user, clears submissions and uploaded files for this XBlock.

        Staff users are able to delete a learner's state for a block in LMS. When that capability is
        used, the block's "clear_student_state" function is called if it exists.
        """
        student_id = kwargs['user_id']
        for submission in submissions_api.get_submissions(
                self.get_student_item_dict(student_id)):
            submissions_api.reset_score(student_id,
                                        self.block_course_id,
                                        self.block_id,
                                        clear_state=True)
コード例 #22
0
 def remove_grade(self, request, suffix=''):
     require(self.is_course_staff())
     student_id = request.params['student_id']
     submissions_api.reset_score(student_id, self.course_id, self.block_id)
     module = StudentModule.objects.get(pk=request.params['module_id'])
     state = json.loads(module.state)
     state['staff_score'] = None
     state['comment'] = ''
     state['annotated_sha1'] = None
     state['annotated_filename'] = None
     state['annotated_mimetype'] = None
     state['annotated_timestamp'] = None
     module.state = json.dumps(state)
     module.save()
     return Response(json_body=self.staff_grading_data())
コード例 #23
0
ファイル: team_api.py プロジェクト: edx/edx-submissions
def reset_scores(team_submission_uuid, clear_state=False):
    """
    Reset scores for a specific team submission to a problem.

    Note: this does *not* delete `Score` models from the database,
    since these are immutable.  It simply creates a new score with
    the "reset" flag set to True.

    Args:
        team_submission_uuid (str): The uuid for the team submission for which to reset scores.
        clear_state (bool): If True, soft delete the team submission and any individual submissions
                            by setting their status to DELETED

    Returns:
        None

    Raises:
        TeamSubmissionInternalError: An unexpected error occurred while resetting scores.

    """
    # Get the team submission
    try:
        team_submission = TeamSubmission.get_team_submission_by_uuid(
            team_submission_uuid)
        for submission in team_submission.submissions.select_related(
                'student_item').all():
            _api.reset_score(
                submission.student_item.student_id,
                submission.student_item.course_id,
                submission.student_item.item_id,
                clear_state=clear_state,
            )
        if clear_state:
            # soft-delete the TeamSubmission
            team_submission.status = DELETED
            team_submission.save(update_fields=["status"])
    except (DatabaseError, SubmissionInternalError) as error:
        msg = (
            f"Error occurred while reseting scores for team submission {team_submission_uuid}"
        )
        logger.exception(msg)
        raise TeamSubmissionInternalError(msg) from error
    else:
        logger.info("Score reset for team submission %(team_submission_uuid)s",
                    {
                        'team_submission_uuid': team_submission_uuid,
                    })
コード例 #24
0
    def test_reset_with_multiple_scores(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)
        sub_api.set_score(submission['uuid'], 2, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Expect that no scores are available for the student
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)
        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id'])
        self.assertEqual(len(scores), 0)
コード例 #25
0
    def test_reset_then_get_score_for_submission(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # If we're retrieving the score for a particular submission,
        # instead of a student item, then we should STILL get a score.
        self.assertIsNot(
            sub_api.get_latest_score_for_submission(submission['uuid']), None)
コード例 #26
0
    def test_reset_with_one_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Expect that no scores are available for the student
        self.assertIs(sub_api.get_score(self.STUDENT_ITEM), None)
        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'],
                                    self.STUDENT_ITEM['student_id'])
        self.assertEqual(len(scores), 0)
コード例 #27
0
    def test_reset_score_signal(self, send_mock):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Verify that the send method was properly called
        send_mock.assert_called_with(
            sender=None,
            anonymous_user_id=self.STUDENT_ITEM['student_id'],
            course_id=self.STUDENT_ITEM['course_id'],
            item_id=self.STUDENT_ITEM['item_id'])
コード例 #28
0
    def test_reset_score_signal(self, send_mock):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Verify that the send method was properly called
        send_mock.assert_called_with(
            sender = None,
            anonymous_user_id=self.STUDENT_ITEM['student_id'],
            course_id=self.STUDENT_ITEM['course_id'],
            item_id=self.STUDENT_ITEM['item_id']
        )
コード例 #29
0
    def test_override_after_reset_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 10)

        # Reset score
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        sub_api.score_override(
            self.STUDENT_ITEM,
            5,
            10,
        )

        self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5)
        self.assertEqual(sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
コード例 #30
0
ファイル: test_api.py プロジェクト: edx/edx-submissions
    def test_clear_state(self):
        # Create a submission, give it a score, and verify that score exists
        submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
        api.set_score(submission["uuid"], 11, 12)
        score = api.get_score(STUDENT_ITEM)
        self._assert_score(score, 11, 12)
        self.assertEqual(score['submission_uuid'], submission['uuid'])

        # Reset the score with clear_state=True
        # This should set the submission's score to None, and make it unavailable to get_submissions
        api.reset_score(
            STUDENT_ITEM["student_id"],
            STUDENT_ITEM["course_id"],
            STUDENT_ITEM["item_id"],
            clear_state=True,
        )
        score = api.get_score(STUDENT_ITEM)
        self.assertIsNone(score)
        subs = api.get_submissions(STUDENT_ITEM)
        self.assertEqual(subs, [])
コード例 #31
0
    def test_clear_state(self):
        # Create a submission, give it a score, and verify that score exists
        submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
        api.set_score(submission["uuid"], 11, 12)
        score = api.get_score(STUDENT_ITEM)
        self._assert_score(score, 11, 12)
        self.assertEqual(score['submission_uuid'], submission['uuid'])

        # Reset the score with clear_state=True
        # This should set the submission's score to None, and make it unavailable to get_submissions
        api.reset_score(
            STUDENT_ITEM["student_id"],
            STUDENT_ITEM["course_id"],
            STUDENT_ITEM["item_id"],
            clear_state=True,
        )
        score = api.get_score(STUDENT_ITEM)
        self.assertIsNone(score)
        subs = api.get_submissions(STUDENT_ITEM)
        self.assertEqual(subs, [])
コード例 #32
0
    def test_reset_then_add_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM["student_id"], self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["item_id"]
        )

        # Score the student again
        sub_api.set_score(submission["uuid"], 3, 4)

        # Expect that the new score is available
        score = sub_api.get_score(self.STUDENT_ITEM)
        self.assertEqual(score["points_earned"], 3)
        self.assertEqual(score["points_possible"], 4)

        scores = sub_api.get_scores(self.STUDENT_ITEM["course_id"], self.STUDENT_ITEM["student_id"])
        self.assertIn(self.STUDENT_ITEM["item_id"], scores)
        self.assertEqual(scores[self.STUDENT_ITEM["item_id"]], (3, 4))
コード例 #33
0
ファイル: enrollment.py プロジェクト: jswope00/griffinx
def reset_student_attempts(course_id,
                           student,
                           module_state_key,
                           delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    # Reset the student's score in the submissions API
    # Currently this is used only by open assessment (ORA 2)
    # We need to do this *before* retrieving the `StudentModule` model,
    # because it's possible for a score to exist even if no student module exists.
    if delete_module:
        sub_api.reset_score(
            anonymous_id_for_user(student, course_id),
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key)

    if delete_module:
        module_to_reset.delete()
    else:
        _reset_module_attempts(module_to_reset)
コード例 #34
0
def reset_student_attempts(course_id, student, module_state_key, delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    # Reset the student's score in the submissions API
    # Currently this is used only by open assessment (ORA 2)
    # We need to do this *before* retrieving the `StudentModule` model,
    # because it's possible for a score to exist even if no student module exists.
    if delete_module:
        sub_api.reset_score(
            anonymous_id_for_user(student, course_id),
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key
    )

    if delete_module:
        module_to_reset.delete()
    else:
        _reset_module_attempts(module_to_reset)
コード例 #35
0
    def test_override_after_reset_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM,
                                               'test answer')
        sub_api.set_score(submission['uuid'], 1, 10)

        # Reset score
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        sub_api.score_override(
            self.STUDENT_ITEM,
            5,
            10,
        )

        self.assertEqual(
            sub_api.get_score(self.STUDENT_ITEM)['points_earned'], 5)
        self.assertEqual(
            sub_api.get_score(self.STUDENT_ITEM)['points_possible'], 10)
コード例 #36
0
ファイル: sga.py プロジェクト: mitodl/edx-sga
    def clear_student_state(self, *args, **kwargs):
        # pylint: disable=unused-argument
        """
        For a given user, clears submissions and uploaded files for this XBlock.

        Staff users are able to delete a learner's state for a block in LMS. When that capability is
        used, the block's "clear_student_state" function is called if it exists.
        """
        student_id = kwargs['user_id']
        for submission in submissions_api.get_submissions(
                self.get_student_item_dict(student_id)
        ):
            submission_file_sha1 = submission['answer'].get('sha1')
            submission_filename = submission['answer'].get('filename')
            submission_file_path = self.file_storage_path(submission_file_sha1, submission_filename)
            if default_storage.exists(submission_file_path):
                default_storage.delete(submission_file_path)
            submissions_api.reset_score(
                student_id,
                self.block_course_id,
                self.block_id,
                clear_state=True
            )
コード例 #37
0
    def test_reset_then_add_score(self):
        # Create a submission for the student and score it
        submission = sub_api.create_submission(self.STUDENT_ITEM, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Reset scores
        sub_api.reset_score(
            self.STUDENT_ITEM['student_id'],
            self.STUDENT_ITEM['course_id'],
            self.STUDENT_ITEM['item_id'],
        )

        # Score the student again
        sub_api.set_score(submission['uuid'], 3, 4)

        # Expect that the new score is available
        score = sub_api.get_score(self.STUDENT_ITEM)
        self.assertEqual(score['points_earned'], 3)
        self.assertEqual(score['points_possible'], 4)

        scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id'])
        self.assertIn(self.STUDENT_ITEM['item_id'], scores)
        item_score = scores[self.STUDENT_ITEM['item_id']]
        self.assertEqual((item_score['points_earned'], item_score['points_possible']), (3, 4))
コード例 #38
0
def reset_student_attempts(course_id,
                           student,
                           module_state_key,
                           requesting_user,
                           delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    user_id = anonymous_id_for_user(student, course_id)
    requesting_user_id = anonymous_id_for_user(requesting_user, course_id)
    submission_cleared = False
    teams_enabled = False
    selected_teamset_id = None
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id,
                                           student,
                                           child,
                                           requesting_user,
                                           delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
        if delete_module:
            # Some blocks (openassessment) use StudentModule data as a key for internal submission data.
            # Inform these blocks of the reset and allow them to handle their data.
            clear_student_state = getattr(block, "clear_student_state", None)
            if callable(clear_student_state):
                with disconnect_submissions_signal_receiver(score_set):
                    clear_student_state(
                        user_id=user_id,
                        course_id=six.text_type(course_id),
                        item_id=six.text_type(module_state_key),
                        requesting_user_id=requesting_user_id)
                submission_cleared = True
        teams_enabled = getattr(block, 'teams_enabled', False)
        if teams_enabled:
            selected_teamset_id = getattr(block, 'selected_teamset_id', None)
    except ItemNotFoundError:
        block = None
        log.warning(
            u"Could not find %s in modulestore when attempting to reset attempts.",
            module_state_key)

    # Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already.
    # We need to do this before retrieving the `StudentModule` model, because a score may exist with no student module.

    # TODO: Should the LMS know about sub_api and call this reset, or should it generically call it on all of its
    # xblock services as well?  See JIRA ARCH-26.
    if delete_module and not submission_cleared:
        sub_api.reset_score(
            user_id,
            text_type(course_id),
            text_type(module_state_key),
        )

    def _reset_or_delete_module(studentmodule):
        if delete_module:
            studentmodule.delete()
            create_new_event_transaction_id()
            set_event_transaction_type(grades_events.STATE_DELETED_EVENT_TYPE)
            tracker.emit(
                six.text_type(grades_events.STATE_DELETED_EVENT_TYPE), {
                    'user_id':
                    six.text_type(student.id),
                    'course_id':
                    six.text_type(course_id),
                    'problem_id':
                    six.text_type(module_state_key),
                    'instructor_id':
                    six.text_type(requesting_user.id),
                    'event_transaction_id':
                    six.text_type(get_event_transaction_id()),
                    'event_transaction_type':
                    six.text_type(grades_events.STATE_DELETED_EVENT_TYPE),
                })
            if not submission_cleared:
                _fire_score_changed_for_block(
                    course_id,
                    student,
                    block,
                    module_state_key,
                )
        else:
            _reset_module_attempts(studentmodule)

    team = None
    if teams_enabled:
        from lms.djangoapps.teams.api import get_team_for_user_course_topic
        team = get_team_for_user_course_topic(student, str(course_id),
                                              selected_teamset_id)
    if team:
        modules_to_reset = StudentModule.objects.filter(
            student__teams=team,
            course_id=course_id,
            module_state_key=module_state_key)
        for module_to_reset in modules_to_reset:
            _reset_or_delete_module(module_to_reset)
        return
    else:
        # Teams are not enabled or the user does not have a team
        module_to_reset = StudentModule.objects.get(
            student_id=student.id,
            course_id=course_id,
            module_state_key=module_state_key)
        _reset_or_delete_module(module_to_reset)
コード例 #39
0
def reset_student_attempts(course_id, student, module_state_key, requesting_user, delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    user_id = anonymous_id_for_user(student, course_id)
    requesting_user_id = anonymous_id_for_user(requesting_user, course_id)
    submission_cleared = False
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id, student, child, requesting_user, delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
        if delete_module:
            # Some blocks (openassessment) use StudentModule data as a key for internal submission data.
            # Inform these blocks of the reset and allow them to handle their data.
            clear_student_state = getattr(block, "clear_student_state", None)
            if callable(clear_student_state):
                with disconnect_submissions_signal_receiver(score_set):
                    clear_student_state(
                        user_id=user_id,
                        course_id=unicode(course_id),
                        item_id=unicode(module_state_key),
                        requesting_user_id=requesting_user_id
                    )
                submission_cleared = True
    except ItemNotFoundError:
        block = None
        log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key)

    # Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already.
    # We need to do this before retrieving the `StudentModule` model, because a score may exist with no student module.

    # TODO: Should the LMS know about sub_api and call this reset, or should it generically call it on all of its
    # xblock services as well?  See JIRA ARCH-26.
    if delete_module and not submission_cleared:
        sub_api.reset_score(
            user_id,
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key
    )

    if delete_module:
        module_to_reset.delete()
        create_new_event_transaction_id()
        grade_update_root_type = 'edx.grades.problem.state_deleted'
        set_event_transaction_type(grade_update_root_type)
        tracker.emit(
            unicode(grade_update_root_type),
            {
                'user_id': unicode(student.id),
                'course_id': unicode(course_id),
                'problem_id': unicode(module_state_key),
                'instructor_id': unicode(requesting_user.id),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(grade_update_root_type),
            }
        )
        if not submission_cleared:
            _fire_score_changed_for_block(
                course_id,
                student,
                block,
                module_state_key,
            )
    else:
        _reset_module_attempts(module_to_reset)
コード例 #40
0
ファイル: enrollment.py プロジェクト: yewtzeee/edx-platform
def reset_student_attempts(course_id, student, module_state_key, requesting_user, delete_module=False):
    """
    Reset student attempts for a problem. Optionally deletes all student state for the specified problem.

    In the previous instructor dashboard it was possible to modify/delete
    modules that were not problems. That has been disabled for safety.

    `student` is a User
    `problem_to_reset` is the name of a problem e.g. 'L2Node1'.
    To build the module_state_key 'problem/' and course information will be appended to `problem_to_reset`.

    Raises:
        ValueError: `problem_state` is invalid JSON.
        StudentModule.DoesNotExist: could not load the student module.
        submissions.SubmissionError: unexpected error occurred while resetting the score in the submissions API.

    """
    user_id = anonymous_id_for_user(student, course_id)
    requesting_user_id = anonymous_id_for_user(requesting_user, course_id)
    submission_cleared = False
    try:
        # A block may have children. Clear state on children first.
        block = modulestore().get_item(module_state_key)
        if block.has_children:
            for child in block.children:
                try:
                    reset_student_attempts(course_id, student, child, requesting_user, delete_module=delete_module)
                except StudentModule.DoesNotExist:
                    # If a particular child doesn't have any state, no big deal, as long as the parent does.
                    pass
        if delete_module:
            # Some blocks (openassessment) use StudentModule data as a key for internal submission data.
            # Inform these blocks of the reset and allow them to handle their data.
            clear_student_state = getattr(block, "clear_student_state", None)
            if callable(clear_student_state):
                clear_student_state(
                    user_id=user_id,
                    course_id=unicode(course_id),
                    item_id=unicode(module_state_key),
                    requesting_user_id=requesting_user_id
                )
                submission_cleared = True
    except ItemNotFoundError:
        block = None
        log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key)

    # Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already.
    # We need to do this before retrieving the `StudentModule` model, because a score may exist with no student module.

    # TODO: Should the LMS know about sub_api and call this reset, or should it generically call it on all of its
    # xblock services as well?  See JIRA ARCH-26.
    if delete_module and not submission_cleared:
        sub_api.reset_score(
            user_id,
            course_id.to_deprecated_string(),
            module_state_key.to_deprecated_string(),
        )

    module_to_reset = StudentModule.objects.get(
        student_id=student.id,
        course_id=course_id,
        module_state_key=module_state_key
    )

    if delete_module:
        module_to_reset.delete()
        _fire_score_changed_for_block(course_id, student, block, module_state_key)
    else:
        _reset_module_attempts(module_to_reset)