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)