Beispiel #1
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)
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(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),
        )

    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()
        set_event_transaction_type(grades_events.STATE_DELETED_EVENT_TYPE)
        tracker.emit(
            unicode(grades_events.STATE_DELETED_EVENT_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(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(module_to_reset)