feat: add open_managed team type (#33672)

Ref: https://openedx.atlassian.net/wiki/spaces/COMM/pages/3885760525/Open+Managed+Group+Type
This commit is contained in:
Cristhian Garcia
2024-02-14 10:30:19 -05:00
committed by GitHub
parent f76b6b4e10
commit 5a36fa9163
8 changed files with 33 additions and 5 deletions

View File

@@ -357,7 +357,7 @@ class CourseMetadata:
"""
error_list = []
valid_teamset_types = [TeamsetType.open.value, TeamsetType.public_managed.value,
TeamsetType.private_managed.value]
TeamsetType.private_managed.value, TeamsetType.open_managed.value]
valid_keys = {'id', 'name', 'description', 'max_team_size', 'type'}
teamset_type = topic_settings.get('type', {})
if teamset_type:

View File

@@ -41,6 +41,12 @@ working_config_block = {
"type": "private_managed",
"description": "Private Topic 2 desc",
"name": "Private Topic 2 Name"
},
{
"id": "open_managed_topic_1_id",
"type": "open_managed",
"description": "Open Managed Topic 1 desc",
"name": "Open Managed Topic 1 Name"
}
]
}

View File

@@ -44,13 +44,14 @@
isMember = TeamUtils.isUserMemberOfTeam(memberships, this.context.userInfo.username),
isAdminOrStaff = this.context.userInfo.privileged || this.context.userInfo.staff,
isInstructorManagedTopic = TeamUtils.isInstructorManagedTopic(this.topic.attributes.type),
canJoinTeam = TeamUtils.canJoinTeam(this.context.userInfo, this.topic.attributes.type),
maxTeamSize = this.topic.getMaxTeamSize(this.context.courseMaxTeamSize);
// Assignments URL isn't provided if team assignments shouldn't be shown
// so we can treat it like a toggle
var showAssignments = !!this.context.teamsAssignmentsUrl;
var showLeaveLink = isMember && (isAdminOrStaff || !isInstructorManagedTopic);
var showLeaveLink = isMember && (isAdminOrStaff || !isInstructorManagedTopic || canJoinTeam);
HtmlUtils.setHtml(
this.$el,

View File

@@ -47,6 +47,8 @@
} else if (!teamHasSpace) {
showJoinButton = false;
message = view.teamFullMessage;
} else if (info.canJoinTeam) {
showJoinButton = true;
} else if (!info.isAdminOrStaff && info.isInstructorManagedTopic) {
showJoinButton = false;
message = view.notJoinInstructorManagedTeam;
@@ -100,12 +102,14 @@
// this.topic.getMaxTeamSize() will return null for a managed team,
// but the size is considered to be arbitarily large.
var isInstructorManagedTopic = TeamUtils.isInstructorManagedTopic(this.topic.attributes.type);
var canJoinTeam = TeamUtils.canJoinTeam(this.context.userInfo, this.topic.attributes.type)
var teamHasSpace = isInstructorManagedTopic
|| (this.model.get('membership').length < this.topic.getMaxTeamSize(courseMaxTeamSize));
info.memberOfCurrentTeam = TeamUtils.isUserMemberOfTeam(this.model.get('membership'), username);
info.isAdminOrStaff = this.context.userInfo.privileged || this.context.userInfo.staff;
info.isInstructorManagedTopic = isInstructorManagedTopic;
info.canJoinTeam = canJoinTeam;
if (info.memberOfCurrentTeam) {
info.alreadyInTeamset = true;

View File

@@ -83,6 +83,10 @@
return topicType.toLowerCase() !== 'open';
},
canJoinTeam: function(userInfo, topicType = '') {
return userInfo.privileged || userInfo.staff || topicType.includes("open");
},
/** Shows info/error banner for team membership CSV upload
* @param: content - string or array for display
* @param: isError - true sets error styling, false/none uses info styling

View File

@@ -24,6 +24,7 @@ COURSE_KEY2 = CourseKey.from_string('course-v1:edx+math+1')
TOPIC1 = 'topic-1'
TOPIC2 = 'topic-2'
TOPIC3 = 'topic-3'
TOPIC4 = 'topic-4'
DISCUSSION_TOPIC_ID = uuid4().hex
@@ -44,7 +45,8 @@ class PythonAPITests(SharedModuleStoreTestCase):
topic_data = [
(TOPIC1, TeamsetType.private_managed.value),
(TOPIC2, TeamsetType.open.value),
(TOPIC3, TeamsetType.public_managed.value)
(TOPIC3, TeamsetType.public_managed.value),
(TOPIC4, TeamsetType.open_managed.value),
]
topics = [
{
@@ -55,7 +57,7 @@ class PythonAPITests(SharedModuleStoreTestCase):
} for topic_id, teamset_type in topic_data
]
teams_config_1 = TeamsConfig({'topics': [topics[0]]})
teams_config_2 = TeamsConfig({'topics': [topics[1], topics[2]]})
teams_config_2 = TeamsConfig({'topics': [topics[1], topics[2], topics[3]]})
cls.course1 = CourseFactory(
org=COURSE_KEY1.org,
course=COURSE_KEY1.course,
@@ -93,10 +95,12 @@ class PythonAPITests(SharedModuleStoreTestCase):
topic_id=TOPIC2
)
cls.team3 = CourseTeamFactory(course_id=COURSE_KEY2, team_id='team3', topic_id=TOPIC3)
cls.team4 = CourseTeamFactory(course_id=COURSE_KEY2, team_id='team4', topic_id=TOPIC4)
cls.team1.add_user(cls.user1)
cls.team1.add_user(cls.user2)
cls.team2.add_user(cls.user3)
cls.team4.add_user(cls.user3)
cls.team1a.add_user(cls.user4)
cls.team2a.add_user(cls.user4)
@@ -122,21 +126,25 @@ class PythonAPITests(SharedModuleStoreTestCase):
assert not teams_api.is_team_discussion_private(None)
assert not teams_api.is_team_discussion_private(self.team2)
assert not teams_api.is_team_discussion_private(self.team3)
assert not teams_api.is_team_discussion_private(self.team4)
def test_is_instructor_managed_team(self):
assert teams_api.is_instructor_managed_team(self.team1)
assert not teams_api.is_instructor_managed_team(self.team2)
assert teams_api.is_instructor_managed_team(self.team3)
assert not teams_api.is_instructor_managed_team(self.team4)
def test_is_instructor_managed_topic(self):
assert teams_api.is_instructor_managed_topic(COURSE_KEY1, TOPIC1)
assert not teams_api.is_instructor_managed_topic(COURSE_KEY2, TOPIC2)
assert teams_api.is_instructor_managed_topic(COURSE_KEY2, TOPIC3)
assert not teams_api.is_instructor_managed_topic(COURSE_KEY2, TOPIC4)
def test_user_is_a_team_member(self):
assert teams_api.user_is_a_team_member(self.user1, self.team1)
assert not teams_api.user_is_a_team_member(self.user1, None)
assert not teams_api.user_is_a_team_member(self.user1, self.team2)
assert not teams_api.user_is_a_team_member(self.user1, self.team4)
def test_private_discussion_visible_by_user(self):
assert teams_api.discussion_visible_by_user(DISCUSSION_TOPIC_ID, self.user1)
@@ -147,6 +155,7 @@ class PythonAPITests(SharedModuleStoreTestCase):
assert teams_api.discussion_visible_by_user(self.team2.discussion_topic_id, self.user1)
assert teams_api.discussion_visible_by_user(self.team2.discussion_topic_id, self.user2)
assert teams_api.discussion_visible_by_user('DO_NOT_EXISTS', self.user3)
assert teams_api.discussion_visible_by_user(self.team4.discussion_topic_id, self.user3)
@ddt.unpack
@ddt.data(

View File

@@ -197,7 +197,10 @@ class TeamsDashboardView(GenericAPIView):
"teams": user_teams_data
},
"has_open_teamset": bool(teamset_counts_by_type[TeamsetType.open.value]),
"has_public_managed_teamset": bool(teamset_counts_by_type[TeamsetType.public_managed.value]),
"has_public_managed_teamset": bool(
teamset_counts_by_type[TeamsetType.public_managed.value] +
teamset_counts_by_type[TeamsetType.open_managed.value]
),
"has_managed_teamset": bool(
teamset_counts_by_type[TeamsetType.public_managed.value] +
teamset_counts_by_type[TeamsetType.private_managed.value]

View File

@@ -304,6 +304,7 @@ class TeamsetType(Enum):
open = "open"
public_managed = "public_managed"
private_managed = "private_managed"
open_managed = "open_managed"
@classmethod
def get_default(cls):