diff --git a/lms/djangoapps/discussion/django_comment_client/tests/group_id.py b/lms/djangoapps/discussion/django_comment_client/tests/group_id.py index 8f4e4b9a391b7544c88d87d062cddc1b64881736..32b62414a4490964cfc174486cbc2a73956a3347 100644 --- a/lms/djangoapps/discussion/django_comment_client/tests/group_id.py +++ b/lms/djangoapps/discussion/django_comment_client/tests/group_id.py @@ -170,7 +170,10 @@ class NonCohortedTopicGroupIdTestMixin(GroupIdAssertionMixin): self._assert_comments_service_called_without_group_id(mock_request) def test_team_discussion_id_not_cohorted(self, mock_request): - team = CourseTeamFactory(course_id=self.course.id) + team = CourseTeamFactory( + course_id=self.course.id, + topic_id='topic-id' + ) team.add_user(self.student) self.call_view(mock_request, team.discussion_topic_id, self.student, None) diff --git a/lms/djangoapps/discussion/django_comment_client/tests/utils.py b/lms/djangoapps/discussion/django_comment_client/tests/utils.py index 03fb9c94ca69b03a021b2ad7fda506b3811f3fe5..600ea776a6a65e60956db1f16e606b76ba2a55bd 100644 --- a/lms/djangoapps/discussion/django_comment_client/tests/utils.py +++ b/lms/djangoapps/discussion/django_comment_client/tests/utils.py @@ -12,6 +12,7 @@ from openedx.core.djangoapps.django_comment_common.utils import ( seed_permissions_roles, set_course_discussion_settings ) +from openedx.core.lib.teams_config import TeamsConfig from student.tests.factories import CourseEnrollmentFactory, UserFactory from util.testing import UrlResetMixin from xmodule.modulestore import ModuleStoreEnum @@ -44,7 +45,14 @@ class CohortedTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCa cohort_config={ "cohorted": True, "cohorted_discussions": ["cohorted_topic"] - } + }, + teams_configuration=TeamsConfig({ + 'topics': [{ + 'id': 'topic-id', + 'name': 'Topic Name', + 'description': 'Topic', + }] + }) ) cls.course.discussion_topics["cohorted topic"] = {"id": "cohorted_topic"} cls.course.discussion_topics["non-cohorted topic"] = {"id": "non_cohorted_topic"} diff --git a/lms/djangoapps/discussion/tests/test_views.py b/lms/djangoapps/discussion/tests/test_views.py index dd57f67d44649ad003b97d335f2a95b43f108239..1e19c7bf3153fe5e9131d0ac2cb3820c56fa5a8f 100644 --- a/lms/djangoapps/discussion/tests/test_views.py +++ b/lms/djangoapps/discussion/tests/test_views.py @@ -53,6 +53,7 @@ from openedx.core.djangoapps.django_comment_common.models import ( from openedx.core.djangoapps.django_comment_common.utils import ThreadContext, seed_permissions_roles from openedx.core.djangoapps.util.testing import ContentGroupTestCase from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES +from openedx.core.lib.teams_config import TeamsConfig from openedx.features.content_type_gating.models import ContentTypeGatingConfig from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired from student.roles import CourseStaffRole, UserBasedRole @@ -1305,7 +1306,18 @@ class InlineDiscussionTestCase(ForumsEnableMixin, ModuleStoreTestCase): def setUp(self): super(InlineDiscussionTestCase, self).setUp() - self.course = CourseFactory.create(org="TestX", number="101", display_name="Test Course") + self.course = CourseFactory.create( + org="TestX", + number="101", + display_name="Test Course", + teams_configuration=TeamsConfig({ + 'topics': [{ + 'id': 'topic_id', + 'name': 'A topic', + 'description': 'A topic', + }] + }) + ) self.student = UserFactory.create() CourseEnrollmentFactory(user=self.student, course_id=self.course.id) self.discussion1 = ItemFactory.create( @@ -1334,7 +1346,7 @@ class InlineDiscussionTestCase(ForumsEnableMixin, ModuleStoreTestCase): def test_context(self, mock_request): team = CourseTeamFactory( name='Team Name', - topic_id='A topic', + topic_id='topic_id', course_id=self.course.id, discussion_topic_id=self.discussion1.discussion_id ) @@ -2148,7 +2160,15 @@ class ThreadViewedEventTestCase(EventTestMixin, ForumsEnableMixin, UrlResetMixin def setUp(self): # pylint: disable=arguments-differ super(ThreadViewedEventTestCase, self).setUp('eventtracking.tracker') - self.course = CourseFactory.create() + self.course = CourseFactory.create( + teams_configuration=TeamsConfig({ + 'topics': [{ + 'id': 'arbitrary-topic-id', + 'name': 'arbitrary-topic-name', + 'description': 'arbitrary-topic-desc' + }] + }) + ) seed_permissions_roles(self.course.id) PASSWORD = 'test' diff --git a/lms/djangoapps/teams/api.py b/lms/djangoapps/teams/api.py index f40baeab233ba1a445316caba466d07951783e04..83ff077f01935271d238f55547bee617862e3273 100644 --- a/lms/djangoapps/teams/api.py +++ b/lms/djangoapps/teams/api.py @@ -13,8 +13,10 @@ from opaque_keys.edx.keys import CourseKey from course_modes.models import CourseMode from lms.djangoapps.discussion.django_comment_client.utils import has_discussion_privileges from lms.djangoapps.teams.models import CourseTeam +from openedx.core.lib.teams_config import TeamsetType from student.models import CourseEnrollment from student.roles import CourseInstructorRole, CourseStaffRole +from xmodule.modulestore.django import modulestore logger = logging.getLogger(__name__) @@ -61,33 +63,41 @@ def get_team_by_discussion(discussion_id): return None +def _get_teamset_type(course_id, teamset_id): + """ + Helper to get teamset type from a course_id and teamset_id. + Assumes course_id exists and teamset_id is defined + """ + course = modulestore().get_course(course_id) + return course.teams_configuration.teamsets_by_id[teamset_id].teamset_type + + def is_team_discussion_private(team): """ - This is the function to check if the team is configured to have its discussion - to be private. We need a way to check the setting on the team. - This function also provide ways to toggle the setting of discussion visibility on the - individual team level. - To be followed up by MST-25 + Checks to see if the team is configured to have its discussion to be private """ - return getattr(team, 'is_discussion_private', False) + if not team: + return False + return _get_teamset_type(team.course_id, team.topic_id) == TeamsetType.private_managed -def is_instructor_managed_team(team): # pylint: disable=unused-argument +def is_instructor_managed_team(team): """ Return true if the team is managed by instructors. - For now always return false, will complete the logic later. - TODO MST-25 """ - return False + if not team: + return False + return is_instructor_managed_topic(team.course_id, team.topic_id) -def is_instructor_managed_topic(topic): # pylint: disable=unused-argument +def is_instructor_managed_topic(course_id, topic): """ Return true if the topic is managed by instructors. - For now always return false, will complete the logic later. - TODO MST-25 """ - return False + if not course_id or not topic: + return False + managed_types = (TeamsetType.private_managed, TeamsetType.public_managed) + return _get_teamset_type(course_id, topic) in managed_types def user_is_a_team_member(user, team): @@ -240,7 +250,7 @@ def can_user_create_team_in_topic(user, course_id, topic_id): Assumes that user is enrolled in course run. """ return ( - (not is_instructor_managed_topic(topic_id)) or + (not is_instructor_managed_topic(course_id, topic_id)) or has_course_staff_privileges(user, course_id) ) diff --git a/lms/djangoapps/teams/tests/test_api.py b/lms/djangoapps/teams/tests/test_api.py index 4e29a683f9037f214114b1edef87e13f9d3b7250..3ba360277ef1008cd62d7f8b8b6d63fdfe410dc9 100644 --- a/lms/djangoapps/teams/tests/test_api.py +++ b/lms/djangoapps/teams/tests/test_api.py @@ -3,7 +3,6 @@ Tests for Python APIs of the Teams app """ -import unittest from uuid import uuid4 import ddt @@ -14,15 +13,18 @@ from course_modes.models import CourseMode from lms.djangoapps.teams import api as teams_api from lms.djangoapps.teams.models import CourseTeam from lms.djangoapps.teams.tests.factories import CourseTeamFactory +from openedx.core.lib.teams_config import TeamsConfig, TeamsetType from student.models import CourseEnrollment from student.roles import CourseStaffRole from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory COURSE_KEY1 = CourseKey.from_string('edx/history/1') -COURSE_KEY2 = CourseKey.from_string('edx/history/2') +COURSE_KEY2 = CourseKey.from_string('edx/math/1') TOPIC1 = 'topic-1' TOPIC2 = 'topic-2' +TOPIC3 = 'topic-3' DISCUSSION_TOPIC_ID = uuid4().hex @@ -40,6 +42,34 @@ class PythonAPITests(SharedModuleStoreTestCase): cls.user3 = UserFactory.create(username='user3') cls.user4 = UserFactory.create(username='user4') + topic_data = [ + (TOPIC1, TeamsetType.private_managed.value), + (TOPIC2, TeamsetType.open.value), + (TOPIC3, TeamsetType.public_managed.value) + ] + topics = [ + { + 'id': topic_id, + 'name': 'name-' + topic_id, + 'description': 'desc-' + topic_id, + 'type': teamset_type + } for topic_id, teamset_type in topic_data + ] + teams_config_1 = TeamsConfig({'topics': [topics[0]]}) + teams_config_2 = TeamsConfig({'topics': [topics[1], topics[2]]}) + cls.course1 = CourseFactory( + org=COURSE_KEY1.org, + course=COURSE_KEY1.course, + run=COURSE_KEY1.run, + teams_configuration=teams_config_1, + ) + cls.course2 = CourseFactory( + org=COURSE_KEY2.org, + course=COURSE_KEY2.course, + run=COURSE_KEY2.run, + teams_configuration=teams_config_2, + ) + for user in (cls.user1, cls.user2, cls.user3, cls.user4): CourseEnrollmentFactory.create(user=user, course_id=COURSE_KEY1) @@ -63,6 +93,7 @@ class PythonAPITests(SharedModuleStoreTestCase): team_id='team2a', topic_id=TOPIC2 ) + cls.team3 = CourseTeamFactory(course_id=COURSE_KEY2, team_id='team3', topic_id=TOPIC3) cls.team1.add_user(cls.user1) cls.team1.add_user(cls.user2) @@ -78,13 +109,23 @@ class PythonAPITests(SharedModuleStoreTestCase): team = teams_api.get_team_by_discussion(DISCUSSION_TOPIC_ID) self.assertEqual(team, self.team1) - @unittest.skip("This functionality is not yet implemented") def test_is_team_discussion_private_is_private(self): self.assertTrue(teams_api.is_team_discussion_private(self.team1)) def test_is_team_discussion_private_is_public(self): self.assertFalse(teams_api.is_team_discussion_private(None)) self.assertFalse(teams_api.is_team_discussion_private(self.team2)) + self.assertFalse(teams_api.is_team_discussion_private(self.team3)) + + def test_is_instructor_managed_team(self): + self.assertTrue(teams_api.is_instructor_managed_team(self.team1)) + self.assertFalse(teams_api.is_instructor_managed_team(self.team2)) + self.assertTrue(teams_api.is_instructor_managed_team(self.team3)) + + def test_is_instructor_managed_topic(self): + self.assertTrue(teams_api.is_instructor_managed_topic(COURSE_KEY1, TOPIC1)) + self.assertFalse(teams_api.is_instructor_managed_topic(COURSE_KEY2, TOPIC2)) + self.assertTrue(teams_api.is_instructor_managed_topic(COURSE_KEY2, TOPIC3)) def test_user_is_a_team_member(self): self.assertTrue(teams_api.user_is_a_team_member(self.user1, self.team1)) diff --git a/lms/djangoapps/teams/tests/test_views.py b/lms/djangoapps/teams/tests/test_views.py index 9dbd1a1b5ec2cd1ef12988c602088380f04f98cb..9ae398d1082472308e6f20875b6ce078a7e82f64 100644 --- a/lms/djangoapps/teams/tests/test_views.py +++ b/lms/djangoapps/teams/tests/test_views.py @@ -886,7 +886,7 @@ class TestCreateTeamAPI(EventTestMixin, TeamAPITestCase): name="Fully specified team", course=self.test_course_1, description="Another fantastic team", - topic_id='great-topic', + topic_id='topic_1', country='CA', language='fr' ), user=creator) @@ -930,7 +930,7 @@ class TestCreateTeamAPI(EventTestMixin, TeamAPITestCase): 'name': 'Fully specified team', 'language': 'fr', 'country': 'CA', - 'topic_id': 'great-topic', + 'topic_id': 'topic_1', 'course_id': str(self.test_course_1.id), 'description': 'Another fantastic team', 'organization_protected': False, diff --git a/lms/djangoapps/teams/views.py b/lms/djangoapps/teams/views.py index e9146b3b111717ee6484702ee2f6425ad8bb624a..9b6b83eb807072edeb276286b9d4c10e8cf3d980 100644 --- a/lms/djangoapps/teams/views.py +++ b/lms/djangoapps/teams/views.py @@ -527,7 +527,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView): return Response(status=status.HTTP_403_FORBIDDEN) topic_id = request.data.get('topic_id') - if not can_user_create_team_in_topic(request.user, course_id, topic_id): + if not can_user_create_team_in_topic(request.user, course_key, topic_id): return Response( build_api_error(ugettext_noop("You can't create a team in an instructor managed topic.")), status=status.HTTP_403_FORBIDDEN diff --git a/openedx/core/djangoapps/util/testing.py b/openedx/core/djangoapps/util/testing.py index 42e397d103896daaa6cbedce2b8c398a5ca6b325..24178f4aad35bcf81f6a1d6f1a75026f81df3f45 100644 --- a/openedx/core/djangoapps/util/testing.py +++ b/openedx/core/djangoapps/util/testing.py @@ -10,6 +10,7 @@ from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory from openedx.core.djangoapps.django_comment_common.models import Role from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory +from openedx.core.lib.teams_config import TeamsConfig from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -51,7 +52,14 @@ class ContentGroupTestCase(ModuleStoreTestCase): }] }, cohort_config={'cohorted': True}, - discussion_topics={} + discussion_topics={}, + teams_configuration=TeamsConfig({ + 'topics': [{ + 'id': 'topic_id', + 'name': 'topic_name', + 'description': 'topic_desc', + }] + }) ) seed_permissions_roles(self.course.id)