Skip to content

Commit ac34c81

Browse files
author
Matt Drayer
committed
mattdrayer/api-moderator-role-valiation: Confirm add/remove of role
1 parent 0581500 commit ac34c81

7 files changed

Lines changed: 101 additions & 46 deletions

File tree

lms/djangoapps/api_manager/courses/tests.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from django.contrib.auth.models import Group
1313
from django.core.cache import cache
14+
from django.core.exceptions import ObjectDoesNotExist
1415
from django.test import TestCase, Client
1516
from django.test.utils import override_settings
1617

@@ -1476,7 +1477,6 @@ def test_coursemodulecompletions_filters(self):
14761477

14771478
def test_coursemodulecompletions_get_invalid_course(self):
14781479
completion_uri = '{}/{}/completions/'.format(self.base_courses_uri, self.test_bogus_course_id)
1479-
print completion_uri
14801480
response = self.do_get(completion_uri)
14811481
self.assertEqual(response.status_code, 404)
14821482

@@ -1796,11 +1796,23 @@ def test_courses_roles_list_get(self):
17961796

17971797
# filter roleset by user
17981798
user_id = {'user_id': '{}'.format(self.users[0].id)}
1799-
test_uri = '{}?{}'.format(test_uri, urlencode(user_id))
1800-
response = self.do_get(test_uri)
1799+
user_filter_uri = '{}?{}'.format(test_uri, urlencode(user_id))
1800+
response = self.do_get(user_filter_uri)
18011801
self.assertEqual(response.status_code, 200)
18021802
self.assertEqual(len(response.data), 1)
18031803

1804+
# filter roleset by role
1805+
role = {'role': 'instructor'}
1806+
role_filter_uri = '{}?{}'.format(test_uri, urlencode(role))
1807+
response = self.do_get(role_filter_uri)
1808+
self.assertEqual(response.status_code, 200)
1809+
self.assertEqual(len(response.data), 1)
1810+
role = {'role': 'invalid_role'}
1811+
role_filter_uri = '{}?{}'.format(test_uri, urlencode(role))
1812+
response = self.do_get(role_filter_uri)
1813+
self.assertEqual(response.status_code, 200)
1814+
self.assertEqual(len(response.data), 0)
1815+
18041816
def test_courses_roles_list_get_invalid_course(self):
18051817
test_uri = '/api/courses/{}/roles/'.format(self.test_bogus_course_id)
18061818
response = self.do_get(test_uri)
@@ -1819,6 +1831,11 @@ def test_courses_roles_list_post(self):
18191831
self.assertEqual(response.status_code, 200)
18201832
self.assertEqual(len(response.data), 1)
18211833

1834+
# Confirm this user also has forum moderation permissions
1835+
role = Role.objects.get(course_id=self.course.id, name=FORUM_ROLE_MODERATOR)
1836+
has_role = role.users.get(id=self.users[0].id)
1837+
self.assertTrue(has_role)
1838+
18221839
def test_courses_roles_list_post_invalid_course(self):
18231840
test_uri = '/api/courses/{}/roles/'.format(self.test_bogus_course_id)
18241841
data = {'user_id': self.users[0].id, 'role': 'instructor'}
@@ -1844,17 +1861,23 @@ def test_courses_roles_users_detail_delete(self):
18441861
self.assertEqual(response.status_code, 201)
18451862

18461863
response = self.do_get(test_uri)
1847-
print response.data
18481864
self.assertEqual(len(response.data), 1)
18491865

18501866
delete_uri = '{}instructor/users/{}'.format(test_uri, self.users[0].id)
1851-
print delete_uri
18521867
response = self.do_delete(delete_uri)
18531868
self.assertEqual(response.status_code, 204)
18541869

18551870
response = self.do_get(test_uri)
18561871
self.assertEqual(len(response.data), 0)
18571872

1873+
# Confirm this user no longer has forum moderation permissions
1874+
role = Role.objects.get(course_id=self.course.id, name=FORUM_ROLE_MODERATOR)
1875+
try:
1876+
has_role = role.users.get(id=self.users[0].id)
1877+
self.assertTrue(False)
1878+
except ObjectDoesNotExist:
1879+
pass
1880+
18581881
def test_courses_roles_users_detail_delete_invalid_course(self):
18591882
test_uri = '/api/courses/{}/roles/'.format(self.test_bogus_course_id)
18601883
delete_uri = '{}instructor/users/{}'.format(test_uri, self.users[0].id)
@@ -1870,6 +1893,5 @@ def test_courses_roles_users_detail_delete_invalid_user(self):
18701893
def test_courses_roles_users_detail_delete_invalid_role(self):
18711894
test_uri = '/api/courses/{}/roles/'.format(unicode(self.course.id))
18721895
delete_uri = '{}invalid_role/users/{}'.format(test_uri, self.users[0].id)
1873-
print delete_uri
18741896
response = self.do_delete(delete_uri)
18751897
self.assertEqual(response.status_code, 404)

lms/djangoapps/api_manager/courses/views.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from lxml import etree
77
from StringIO import StringIO
88

9-
109
from django.conf import settings
1110
from django.contrib.auth.models import Group, User
1211
from django.core.exceptions import ObjectDoesNotExist
@@ -22,9 +21,9 @@
2221
from courseware.models import StudentModule
2322
from courseware.views import get_static_tab_contents
2423
from django_comment_common.models import FORUM_ROLE_MODERATOR
25-
from instructor.access import allow_access, revoke_access, update_forum_role
24+
from instructor.access import revoke_access, update_forum_role
2625
from student.models import CourseEnrollment, CourseEnrollmentAllowed
27-
from student.roles import CourseInstructorRole, CourseStaffRole, CourseObserverRole, UserBasedRole
26+
from student.roles import CourseAccessRole, CourseInstructorRole, CourseStaffRole, CourseObserverRole, UserBasedRole
2827

2928
from xmodule.modulestore.django import modulestore
3029

@@ -279,24 +278,30 @@ def _manage_role(course_descriptor, user, role, action):
279278
"""
280279
Helper method for managing course/forum roles
281280
"""
281+
supported_roles = ('instructor', 'staff', 'observer')
282282
forum_moderator_roles = ('instructor', 'staff')
283+
if role not in supported_roles:
284+
raise ValueError
283285
if action is 'allow':
284-
allow_access(course_descriptor, user, role)
286+
existing_role = CourseAccessRole.objects.filter(user=user, role=role, course_id=course_descriptor.id, org=course_descriptor.org)
287+
if not existing_role:
288+
new_role = CourseAccessRole(user=user, role=role, course_id=course_descriptor.id, org=course_descriptor.org)
289+
new_role.save()
285290
if role in forum_moderator_roles:
286291
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'allow')
287292
elif action is 'revoke':
293+
revoke_access(course_descriptor, user, role)
288294
if role in forum_moderator_roles:
289295
# There's a possibilty that the user may play more than one role in a course
290296
# And that more than one of these roles allow for forum moderation
291-
# So we need to confirm the current role is the only one for this user for this course
297+
# So we need to confirm the removed role was the only role for this user for this course
292298
# Before we can safely remove the corresponding forum moderator role
293299
user_instructor_courses = UserBasedRole(user, CourseInstructorRole.ROLE).courses_with_role()
294300
user_staff_courses = UserBasedRole(user, CourseStaffRole.ROLE).courses_with_role()
295301
queryset = user_instructor_courses | user_staff_courses
296302
queryset = queryset.filter(course_id=course_descriptor.id)
297-
if len(queryset) <= 1:
298-
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'allow')
299-
revoke_access(course_descriptor, user, role)
303+
if len(queryset) == 0:
304+
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'revoke')
300305

301306

302307
class CourseContentList(SecureAPIView):
@@ -1329,7 +1334,6 @@ def get_queryset(self):
13291334
stage = self.request.QUERY_PARAMS.get('stage', None)
13301335
course_id = self.kwargs['course_id']
13311336
course_descriptor, course_key, course_content = get_course(self.request, self.request.user, course_id) # pylint: disable=W0612
1332-
print course_descriptor
13331337
if not course_descriptor:
13341338
raise Http404
13351339
queryset = CourseModuleCompletion.objects.filter(course_id=course_key)
@@ -1712,6 +1716,10 @@ def get(self, request, course_id): # pylint: disable=W0613
17121716
if user_id:
17131717
response_data = list([item for item in response_data if int(item['id']) == int(user_id)])
17141718

1719+
role = self.request.QUERY_PARAMS.get('role', None)
1720+
if role:
1721+
response_data = list([item for item in response_data if item['role'] == role])
1722+
17151723
return Response(response_data, status=status.HTTP_200_OK)
17161724

17171725
def post(self, request, course_id):

lms/djangoapps/api_manager/groups/tests.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,6 @@ def test_group_courses_detail_get(self):
894894
response = self.do_post(test_uri, data)
895895
self.assertEqual(response.status_code, 201)
896896
test_uri = '{}/{}/courses/{}'.format(self.base_groups_uri, group_id, self.test_course_id)
897-
print test_uri
898897
response = self.do_get(test_uri)
899898
self.assertEqual(response.status_code, 200)
900899
confirm_uri = '{}{}/{}/courses/{}'.format(

lms/djangoapps/api_manager/users/tests.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
from urllib import urlencode
1313
from mock import patch
1414

15-
from django.utils.translation import ugettext as _
1615
from django.core.cache import cache
16+
from django.core.exceptions import ObjectDoesNotExist
1717
from django.test import TestCase, Client
1818
from django.test.utils import override_settings
19+
from django.utils.translation import ugettext as _
1920

2021
from capa.tests.response_xml_factory import StringResponseXMLFactory
2122
from courseware.tests.factories import StudentModuleFactory
@@ -1387,11 +1388,23 @@ def test_users_roles_list_get(self):
13871388

13881389
# filter roleset by course
13891390
course_id = {'course_id': '{}'.format(unicode(course3.id))}
1390-
test_uri = '{}?{}'.format(test_uri, urlencode(course_id))
1391-
response = self.do_get(test_uri)
1391+
course_filter_uri = '{}?{}'.format(test_uri, urlencode(course_id))
1392+
response = self.do_get(course_filter_uri)
13921393
self.assertEqual(response.status_code, 200)
13931394
self.assertEqual(response.data['count'], 1)
13941395

1396+
# filter roleset by role
1397+
role = {'role': 'instructor'}
1398+
role_filter_uri = '{}?{}'.format(test_uri, urlencode(role))
1399+
response = self.do_get(role_filter_uri)
1400+
self.assertEqual(response.status_code, 200)
1401+
self.assertEqual(response.data['count'], 1)
1402+
role = {'role': 'invalid_role'}
1403+
role_filter_uri = '{}?{}'.format(test_uri, urlencode(role))
1404+
response = self.do_get(role_filter_uri)
1405+
self.assertEqual(response.status_code, 200)
1406+
self.assertEqual(response.data['count'], 0)
1407+
13951408
def test_users_roles_list_get_invalid_user(self):
13961409
test_uri = '/api/users/23423/roles/'
13971410
response = self.do_get(test_uri)
@@ -1417,6 +1430,11 @@ def test_users_roles_list_post(self):
14171430
self.assertEqual(response.status_code, 200)
14181431
self.assertEqual(response.data['count'], 1)
14191432

1433+
# Confirm this user also has forum moderation permissions
1434+
role = Role.objects.get(course_id=self.course.id, name=FORUM_ROLE_MODERATOR)
1435+
has_role = role.users.get(id=self.user.id)
1436+
self.assertTrue(has_role)
1437+
14201438
def test_users_roles_list_post_invalid_user(self):
14211439
test_uri = '/api/users/2131/roles/'
14221440
data = {'course_id': unicode(self.course.id), 'role': 'instructor'}
@@ -1527,6 +1545,14 @@ def test_users_roles_courses_detail_delete(self):
15271545
response = self.do_get(test_uri)
15281546
self.assertEqual(response.data['count'], 0)
15291547

1548+
# Confirm this user no longer has forum moderation permissions
1549+
role = Role.objects.get(course_id=self.course.id, name=FORUM_ROLE_MODERATOR)
1550+
try:
1551+
has_role = role.users.get(id=self.user.id)
1552+
self.assertTrue(False)
1553+
except ObjectDoesNotExist:
1554+
pass
1555+
15301556
def test_users_roles_courses_detail_delete_invalid_course(self):
15311557
test_uri = '/api/users/{}/roles/'.format(self.user.id)
15321558
delete_uri = '{}instructor/courses/{}'.format(test_uri, self.test_bogus_course_id)

lms/djangoapps/api_manager/users/views.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
from courseware.models import StudentModule
2121
from courseware.views import get_module_for_descriptor, save_child_position, get_current_child
2222
from django_comment_common.models import FORUM_ROLE_MODERATOR
23-
from instructor.access import allow_access, revoke_access, update_forum_role
23+
from instructor.access import revoke_access, update_forum_role
2424
from lang_pref import LANGUAGE_KEY
2525
from lms.lib.comment_client.user import User as CommentUser
2626
from lms.lib.comment_client.utils import CommentClientRequestError
2727
from student.models import CourseEnrollment, PasswordHistory, UserProfile
28-
from student.roles import CourseInstructorRole, CourseObserverRole, CourseStaffRole, UserBasedRole
28+
from student.roles import CourseAccessRole, CourseInstructorRole, CourseObserverRole, CourseStaffRole, UserBasedRole
2929
from user_api.models import UserPreference
3030
from util.bad_request_rate_limiter import BadRequestRateLimiter
3131
from util.password_policy_validators import (
@@ -98,24 +98,30 @@ def _manage_role(course_descriptor, user, role, action):
9898
"""
9999
Helper method for managing course/forum roles
100100
"""
101+
supported_roles = ('instructor', 'staff', 'observer')
101102
forum_moderator_roles = ('instructor', 'staff')
103+
if role not in supported_roles:
104+
raise ValueError
102105
if action is 'allow':
103-
allow_access(course_descriptor, user, role)
106+
existing_role = CourseAccessRole.objects.filter(user=user, role=role, course_id=course_descriptor.id, org=course_descriptor.org)
107+
if not existing_role:
108+
new_role = CourseAccessRole(user=user, role=role, course_id=course_descriptor.id, org=course_descriptor.org)
109+
new_role.save()
104110
if role in forum_moderator_roles:
105111
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'allow')
106112
elif action is 'revoke':
113+
revoke_access(course_descriptor, user, role)
107114
if role in forum_moderator_roles:
108115
# There's a possibilty that the user may play more than one role in a course
109116
# And that more than one of these roles allow for forum moderation
110-
# So we need to confirm the current role is the only one for this user for this course
117+
# So we need to confirm the removed role was the only role for this user for this course
111118
# Before we can safely remove the corresponding forum moderator role
112119
user_instructor_courses = UserBasedRole(user, CourseInstructorRole.ROLE).courses_with_role()
113120
user_staff_courses = UserBasedRole(user, CourseStaffRole.ROLE).courses_with_role()
114121
queryset = user_instructor_courses | user_staff_courses
115122
queryset = queryset.filter(course_id=course_descriptor.id)
116-
if len(queryset) == 1:
117-
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'allow')
118-
revoke_access(course_descriptor, user, role)
123+
if len(queryset) == 0:
124+
update_forum_role(course_descriptor.id, user, FORUM_ROLE_MODERATOR, 'revoke')
119125

120126

121127
class UsersList(SecureListAPIView):
@@ -1140,6 +1146,10 @@ def get_queryset(self):
11401146
raise Http404
11411147
queryset = queryset.filter(course_id=course_key)
11421148

1149+
role = self.request.QUERY_PARAMS.get('role', None)
1150+
if role:
1151+
queryset = queryset.filter(role=role)
1152+
11431153
return queryset
11441154

11451155
def post(self, request, user_id):
@@ -1217,9 +1227,7 @@ def delete(self, request, user_id, role, course_id): # pylint: disable=W0613
12171227
return Response({}, status=status.HTTP_404_NOT_FOUND)
12181228

12191229
try:
1220-
revoke_access(course_descriptor, user, role)
1221-
if role in ('instructor', 'staff'):
1222-
update_forum_role(course_key, user, FORUM_ROLE_MODERATOR, 'revoke')
1230+
_manage_role(course_descriptor, user, role, 'revoke')
12231231
except ValueError:
12241232
return Response({}, status=status.HTTP_404_NOT_FOUND)
12251233

lms/djangoapps/projects/tests/test_workgroups.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def test_workgroups_list_post(self):
172172
response.data['id'],
173173
self.test_workgroup_name
174174
)
175-
cohort = get_cohort_by_name(self.test_project.course_id, cohort_name, CourseUserGroup.WORKGROUP)
175+
cohort = get_cohort_by_name(self.test_course.id, cohort_name, CourseUserGroup.WORKGROUP)
176176
self.assertIsNotNone(cohort)
177177

178178
def test_workgroups_detail_get(self):
@@ -266,7 +266,7 @@ def test_workgroups_users_post(self):
266266
response.data['id'],
267267
self.test_workgroup_name
268268
)
269-
cohort = get_cohort_by_name(self.test_project.course_id, cohort_name, CourseUserGroup.WORKGROUP)
269+
cohort = get_cohort_by_name(self.test_course.id, cohort_name, CourseUserGroup.WORKGROUP)
270270
self.assertIsNotNone(cohort)
271271
self.assertTrue(is_user_in_cohort(cohort, self.test_user.id, CourseUserGroup.WORKGROUP))
272272

@@ -296,15 +296,15 @@ def test_workgroups_users_post_with_cohort_backfill(self):
296296
)
297297

298298
# now let's remove existing cohort users
299-
cohort = get_cohort_by_name(self.test_project.course_id, cohort_name, CourseUserGroup.WORKGROUP)
299+
cohort = get_cohort_by_name(self.test_course.id, cohort_name, CourseUserGroup.WORKGROUP)
300300
self.assertTrue(is_user_in_cohort(cohort, self.test_user.id, CourseUserGroup.WORKGROUP))
301301

302302
remove_user_from_cohort(cohort, self.test_user.username, CourseUserGroup.WORKGROUP)
303303
self.assertFalse(is_user_in_cohort(cohort, self.test_user.id, CourseUserGroup.WORKGROUP))
304304

305305
# delete cohort
306-
delete_empty_cohort(self.test_project.course_id, cohort_name, CourseUserGroup.WORKGROUP)
307-
self.assertEqual(0, len(get_course_cohort_names(self.test_project.course_id, CourseUserGroup.WORKGROUP)))
306+
delete_empty_cohort(self.test_course.id, cohort_name, CourseUserGroup.WORKGROUP)
307+
self.assertEqual(0, len(get_course_cohort_names(self.test_course.id, CourseUserGroup.WORKGROUP)))
308308

309309
# add a 2nd user and make sure a discussion cohort was created and users were backfilled
310310
test_uri = '{}{}/'.format(self.test_workgroups_uri, str(response.data['id']))
@@ -314,7 +314,7 @@ def test_workgroups_users_post_with_cohort_backfill(self):
314314
self.assertEqual(response.status_code, 201)
315315

316316
# now inspect cohort and assert that things are as we anticipate (i.e. both users are in there)
317-
cohort = get_cohort_by_name(self.test_project.course_id, cohort_name, CourseUserGroup.WORKGROUP)
317+
cohort = get_cohort_by_name(self.test_course.id, cohort_name, CourseUserGroup.WORKGROUP)
318318
self.assertIsNotNone(cohort)
319319
self.assertTrue(is_user_in_cohort(cohort, self.test_user.id, CourseUserGroup.WORKGROUP))
320320
self.assertTrue(is_user_in_cohort(cohort, self.test_user2.id, CourseUserGroup.WORKGROUP))

lms/djangoapps/projects/views.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,28 +177,20 @@ def users(self, request, pk):
177177
return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)
178178
workgroup = self.get_object()
179179
workgroup.users.add(user)
180-
181-
# add user to the workgroup cohort
182-
course_descriptor, course_key, course_content = _get_course(self.request, user, workgroup.project.course_id) # pylint: disable=W0612
183-
cohort = get_cohort_by_name(course_key,
184-
workgroup.cohort_name, CourseUserGroup.WORKGROUP)
185-
add_user_to_cohort(cohort, user.username)
186-
187180
workgroup.save()
188181

189182
# add user to the workgroup cohort, create it if it doesn't exist (for cases where there is a legacy
190183
# workgroup)
184+
course_descriptor, course_key, course_content = _get_course(self.request, user, workgroup.project.course_id) # pylint: disable=W0612
191185
try:
192-
cohort = get_cohort_by_name(workgroup.project.course_id,
193-
workgroup.cohort_name, CourseUserGroup.WORKGROUP)
186+
cohort = get_cohort_by_name(course_key, workgroup.cohort_name, CourseUserGroup.WORKGROUP)
194187
add_user_to_cohort(cohort, user.username)
195188
except ObjectDoesNotExist:
196189
# This use case handles cases where a workgroup might have been created before
197190
# the notion of a cohorted discussion. So we need to backfill in the data
198-
cohort = add_cohort(workgroup.project.course_id, workgroup.cohort_name, CourseUserGroup.WORKGROUP)
191+
cohort = add_cohort(course_key, workgroup.cohort_name, CourseUserGroup.WORKGROUP)
199192
for workgroup_user in workgroup.users.all():
200193
add_user_to_cohort(cohort, workgroup_user.username)
201-
202194
return Response({}, status=status.HTTP_201_CREATED)
203195
else:
204196
user_id = request.DATA.get('id')

0 commit comments

Comments
 (0)