EDUCATOR-5031: Delete 'Student State' for all team members (Part 2): Delete team member student module (#24187)
reset student module for teams
This commit is contained in:
@@ -247,6 +247,8 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user
|
||||
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)
|
||||
@@ -270,6 +272,9 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user
|
||||
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)
|
||||
@@ -286,36 +291,53 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user
|
||||
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(
|
||||
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,
|
||||
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:
|
||||
_reset_module_attempts(module_to_reset)
|
||||
# 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_module_attempts(studentmodule):
|
||||
|
||||
@@ -34,6 +34,8 @@ from lms.djangoapps.instructor.enrollment import (
|
||||
send_beta_role_email,
|
||||
unenroll_email
|
||||
)
|
||||
from lms.djangoapps.teams.models import CourseTeamMembership
|
||||
from lms.djangoapps.teams.tests.factories import CourseTeamFactory
|
||||
from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, get_mock_request
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user
|
||||
@@ -327,6 +329,12 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase):
|
||||
parent=cls.course,
|
||||
publish_item=True,
|
||||
)
|
||||
cls.team_enabled_ora = ItemFactory.create(
|
||||
parent=cls.parent,
|
||||
category="openassessment",
|
||||
teams_enabled=True,
|
||||
selected_teamset_id='final project teamset'
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstructorEnrollmentStudentModule, self).setUp()
|
||||
@@ -435,11 +443,145 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase):
|
||||
score = sub_api.get_score(student_item)
|
||||
self.assertIs(score, None)
|
||||
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
def setup_team(self):
|
||||
""" Set up a team with teammates and StudentModules """
|
||||
# Make users
|
||||
self.teammate_a = UserFactory()
|
||||
self.teammate_b = UserFactory()
|
||||
# This teammate has never opened the assignment so they don't have a state
|
||||
self.lazy_teammate = UserFactory()
|
||||
|
||||
# Enroll users in course, so we can add them to the team with add_user
|
||||
CourseEnrollment.enroll(self.user, self.course_key)
|
||||
CourseEnrollment.enroll(self.teammate_a, self.course_key)
|
||||
CourseEnrollment.enroll(self.teammate_b, self.course_key)
|
||||
CourseEnrollment.enroll(self.lazy_teammate, self.course_key)
|
||||
|
||||
# Make team
|
||||
self.team = CourseTeamFactory.create(
|
||||
course_id=self.course_key,
|
||||
topic_id=self.team_enabled_ora.selected_teamset_id
|
||||
)
|
||||
# Add users to team
|
||||
self.team.add_user(self.user)
|
||||
self.team.add_user(self.teammate_a)
|
||||
self.team.add_user(self.teammate_b)
|
||||
self.team.add_user(self.lazy_teammate)
|
||||
|
||||
# Create student modules for everyone but lazy_student
|
||||
self.team_state_dict = {
|
||||
'attempts': 1,
|
||||
'saved_files_descriptions': ['summary', 'proposal', 'diagrams'],
|
||||
'saved_files_sizes': [1364677, 958418],
|
||||
'saved_files_names': ['case_study_abstract.txt', 'design_prop.pdf', 'diagram1.png']
|
||||
}
|
||||
team_state = json.dumps(self.team_state_dict)
|
||||
|
||||
StudentModule.objects.create(
|
||||
student=self.user,
|
||||
course_id=self.course_key,
|
||||
module_state_key=self.team_enabled_ora.location,
|
||||
state=team_state,
|
||||
)
|
||||
StudentModule.objects.create(
|
||||
student=self.teammate_a,
|
||||
course_id=self.course_key,
|
||||
module_state_key=self.team_enabled_ora.location,
|
||||
state=team_state,
|
||||
)
|
||||
StudentModule.objects.create(
|
||||
student=self.teammate_b,
|
||||
course_id=self.course_key,
|
||||
module_state_key=self.team_enabled_ora.location,
|
||||
state=team_state,
|
||||
)
|
||||
|
||||
def test_reset_team_attempts(self):
|
||||
self.setup_team()
|
||||
team_ora_location = self.team_enabled_ora.location
|
||||
# All teammates should have a student module (except lazy_teammate)
|
||||
self.assertIsNotNone(self.get_student_module(self.user, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_a, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_b, team_ora_location))
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
reset_student_attempts(self.course_key, self.user, team_ora_location, requesting_user=self.user)
|
||||
|
||||
# Everyone's state should have had the attempts set to zero but otherwise unchanged
|
||||
attempt_reset_team_state_dict = dict(self.team_state_dict)
|
||||
attempt_reset_team_state_dict['attempts'] = 0
|
||||
|
||||
def _assert_student_module(user):
|
||||
student_module = self.get_student_module(user, team_ora_location)
|
||||
self.assertIsNotNone(student_module)
|
||||
student_state = json.loads(student_module.state)
|
||||
self.assertDictEqual(student_state, attempt_reset_team_state_dict)
|
||||
|
||||
_assert_student_module(self.user)
|
||||
_assert_student_module(self.teammate_a)
|
||||
_assert_student_module(self.teammate_b)
|
||||
# Still should have no state
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
@patch('lms.djangoapps.grades.signals.handlers.PROBLEM_WEIGHTED_SCORE_CHANGED.send')
|
||||
def test_delete_team_attempts(self, _mock_signal):
|
||||
self.setup_team()
|
||||
team_ora_location = self.team_enabled_ora.location
|
||||
# All teammates should have a student module (except lazy_teammate)
|
||||
self.assertIsNotNone(self.get_student_module(self.user, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_a, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_b, team_ora_location))
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
reset_student_attempts(
|
||||
self.course_key, self.user, team_ora_location, requesting_user=self.user, delete_module=True
|
||||
)
|
||||
|
||||
# No one should have a state now
|
||||
self.assert_no_student_module(self.user, team_ora_location)
|
||||
self.assert_no_student_module(self.teammate_a, team_ora_location)
|
||||
self.assert_no_student_module(self.teammate_b, team_ora_location)
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
@patch('lms.djangoapps.grades.signals.handlers.PROBLEM_WEIGHTED_SCORE_CHANGED.send')
|
||||
def test_delete_team_attempts_no_team_fallthrough(self, _mock_signal):
|
||||
self.setup_team()
|
||||
team_ora_location = self.team_enabled_ora.location
|
||||
|
||||
# Remove self.user from the team
|
||||
CourseTeamMembership.objects.get(user=self.user, team=self.team).delete()
|
||||
|
||||
# All teammates should have a student module (except lazy_teammate)
|
||||
self.assertIsNotNone(self.get_student_module(self.user, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_a, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_b, team_ora_location))
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
reset_student_attempts(
|
||||
self.course_key, self.user, team_ora_location, requesting_user=self.user, delete_module=True
|
||||
)
|
||||
|
||||
# self.user should be deleted, but no other teammates should be affected.
|
||||
self.assert_no_student_module(self.user, team_ora_location)
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_a, team_ora_location))
|
||||
self.assertIsNotNone(self.get_student_module(self.teammate_b, team_ora_location))
|
||||
self.assert_no_student_module(self.lazy_teammate, team_ora_location)
|
||||
|
||||
def assert_no_student_module(self, user, location):
|
||||
""" Assert that there is no student module for the given user and item for self.course_key """
|
||||
with self.assertRaises(StudentModule.DoesNotExist):
|
||||
self.get_student_module(user, location)
|
||||
|
||||
def get_student_module(self, user, location):
|
||||
""" Get the student module for the given user and item for self.course_key"""
|
||||
return StudentModule.objects.get(
|
||||
student=user, course_id=self.course_key, module_state_key=location
|
||||
)
|
||||
|
||||
def get_state(self, location):
|
||||
"""Reload and grab the module state from the database"""
|
||||
return StudentModule.objects.get(
|
||||
student=self.user, course_id=self.course_key, module_state_key=location
|
||||
).state
|
||||
return self.get_student_module(self.user, location).state
|
||||
|
||||
def test_reset_student_attempts_children(self):
|
||||
parent_state = json.loads(self.get_state(self.parent.location))
|
||||
|
||||
Reference in New Issue
Block a user