EDUCATOR 5065 - updates
EDUCATOR 5065 - CR comments 3
This commit is contained in:
@@ -7,7 +7,11 @@ from collections import Counter
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from lms.djangoapps.teams.api import OrganizationProtectionStatus, user_organization_protection_status
|
||||
from lms.djangoapps.teams.api import (
|
||||
OrganizationProtectionStatus,
|
||||
user_organization_protection_status,
|
||||
ORGANIZATION_PROTECTED_MODES
|
||||
)
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
from lms.djangoapps.program_enrollments.models import ProgramEnrollment
|
||||
from student.models import CourseEnrollment
|
||||
@@ -110,8 +114,10 @@ class TeamMembershipImportManager(object):
|
||||
self.existing_course_team_memberships = {}
|
||||
self.existing_course_teams = {}
|
||||
self.user_count_by_team = Counter()
|
||||
self.user_enrollment_by_team = {}
|
||||
self.user_to_remove_by_team = Counter()
|
||||
self.number_of_learners_assigned = 0
|
||||
self.user_to_actual_enrollment_mode = {}
|
||||
|
||||
@property
|
||||
def import_succeeded(self):
|
||||
@@ -240,7 +246,7 @@ class TeamMembershipImportManager(object):
|
||||
if actual_enrollment_mode != supplied_enrollment.strip():
|
||||
self.validation_errors.append('User ' + user.username + ' enrollment mismatch.')
|
||||
return False
|
||||
|
||||
self.user_to_actual_enrollment_mode[user.id] = actual_enrollment_mode
|
||||
return True
|
||||
|
||||
def is_username_unique(self, username, usernames_found_so_far):
|
||||
@@ -294,6 +300,8 @@ class TeamMembershipImportManager(object):
|
||||
pass
|
||||
continue
|
||||
try:
|
||||
if not self.validate_compatible_enrollment_modes(user, team_name, teamset_id):
|
||||
return False
|
||||
# checks for a team inside a specific team set. This way team names can be duplicated across
|
||||
# teamsets
|
||||
team = self.existing_course_teams[(team_name, teamset_id)]
|
||||
@@ -306,6 +314,38 @@ class TeamMembershipImportManager(object):
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate_compatible_enrollment_modes(self, user, team_name, teamset_id):
|
||||
"""
|
||||
Validates that only students enrolled in a masters track are on a single team. Disallows mixing of masters
|
||||
with other enrollment modes on a single team.
|
||||
"""
|
||||
if(teamset_id, team_name) not in self.user_enrollment_by_team:
|
||||
self.user_enrollment_by_team[teamset_id, team_name] = set()
|
||||
self.user_enrollment_by_team[teamset_id, team_name].add(self.user_to_actual_enrollment_mode[user.id])
|
||||
if self.is_FERPA_bubble_breached(teamset_id, team_name):
|
||||
error_message = \
|
||||
'Team {} cannot have Master’s track users mixed with users in other tracks.'.format(team_name)
|
||||
self.add_error_and_check_if_max_exceeded(error_message)
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_FERPA_bubble_breached(self, teamset_id, team_name):
|
||||
"""
|
||||
Ensures that FERPA bubble is not breached.
|
||||
Checks that we are not trying to violate FERPA proctection by mixing masters
|
||||
track students with other enrollment tracks.
|
||||
"""
|
||||
|
||||
team_enrollment_modes = self.user_enrollment_by_team[teamset_id, team_name]
|
||||
protected_modes = set(ORGANIZATION_PROTECTED_MODES)
|
||||
|
||||
if team_enrollment_modes.isdisjoint(protected_modes):
|
||||
return False
|
||||
elif team_enrollment_modes.issubset(protected_modes):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def validate_proposed_team_size_wont_exceed_maximum(self, team_name, teamset_id):
|
||||
"""
|
||||
Validates that the number of users we want to add to a team won't exceed maximum team size.
|
||||
|
||||
@@ -2722,6 +2722,30 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
data={'csv': csv_file}, user='staff'
|
||||
)
|
||||
|
||||
def test_upload_invalid_multiple_student_enrollment_mismatch(self):
|
||||
audit_username = 'audit_user'
|
||||
masters_username_a = 'masters_a'
|
||||
masters_username_b = 'masters_b'
|
||||
self.create_and_enroll_student(username=audit_username, mode=CourseMode.AUDIT)
|
||||
self.create_and_enroll_student(username=masters_username_a, mode=CourseMode.MASTERS)
|
||||
self.create_and_enroll_student(username=masters_username_b, mode=CourseMode.MASTERS)
|
||||
|
||||
csv_content = 'user,mode,topic_1' + '\n'
|
||||
csv_content += '{},audit,team wind power'.format(audit_username) + '\n'
|
||||
csv_content += '{},masters,team wind power'.format(masters_username_a) + '\n'
|
||||
csv_content += '{},masters,team wind power'.format(masters_username_b) + '\n'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
response = self.make_call(reverse(
|
||||
'team_membership_bulk_management',
|
||||
args=[self.good_course_id]),
|
||||
400, method='post',
|
||||
data={'csv': csv_file}, user='staff'
|
||||
)
|
||||
response_text = json.loads(response.content.decode('utf-8'))
|
||||
expected_error = 'Team team wind power cannot have Master’s track users mixed with users in other tracks.'
|
||||
self.assertEqual(response_text['errors'][0], expected_error)
|
||||
|
||||
def test_upload_learners_exceed_max_team_size(self):
|
||||
csv_content = 'user,mode,topic_0,topic_1' + '\n'
|
||||
team1 = 'team wind power'
|
||||
|
||||
Reference in New Issue
Block a user