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:
Jansen Kantor
2020-08-07 13:59:13 -04:00
committed by GitHub
parent b04f784d7a
commit eed9ec8824
2 changed files with 195 additions and 31 deletions

View File

@@ -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):

View File

@@ -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))