Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
8a7fef07
Commit
8a7fef07
authored
Aug 28, 2014
by
Daniel Friedman
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4937 from edx/dan-f/improve-course-group-coverage
Improve test coverage for course groups
parents
87068c41
a01e57ee
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
942 additions
and
97 deletions
+942
-97
common/djangoapps/course_groups/cohorts.py
+9
-4
common/djangoapps/course_groups/tests/test_cohorts.py
+293
-25
common/djangoapps/course_groups/tests/test_views.py
+618
-56
common/djangoapps/course_groups/views.py
+21
-11
common/static/js/course_groups/cohorts.js
+1
-1
No files found.
common/djangoapps/course_groups/cohorts.py
View file @
8a7fef07
...
@@ -4,6 +4,7 @@ forums, and to the cohort admin views.
...
@@ -4,6 +4,7 @@ forums, and to the cohort admin views.
"""
"""
from
django.http
import
Http404
from
django.http
import
Http404
import
logging
import
logging
import
random
import
random
...
@@ -86,14 +87,14 @@ def is_commentable_cohorted(course_key, commentable_id):
...
@@ -86,14 +87,14 @@ def is_commentable_cohorted(course_key, commentable_id):
def
get_cohorted_commentables
(
course_key
):
def
get_cohorted_commentables
(
course_key
):
"""
"""
Given a course_key return a
list of strings representing cohorted commentables
Given a course_key return a
set of strings representing cohorted commentables.
"""
"""
course
=
courses
.
get_course_by_id
(
course_key
)
course
=
courses
.
get_course_by_id
(
course_key
)
if
not
course
.
is_cohorted
:
if
not
course
.
is_cohorted
:
# this is the easy case :)
# this is the easy case :)
ans
=
[]
ans
=
set
()
else
:
else
:
ans
=
course
.
cohorted_discussions
ans
=
course
.
cohorted_discussions
...
@@ -213,8 +214,13 @@ def add_cohort(course_key, name):
...
@@ -213,8 +214,13 @@ def add_cohort(course_key, name):
name
=
name
)
.
exists
():
name
=
name
)
.
exists
():
raise
ValueError
(
"Can't create two cohorts with the same name"
)
raise
ValueError
(
"Can't create two cohorts with the same name"
)
try
:
course
=
courses
.
get_course_by_id
(
course_key
)
except
Http404
:
raise
ValueError
(
"Invalid course_key"
)
return
CourseUserGroup
.
objects
.
create
(
return
CourseUserGroup
.
objects
.
create
(
course_id
=
course
_key
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
,
group_type
=
CourseUserGroup
.
COHORT
,
name
=
name
name
=
name
)
)
...
@@ -240,7 +246,6 @@ def add_user_to_cohort(cohort, username_or_email):
...
@@ -240,7 +246,6 @@ def add_user_to_cohort(cohort, username_or_email):
Raises:
Raises:
User.DoesNotExist if can't find user.
User.DoesNotExist if can't find user.
ValueError if user already present in this cohort.
ValueError if user already present in this cohort.
"""
"""
user
=
get_user_by_username_or_email
(
username_or_email
)
user
=
get_user_by_username_or_email
(
username_or_email
)
...
...
common/djangoapps/course_groups/tests/test_cohorts.py
View file @
8a7fef07
import
django.test
import
django.test
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.conf
import
settings
from
django.conf
import
settings
from
django.http
import
Http404
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
student.models
import
CourseEnrollment
from
course_groups.models
import
CourseUserGroup
from
course_groups.models
import
CourseUserGroup
from
course_groups.cohorts
import
(
get_cohort
,
get_course_cohorts
,
from
course_groups
import
cohorts
is_commentable_cohorted
,
get_cohort_by_name
)
from
xmodule.modulestore.django
import
modulestore
,
clear_existing_modulestores
from
xmodule.modulestore.django
import
modulestore
,
clear_existing_modulestores
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
...
@@ -87,9 +88,49 @@ class TestCohorts(django.test.TestCase):
...
@@ -87,9 +88,49 @@ class TestCohorts(django.test.TestCase):
clear_existing_modulestores
()
clear_existing_modulestores
()
self
.
toy_course_key
=
SlashSeparatedCourseKey
(
"edX"
,
"toy"
,
"2012_Fall"
)
self
.
toy_course_key
=
SlashSeparatedCourseKey
(
"edX"
,
"toy"
,
"2012_Fall"
)
def
test_is_course_cohorted
(
self
):
"""
Make sure cohorts.is_course_cohorted() correctly reports if a course is cohorted or not.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertFalse
(
course
.
is_cohorted
)
self
.
assertFalse
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
config_course_cohorts
(
course
,
[],
cohorted
=
True
)
self
.
assertTrue
(
course
.
is_cohorted
)
self
.
assertTrue
(
cohorts
.
is_course_cohorted
(
course
.
id
))
# Make sure we get a Http404 if there's no course
fake_key
=
SlashSeparatedCourseKey
(
'a'
,
'b'
,
'c'
)
self
.
assertRaises
(
Http404
,
lambda
:
cohorts
.
is_course_cohorted
(
fake_key
))
def
test_get_cohort_id
(
self
):
"""
Make sure that cohorts.get_cohort_id() correctly returns the cohort id, or raises a ValueError when given an
invalid course key.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertFalse
(
course
.
is_cohorted
)
user
=
User
.
objects
.
create
(
username
=
"test"
,
email
=
"a@b.com"
)
self
.
assertIsNone
(
cohorts
.
get_cohort_id
(
user
,
course
.
id
))
self
.
config_course_cohorts
(
course
,
[],
cohorted
=
True
)
cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"TestCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
cohort
.
users
.
add
(
user
)
self
.
assertEqual
(
cohorts
.
get_cohort_id
(
user
,
course
.
id
),
cohort
.
id
)
self
.
assertRaises
(
ValueError
,
lambda
:
cohorts
.
get_cohort_id
(
user
,
SlashSeparatedCourseKey
(
"course"
,
"does_not"
,
"exist"
))
)
def
test_get_cohort
(
self
):
def
test_get_cohort
(
self
):
"""
"""
Make sure get_cohort() does the right thing when the course is cohorted
Make sure
cohorts.
get_cohort() does the right thing when the course is cohorted
"""
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertEqual
(
course
.
id
,
self
.
toy_course_key
)
self
.
assertEqual
(
course
.
id
,
self
.
toy_course_key
)
...
@@ -98,7 +139,7 @@ class TestCohorts(django.test.TestCase):
...
@@ -98,7 +139,7 @@ class TestCohorts(django.test.TestCase):
user
=
User
.
objects
.
create
(
username
=
"test"
,
email
=
"a@b.com"
)
user
=
User
.
objects
.
create
(
username
=
"test"
,
email
=
"a@b.com"
)
other_user
=
User
.
objects
.
create
(
username
=
"test2"
,
email
=
"a2@b.com"
)
other_user
=
User
.
objects
.
create
(
username
=
"test2"
,
email
=
"a2@b.com"
)
self
.
assertIsNone
(
get_cohort
(
user
,
course
.
id
),
"No cohort created yet"
)
self
.
assertIsNone
(
cohorts
.
get_cohort
(
user
,
course
.
id
),
"No cohort created yet"
)
cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"TestCohort"
,
cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"TestCohort"
,
course_id
=
course
.
id
,
course_id
=
course
.
id
,
...
@@ -106,21 +147,21 @@ class TestCohorts(django.test.TestCase):
...
@@ -106,21 +147,21 @@ class TestCohorts(django.test.TestCase):
cohort
.
users
.
add
(
user
)
cohort
.
users
.
add
(
user
)
self
.
assertIsNone
(
get_cohort
(
user
,
course
.
id
),
self
.
assertIsNone
(
cohorts
.
get_cohort
(
user
,
course
.
id
),
"Course isn't cohorted, so shouldn't have a cohort"
)
"Course isn't cohorted, so shouldn't have a cohort"
)
# Make the course cohorted...
# Make the course cohorted...
self
.
config_course_cohorts
(
course
,
[],
cohorted
=
True
)
self
.
config_course_cohorts
(
course
,
[],
cohorted
=
True
)
self
.
assertEquals
(
get_cohort
(
user
,
course
.
id
)
.
id
,
cohort
.
id
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user
,
course
.
id
)
.
id
,
cohort
.
id
,
"Should find the right cohort"
)
"Should find the right cohort"
)
self
.
assertEquals
(
get_cohort
(
other_user
,
course
.
id
),
None
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
other_user
,
course
.
id
),
None
,
"other_user shouldn't have a cohort"
)
"other_user shouldn't have a cohort"
)
def
test_auto_cohorting
(
self
):
def
test_auto_cohorting
(
self
):
"""
"""
Make sure get_cohort() does the right thing when the course is auto_cohorted
Make sure
cohorts.
get_cohort() does the right thing when the course is auto_cohorted
"""
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertFalse
(
course
.
is_cohorted
)
self
.
assertFalse
(
course
.
is_cohorted
)
...
@@ -141,10 +182,10 @@ class TestCohorts(django.test.TestCase):
...
@@ -141,10 +182,10 @@ class TestCohorts(django.test.TestCase):
auto_cohort
=
True
,
auto_cohort
=
True
,
auto_cohort_groups
=
[
"AutoGroup"
])
auto_cohort_groups
=
[
"AutoGroup"
])
self
.
assertEquals
(
get_cohort
(
user1
,
course
.
id
)
.
id
,
cohort
.
id
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user1
,
course
.
id
)
.
id
,
cohort
.
id
,
"user1 should stay put"
)
"user1 should stay put"
)
self
.
assertEquals
(
get_cohort
(
user2
,
course
.
id
)
.
name
,
"AutoGroup"
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user2
,
course
.
id
)
.
name
,
"AutoGroup"
,
"user2 should be auto-cohorted"
)
"user2 should be auto-cohorted"
)
# Now make the group list empty
# Now make the group list empty
...
@@ -152,7 +193,7 @@ class TestCohorts(django.test.TestCase):
...
@@ -152,7 +193,7 @@ class TestCohorts(django.test.TestCase):
auto_cohort
=
True
,
auto_cohort
=
True
,
auto_cohort_groups
=
[])
auto_cohort_groups
=
[])
self
.
assertEquals
(
get_cohort
(
user3
,
course
.
id
),
None
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user3
,
course
.
id
),
None
,
"No groups->no auto-cohorting"
)
"No groups->no auto-cohorting"
)
# Now make it different
# Now make it different
...
@@ -160,14 +201,14 @@ class TestCohorts(django.test.TestCase):
...
@@ -160,14 +201,14 @@ class TestCohorts(django.test.TestCase):
auto_cohort
=
True
,
auto_cohort
=
True
,
auto_cohort_groups
=
[
"OtherGroup"
])
auto_cohort_groups
=
[
"OtherGroup"
])
self
.
assertEquals
(
get_cohort
(
user3
,
course
.
id
)
.
name
,
"OtherGroup"
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user3
,
course
.
id
)
.
name
,
"OtherGroup"
,
"New list->new group"
)
"New list->new group"
)
self
.
assertEquals
(
get_cohort
(
user2
,
course
.
id
)
.
name
,
"AutoGroup"
,
self
.
assertEquals
(
cohorts
.
get_cohort
(
user2
,
course
.
id
)
.
name
,
"AutoGroup"
,
"user2 should still be in originally placed cohort"
)
"user2 should still be in originally placed cohort"
)
def
test_auto_cohorting_randomization
(
self
):
def
test_auto_cohorting_randomization
(
self
):
"""
"""
Make sure get_cohort() randomizes properly.
Make sure
cohorts.
get_cohort() randomizes properly.
"""
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertFalse
(
course
.
is_cohorted
)
self
.
assertFalse
(
course
.
is_cohorted
)
...
@@ -181,14 +222,14 @@ class TestCohorts(django.test.TestCase):
...
@@ -181,14 +222,14 @@ class TestCohorts(django.test.TestCase):
for
i
in
range
(
100
):
for
i
in
range
(
100
):
user
=
User
.
objects
.
create
(
username
=
"test_{0}"
.
format
(
i
),
user
=
User
.
objects
.
create
(
username
=
"test_{0}"
.
format
(
i
),
email
=
"a@b{0}.com"
.
format
(
i
))
email
=
"a@b{0}.com"
.
format
(
i
))
get_cohort
(
user
,
course
.
id
)
cohorts
.
get_cohort
(
user
,
course
.
id
)
# Now make sure that the assignment was at least vaguely random:
# Now make sure that the assignment was at least vaguely random:
# each cohort should have at least 1, and fewer than 50 students.
# each cohort should have at least 1, and fewer than 50 students.
# (with 5 groups, probability of 0 users in any group is about
# (with 5 groups, probability of 0 users in any group is about
# .8**100= 2.0e-10)
# .8**100= 2.0e-10)
for
cohort_name
in
groups
:
for
cohort_name
in
groups
:
cohort
=
get_cohort_by_name
(
course
.
id
,
cohort_name
)
cohort
=
cohorts
.
get_cohort_by_name
(
course
.
id
,
cohort_name
)
num_users
=
cohort
.
users
.
count
()
num_users
=
cohort
.
users
.
count
()
self
.
assertGreater
(
num_users
,
1
)
self
.
assertGreater
(
num_users
,
1
)
self
.
assertLess
(
num_users
,
50
)
self
.
assertLess
(
num_users
,
50
)
...
@@ -207,10 +248,10 @@ class TestCohorts(django.test.TestCase):
...
@@ -207,10 +248,10 @@ class TestCohorts(django.test.TestCase):
group_type
=
CourseUserGroup
.
COHORT
)
group_type
=
CourseUserGroup
.
COHORT
)
# second course should have no cohorts
# second course should have no cohorts
self
.
assertEqual
(
get_course_cohorts
(
course2_key
),
[])
self
.
assertEqual
(
cohorts
.
get_course_cohorts
(
course2_key
),
[])
co
horts
=
sorted
([
c
.
name
for
c
in
get_course_cohorts
(
course1_key
)])
co
urse1_cohorts
=
sorted
([
c
.
name
for
c
in
cohorts
.
get_course_cohorts
(
course1_key
)])
self
.
assertEqual
(
cohorts
,
[
'TestCohort'
,
'TestCohort2'
])
self
.
assertEqual
(
co
urse1_co
horts
,
[
'TestCohort'
,
'TestCohort2'
])
def
test_is_commentable_cohorted
(
self
):
def
test_is_commentable_cohorted
(
self
):
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
...
@@ -220,14 +261,14 @@ class TestCohorts(django.test.TestCase):
...
@@ -220,14 +261,14 @@ class TestCohorts(django.test.TestCase):
return
self
.
topic_name_to_id
(
course
,
name
)
return
self
.
topic_name_to_id
(
course
,
name
)
# no topics
# no topics
self
.
assertFalse
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
self
.
assertFalse
(
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
"Course doesn't even have a 'General' topic"
)
"Course doesn't even have a 'General' topic"
)
# not cohorted
# not cohorted
self
.
config_course_cohorts
(
course
,
[
"General"
,
"Feedback"
],
self
.
config_course_cohorts
(
course
,
[
"General"
,
"Feedback"
],
cohorted
=
False
)
cohorted
=
False
)
self
.
assertFalse
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
self
.
assertFalse
(
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
"Course isn't cohorted"
)
"Course isn't cohorted"
)
# cohorted, but top level topics aren't
# cohorted, but top level topics aren't
...
@@ -235,11 +276,11 @@ class TestCohorts(django.test.TestCase):
...
@@ -235,11 +276,11 @@ class TestCohorts(django.test.TestCase):
cohorted
=
True
)
cohorted
=
True
)
self
.
assertTrue
(
course
.
is_cohorted
)
self
.
assertTrue
(
course
.
is_cohorted
)
self
.
assertFalse
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
self
.
assertFalse
(
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
"Course is cohorted, but 'General' isn't."
)
"Course is cohorted, but 'General' isn't."
)
self
.
assertTrue
(
self
.
assertTrue
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"random"
)),
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"random"
)),
"Non-top-level discussion is always cohorted in cohorted courses."
)
"Non-top-level discussion is always cohorted in cohorted courses."
)
# cohorted, including "Feedback" top-level topics aren't
# cohorted, including "Feedback" top-level topics aren't
...
@@ -248,9 +289,236 @@ class TestCohorts(django.test.TestCase):
...
@@ -248,9 +289,236 @@ class TestCohorts(django.test.TestCase):
cohorted_discussions
=
[
"Feedback"
])
cohorted_discussions
=
[
"Feedback"
])
self
.
assertTrue
(
course
.
is_cohorted
)
self
.
assertTrue
(
course
.
is_cohorted
)
self
.
assertFalse
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
self
.
assertFalse
(
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"General"
)),
"Course is cohorted, but 'General' isn't."
)
"Course is cohorted, but 'General' isn't."
)
self
.
assertTrue
(
self
.
assertTrue
(
is_commentable_cohorted
(
course
.
id
,
to_id
(
"Feedback"
)),
cohorts
.
is_commentable_cohorted
(
course
.
id
,
to_id
(
"Feedback"
)),
"Feedback was listed as cohorted. Should be."
)
"Feedback was listed as cohorted. Should be."
)
def
test_get_cohorted_commentables
(
self
):
"""
Make sure cohorts.get_cohorted_commentables() correctly returns a list of strings representing cohorted
commentables. Also verify that we can't get the cohorted commentables from a course which does not exist.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertEqual
(
cohorts
.
get_cohorted_commentables
(
course
.
id
),
set
())
self
.
config_course_cohorts
(
course
,
[],
cohorted
=
True
)
self
.
assertEqual
(
cohorts
.
get_cohorted_commentables
(
course
.
id
),
set
())
self
.
config_course_cohorts
(
course
,
[
"General"
,
"Feedback"
],
cohorted
=
True
,
cohorted_discussions
=
[
"Feedback"
]
)
self
.
assertItemsEqual
(
cohorts
.
get_cohorted_commentables
(
course
.
id
),
set
([
self
.
topic_name_to_id
(
course
,
"Feedback"
)])
)
self
.
config_course_cohorts
(
course
,
[
"General"
,
"Feedback"
],
cohorted
=
True
,
cohorted_discussions
=
[
"General"
,
"Feedback"
]
)
self
.
assertItemsEqual
(
cohorts
.
get_cohorted_commentables
(
course
.
id
),
set
([
self
.
topic_name_to_id
(
course
,
"General"
),
self
.
topic_name_to_id
(
course
,
"Feedback"
)])
)
self
.
assertRaises
(
Http404
,
lambda
:
cohorts
.
get_cohorted_commentables
(
SlashSeparatedCourseKey
(
"course"
,
"does_not"
,
"exist"
))
)
def
test_get_cohort_by_name
(
self
):
"""
Make sure cohorts.get_cohort_by_name() properly finds a cohort by name for a given course. Also verify that it
raises an error when the cohort is not found.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
cohorts
.
get_cohort_by_name
(
course
.
id
,
"CohortDoesNotExist"
)
)
cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"MyCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
self
.
assertEqual
(
cohorts
.
get_cohort_by_name
(
course
.
id
,
"MyCohort"
),
cohort
)
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
cohorts
.
get_cohort_by_name
(
SlashSeparatedCourseKey
(
"course"
,
"does_not"
,
"exist"
),
cohort
)
)
def
test_get_cohort_by_id
(
self
):
"""
Make sure cohorts.get_cohort_by_id() properly finds a cohort by id for a given
course.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"MyCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
self
.
assertEqual
(
cohorts
.
get_cohort_by_id
(
course
.
id
,
cohort
.
id
),
cohort
)
cohort
.
delete
()
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
cohorts
.
get_cohort_by_id
(
course
.
id
,
cohort
.
id
)
)
def
test_add_cohort
(
self
):
"""
Make sure cohorts.add_cohort() properly adds a cohort to a course and handles
errors.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
added_cohort
=
cohorts
.
add_cohort
(
course
.
id
,
"My Cohort"
)
self
.
assertEqual
(
added_cohort
.
name
,
"My Cohort"
)
self
.
assertRaises
(
ValueError
,
lambda
:
cohorts
.
add_cohort
(
course
.
id
,
"My Cohort"
)
)
self
.
assertRaises
(
ValueError
,
lambda
:
cohorts
.
add_cohort
(
SlashSeparatedCourseKey
(
"course"
,
"does_not"
,
"exist"
),
"My Cohort"
)
)
def
test_add_user_to_cohort
(
self
):
"""
Make sure cohorts.add_user_to_cohort() properly adds a user to a cohort and
handles errors.
"""
course_user
=
User
.
objects
.
create
(
username
=
"Username"
,
email
=
"a@b.com"
)
User
.
objects
.
create
(
username
=
"RandomUsername"
,
email
=
"b@b.com"
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
CourseEnrollment
.
enroll
(
course_user
,
self
.
toy_course_key
)
first_cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"FirstCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
second_cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"SecondCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
# Success cases
# We shouldn't get back a previous cohort, since the user wasn't in one
self
.
assertEqual
(
cohorts
.
add_user_to_cohort
(
first_cohort
,
"Username"
),
(
course_user
,
None
)
)
# Should get (user, previous_cohort_name) when moved from one cohort to
# another
self
.
assertEqual
(
cohorts
.
add_user_to_cohort
(
second_cohort
,
"Username"
),
(
course_user
,
"FirstCohort"
)
)
# Error cases
# Should get ValueError if user already in cohort
self
.
assertRaises
(
ValueError
,
lambda
:
cohorts
.
add_user_to_cohort
(
second_cohort
,
"Username"
)
)
# UserDoesNotExist if user truly does not exist
self
.
assertRaises
(
User
.
DoesNotExist
,
lambda
:
cohorts
.
add_user_to_cohort
(
first_cohort
,
"non_existent_username"
)
)
def
test_get_course_cohort_names
(
self
):
"""
Make sure cohorts.get_course_cohort_names() properly returns a list of cohort
names for a given course.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertItemsEqual
(
cohorts
.
get_course_cohort_names
(
course
.
id
),
[]
)
self
.
assertItemsEqual
(
cohorts
.
get_course_cohort_names
(
SlashSeparatedCourseKey
(
'course'
,
'does_not'
,
'exist'
)),
[]
)
CourseUserGroup
.
objects
.
create
(
name
=
"FirstCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
self
.
assertItemsEqual
(
cohorts
.
get_course_cohort_names
(
course
.
id
),
[
"FirstCohort"
]
)
CourseUserGroup
.
objects
.
create
(
name
=
"SecondCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
self
.
assertItemsEqual
(
cohorts
.
get_course_cohort_names
(
course
.
id
),
[
"FirstCohort"
,
"SecondCohort"
]
)
def
test_delete_empty_cohort
(
self
):
"""
Make sure that cohorts.delete_empty_cohort() properly removes an empty cohort
for a given course.
"""
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
user
=
User
.
objects
.
create
(
username
=
"Username"
,
email
=
"a@b.com"
)
empty_cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"EmptyCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
nonempty_cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"NonemptyCohort"
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
nonempty_cohort
.
users
.
add
(
user
)
cohorts
.
delete_empty_cohort
(
course
.
id
,
"EmptyCohort"
)
# Make sure we cannot access the deleted cohort
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
CourseUserGroup
.
objects
.
get
(
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
,
id
=
empty_cohort
.
id
)
)
self
.
assertRaises
(
ValueError
,
lambda
:
cohorts
.
delete_empty_cohort
(
course
.
id
,
"NonemptyCohort"
)
)
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
cohorts
.
delete_empty_cohort
(
SlashSeparatedCourseKey
(
'course'
,
'does_not'
,
'exist'
),
"EmptyCohort"
)
)
self
.
assertRaises
(
CourseUserGroup
.
DoesNotExist
,
lambda
:
cohorts
.
delete_empty_cohort
(
course
.
id
,
"NonExistentCohort"
)
)
common/djangoapps/course_groups/tests/test_views.py
View file @
8a7fef07
...
@@ -5,13 +5,17 @@ from django.test.utils import override_settings
...
@@ -5,13 +5,17 @@ from django.test.utils import override_settings
from
factory
import
post_generation
,
Sequence
from
factory
import
post_generation
,
Sequence
from
factory.django
import
DjangoModelFactory
from
factory.django
import
DjangoModelFactory
from
django.http
import
Http404
from
django.contrib.auth.models
import
User
from
course_groups.models
import
CourseUserGroup
from
course_groups.models
import
CourseUserGroup
from
course_groups.views
import
add_users_to_cohort
from
courseware.tests.tests
import
TEST_DATA_MIXED_MODULESTORE
from
courseware.tests.tests
import
TEST_DATA_MIXED_MODULESTORE
from
student.models
import
CourseEnrollment
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
course_groups.views
import
list_cohorts
,
add_cohort
,
users_in_cohort
,
add_users_to_cohort
,
remove_user_from_cohort
class
CohortFactory
(
DjangoModelFactory
):
class
CohortFactory
(
DjangoModelFactory
):
FACTORY_FOR
=
CourseUserGroup
FACTORY_FOR
=
CourseUserGroup
...
@@ -26,60 +30,381 @@ class CohortFactory(DjangoModelFactory):
...
@@ -26,60 +30,381 @@ class CohortFactory(DjangoModelFactory):
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
class
AddUsersToCohortTestCase
(
ModuleStoreTestCase
):
class
CohortViewsTestCase
(
ModuleStoreTestCase
):
"""
Base class which sets up a course and staff/non-staff users.
"""
def
setUp
(
self
):
def
setUp
(
self
):
self
.
course
=
CourseFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
staff_user
=
UserFactory
.
create
(
is_staff
=
True
)
self
.
staff_user
=
UserFactory
.
create
(
is_staff
=
True
,
username
=
"staff"
)
self
.
non_staff_user
=
UserFactory
.
create
(
username
=
"nonstaff"
)
def
_enroll_users
(
self
,
users
,
course_key
):
"""Enroll each user in the specified course"""
for
user
in
users
:
CourseEnrollment
.
enroll
(
user
,
course_key
)
def
_create_cohorts
(
self
):
"""Creates cohorts for testing"""
self
.
cohort1_users
=
[
UserFactory
.
create
()
for
_
in
range
(
3
)]
self
.
cohort1_users
=
[
UserFactory
.
create
()
for
_
in
range
(
3
)]
self
.
cohort2_users
=
[
UserFactory
.
create
()
for
_
in
range
(
2
)]
self
.
cohort2_users
=
[
UserFactory
.
create
()
for
_
in
range
(
2
)]
self
.
cohort3_users
=
[
UserFactory
.
create
()
for
_
in
range
(
2
)]
self
.
cohort3_users
=
[
UserFactory
.
create
()
for
_
in
range
(
2
)]
self
.
cohortless_users
=
[
UserFactory
.
create
()
for
_
in
range
(
3
)]
self
.
cohortless_users
=
[
UserFactory
.
create
()
for
_
in
range
(
3
)]
self
.
unenrolled_users
=
[
UserFactory
.
create
()
for
_
in
range
(
3
)]
self
.
_enroll_users
(
self
.
cohort1_users
+
self
.
cohort2_users
+
self
.
cohort3_users
+
self
.
cohortless_users
,
self
.
course
.
id
)
self
.
cohort1
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort1_users
)
self
.
cohort1
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort1_users
)
self
.
cohort2
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort2_users
)
self
.
cohort2
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort2_users
)
self
.
cohort3
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort3_users
)
self
.
cohort3
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
self
.
cohort3_users
)
def
check_request
(
def
_cohort_in_course
(
self
,
cohort_name
,
course
):
self
,
users_string
,
expected_added
=
None
,
expected_changed
=
None
,
expected_present
=
None
,
expected_unknown
=
None
):
"""
"""
Check that add_users_to_cohort returns the expected result and has the
Returns true iff `course` contains a cohort with the name
expected side effects. The given users will be added to cohort1.
`cohort_name`.
"""
try
:
CourseUserGroup
.
objects
.
get
(
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
,
name
=
cohort_name
)
except
CourseUserGroup
.
DoesNotExist
:
return
False
else
:
return
True
users_string is the string input entered by the client
def
_user_in_cohort
(
self
,
username
,
cohort
):
"""
Return true iff a user with `username` exists in `cohort`.
"""
return
username
in
[
user
.
username
for
user
in
cohort
.
users
.
all
()]
expected_added is a list of users
def
_verify_non_staff_cannot_access
(
self
,
view
,
request_method
,
view_args
):
"""
Verify that a non-staff user cannot access a given view.
expected_changed is a list of (user, previous_cohort) tuples
`view` is the view to test.
`view_args` is a list of arguments (not including the request) to pass
to the view.
"""
if
request_method
==
"GET"
:
request
=
RequestFactory
()
.
get
(
"dummy_url"
)
elif
request_method
==
"POST"
:
request
=
RequestFactory
()
.
post
(
"dummy_url"
)
else
:
request
=
RequestFactory
()
.
request
()
request
.
user
=
self
.
non_staff_user
view_args
.
insert
(
0
,
request
)
self
.
assertRaises
(
Http404
,
view
,
*
view_args
)
expected_present is a list of (user, email/username) tuples where
email/username corresponds to the input
expected_unknown is a list of strings corresponding to the input
class
ListCohortsTestCase
(
CohortViewsTestCase
):
"""
Tests the `list_cohorts` view.
"""
def
request_list_cohorts
(
self
,
course
):
"""
"""
expected_added
=
expected_added
or
[]
Call `list_cohorts` for a given `course` and return its response as a
expected_changed
=
expected_changed
or
[]
dict.
expected_present
=
expected_present
or
[]
"""
expected_unknown
=
expected_unknown
or
[]
request
=
RequestFactory
()
.
get
(
"dummy_url"
)
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"users"
:
users_string
})
request
.
user
=
self
.
staff_user
request
.
user
=
self
.
staff_user
response
=
add_users_to_cohort
(
request
,
self
.
course
.
id
.
to_deprecated_string
(),
self
.
cohort1
.
id
)
response
=
list_cohorts
(
request
,
course
.
id
.
to_deprecated_string
()
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
result
=
json
.
loads
(
response
.
content
)
return
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
result
.
get
(
"success"
),
True
)
def
verify_lists_expected_cohorts
(
self
,
response_dict
,
expected_cohorts
):
"""
Verify that the server response contains the expected_cohorts.
"""
self
.
assertTrue
(
response_dict
.
get
(
"success"
))
self
.
assertItemsEqual
(
self
.
assertItemsEqual
(
result
.
get
(
"added"
),
response_dict
.
get
(
"cohorts"
),
[
{
"name"
:
cohort
.
name
,
"id"
:
cohort
.
id
}
for
cohort
in
expected_cohorts
]
)
def
test_non_staff
(
self
):
"""
Verify that we cannot access list_cohorts if we're a non-staff user.
"""
self
.
_verify_non_staff_cannot_access
(
list_cohorts
,
"GET"
,
[
self
.
course
.
id
.
to_deprecated_string
()])
def
test_no_cohorts
(
self
):
"""
Verify that no cohorts are in response for a course with no cohorts.
"""
self
.
verify_lists_expected_cohorts
(
self
.
request_list_cohorts
(
self
.
course
),
[])
def
test_some_cohorts
(
self
):
"""
Verify that cohorts are in response for a course with some cohorts.
"""
self
.
_create_cohorts
()
expected_cohorts
=
CourseUserGroup
.
objects
.
filter
(
course_id
=
self
.
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
self
.
verify_lists_expected_cohorts
(
self
.
request_list_cohorts
(
self
.
course
),
expected_cohorts
)
class
AddCohortTestCase
(
CohortViewsTestCase
):
"""
Tests the `add_cohort` view.
"""
def
request_add_cohort
(
self
,
cohort_name
,
course
):
"""
Call `add_cohort` and return its response as a dict.
"""
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"name"
:
cohort_name
})
request
.
user
=
self
.
staff_user
response
=
add_cohort
(
request
,
course
.
id
.
to_deprecated_string
())
self
.
assertEqual
(
response
.
status_code
,
200
)
return
json
.
loads
(
response
.
content
)
def
verify_contains_added_cohort
(
self
,
response_dict
,
cohort_name
,
course
,
expected_error_msg
=
None
):
"""
Check that `add_cohort`'s response correctly returns the newly added
cohort (or error) in the response. Also verify that the cohort was
actually created/exists.
"""
if
expected_error_msg
is
not
None
:
self
.
assertFalse
(
response_dict
.
get
(
"success"
))
self
.
assertEqual
(
response_dict
.
get
(
"msg"
),
expected_error_msg
)
else
:
self
.
assertTrue
(
response_dict
.
get
(
"success"
))
self
.
assertEqual
(
response_dict
.
get
(
"cohort"
)
.
get
(
"name"
),
cohort_name
)
self
.
assertTrue
(
self
.
_cohort_in_course
(
cohort_name
,
course
))
def
test_non_staff
(
self
):
"""
Verify that non-staff users cannot access add_cohort.
"""
self
.
_verify_non_staff_cannot_access
(
add_cohort
,
"POST"
,
[
self
.
course
.
id
.
to_deprecated_string
()])
def
test_new_cohort
(
self
):
"""
Verify that we can add a new cohort.
"""
cohort_name
=
"New Cohort"
self
.
verify_contains_added_cohort
(
self
.
request_add_cohort
(
cohort_name
,
self
.
course
),
cohort_name
,
self
.
course
)
def
test_no_cohort
(
self
):
"""
Verify that we cannot explicitly add no cohort.
"""
response_dict
=
self
.
request_add_cohort
(
""
,
self
.
course
)
self
.
assertFalse
(
response_dict
.
get
(
"success"
))
self
.
assertEqual
(
response_dict
.
get
(
"msg"
),
"No name specified"
)
def
test_existing_cohort
(
self
):
"""
Verify that we cannot add a cohort with the same name as an existing
cohort.
"""
self
.
_create_cohorts
()
cohort_name
=
self
.
cohort1
.
name
self
.
verify_contains_added_cohort
(
self
.
request_add_cohort
(
cohort_name
,
self
.
course
),
cohort_name
,
self
.
course
,
expected_error_msg
=
"Can't create two cohorts with the same name"
)
class
UsersInCohortTestCase
(
CohortViewsTestCase
):
"""
Tests the `users_in_cohort` view.
"""
def
request_users_in_cohort
(
self
,
cohort
,
course
,
requested_page
,
should_return_bad_request
=
False
):
"""
Call `users_in_cohort` for a given cohort/requested page, and return
its response as a dict. When `should_return_bad_request` is True,
verify that the response indicates a bad request.
"""
request
=
RequestFactory
()
.
get
(
"dummy_url"
,
{
"page"
:
requested_page
})
request
.
user
=
self
.
staff_user
response
=
users_in_cohort
(
request
,
course
.
id
.
to_deprecated_string
(),
cohort
.
id
)
if
should_return_bad_request
:
self
.
assertEqual
(
response
.
status_code
,
400
)
return
self
.
assertEqual
(
response
.
status_code
,
200
)
return
json
.
loads
(
response
.
content
)
def
verify_users_in_cohort_and_response
(
self
,
cohort
,
response_dict
,
expected_users
,
expected_page
,
expected_num_pages
):
"""
Check that the `users_in_cohort` response contains the expected list of
users, page number, and total number of pages for a given cohort. Also
verify that those users are actually in the given cohort.
"""
self
.
assertTrue
(
response_dict
.
get
(
"success"
))
self
.
assertEqual
(
response_dict
.
get
(
"page"
),
expected_page
)
self
.
assertEqual
(
response_dict
.
get
(
"num_pages"
),
expected_num_pages
)
returned_users
=
User
.
objects
.
filter
(
username__in
=
[
user
.
get
(
"username"
)
for
user
in
response_dict
.
get
(
"users"
)])
self
.
assertItemsEqual
(
returned_users
,
expected_users
)
self
.
assertTrue
(
set
(
returned_users
)
.
issubset
(
cohort
.
users
.
all
()))
def
test_non_staff
(
self
):
"""
Verify that non-staff users cannot access `check_users_in_cohort`.
"""
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
self
.
_verify_non_staff_cannot_access
(
users_in_cohort
,
"GET"
,
[
self
.
course
.
id
.
to_deprecated_string
(),
cohort
.
id
])
def
test_no_users
(
self
):
"""
Verify that we don't get back any users for a cohort with no users.
"""
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
response_dict
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
1
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response_dict
,
expected_users
=
[],
expected_page
=
1
,
expected_num_pages
=
1
)
def
test_few_users
(
self
):
"""
Verify that we get back all users for a cohort when the cohort has
<=100 users.
"""
users
=
[
UserFactory
.
create
()
for
_
in
range
(
5
)]
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
users
)
response_dict
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
1
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response_dict
,
expected_users
=
users
,
expected_page
=
1
,
expected_num_pages
=
1
)
def
test_many_users
(
self
):
"""
Verify that pagination works correctly for cohorts with >100 users.
"""
users
=
[
UserFactory
.
create
()
for
_
in
range
(
101
)]
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
users
)
response_dict_1
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
1
)
response_dict_2
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
2
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response_dict_1
,
expected_users
=
users
[:
100
],
expected_page
=
1
,
expected_num_pages
=
2
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response_dict_2
,
expected_users
=
users
[
100
:],
expected_page
=
2
,
expected_num_pages
=
2
)
def
test_out_of_range
(
self
):
"""
Verify that we get a blank page of users when requesting page 0 or a
page greater than the actual number of pages.
"""
users
=
[
UserFactory
.
create
()
for
_
in
range
(
5
)]
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
users
)
response
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
0
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response
,
expected_users
=
[],
expected_page
=
0
,
expected_num_pages
=
1
)
response
=
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
2
)
self
.
verify_users_in_cohort_and_response
(
cohort
,
response
,
expected_users
=
[],
expected_page
=
2
,
expected_num_pages
=
1
)
def
test_non_positive_page
(
self
):
"""
Verify that we get a `HttpResponseBadRequest` (bad request) when the
page we request isn't a positive integer.
"""
users
=
[
UserFactory
.
create
()
for
_
in
range
(
5
)]
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
users
)
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
"invalid"
,
should_return_bad_request
=
True
)
self
.
request_users_in_cohort
(
cohort
,
self
.
course
,
-
1
,
should_return_bad_request
=
True
)
class
AddUsersToCohortTestCase
(
CohortViewsTestCase
):
"""
Tests the `add_users_to_cohort` view.
"""
def
setUp
(
self
):
super
(
AddUsersToCohortTestCase
,
self
)
.
setUp
()
self
.
_create_cohorts
()
def
request_add_users_to_cohort
(
self
,
users_string
,
cohort
,
course
,
should_raise_404
=
False
):
"""
Call `add_users_to_cohort` for a given cohort, course, and list of
users, returning its response as a dict. When `should_raise_404` is
True, verify that the request raised a Http404.
"""
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"users"
:
users_string
})
request
.
user
=
self
.
staff_user
if
should_raise_404
:
self
.
assertRaises
(
Http404
,
lambda
:
add_users_to_cohort
(
request
,
course
.
id
.
to_deprecated_string
(),
cohort
.
id
)
)
else
:
response
=
add_users_to_cohort
(
request
,
course
.
id
.
to_deprecated_string
(),
cohort
.
id
)
self
.
assertEqual
(
response
.
status_code
,
200
)
return
json
.
loads
(
response
.
content
)
def
verify_added_users_to_cohort
(
self
,
response_dict
,
cohort
,
course
,
expected_added
,
expected_changed
,
expected_present
,
expected_unknown
):
"""
Check that add_users_to_cohort returned the expected response and has
the expected side effects.
`expected_added` is a list of users
`expected_changed` is a list of (user, previous_cohort) tuples
`expected_present` is a list of (user, email/username) tuples where
email/username corresponds to the input
`expected_unknown` is a list of strings corresponding to the input
"""
self
.
assertTrue
(
response_dict
.
get
(
"success"
))
self
.
assertItemsEqual
(
response_dict
.
get
(
"added"
),
[
[
{
"username"
:
user
.
username
,
"name"
:
user
.
profile
.
name
,
"email"
:
user
.
email
}
{
"username"
:
user
.
username
,
"name"
:
user
.
profile
.
name
,
"email"
:
user
.
email
}
for
user
in
expected_added
for
user
in
expected_added
]
]
)
)
self
.
assertItemsEqual
(
self
.
assertItemsEqual
(
res
ul
t
.
get
(
"changed"
),
res
ponse_dic
t
.
get
(
"changed"
),
[
[
{
{
"username"
:
user
.
username
,
"username"
:
user
.
username
,
...
@@ -91,55 +416,133 @@ class AddUsersToCohortTestCase(ModuleStoreTestCase):
...
@@ -91,55 +416,133 @@ class AddUsersToCohortTestCase(ModuleStoreTestCase):
]
]
)
)
self
.
assertItemsEqual
(
self
.
assertItemsEqual
(
res
ul
t
.
get
(
"present"
),
res
ponse_dic
t
.
get
(
"present"
),
[
username_or_email
for
(
_
,
username_or_email
)
in
expected_present
]
[
username_or_email
for
(
_
,
username_or_email
)
in
expected_present
]
)
)
self
.
assertItemsEqual
(
res
ul
t
.
get
(
"unknown"
),
expected_unknown
)
self
.
assertItemsEqual
(
res
ponse_dic
t
.
get
(
"unknown"
),
expected_unknown
)
for
user
in
expected_added
+
[
user
for
(
user
,
_
)
in
expected_changed
+
expected_present
]:
for
user
in
expected_added
+
[
user
for
(
user
,
_
)
in
expected_changed
+
expected_present
]:
self
.
assertEqual
(
self
.
assertEqual
(
CourseUserGroup
.
objects
.
get
(
CourseUserGroup
.
objects
.
get
(
course_id
=
self
.
course
.
id
,
course_id
=
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
,
group_type
=
CourseUserGroup
.
COHORT
,
users__id
=
user
.
id
users__id
=
user
.
id
),
),
self
.
cohort1
cohort
)
)
def
test_non_staff
(
self
):
"""
Verify that non-staff users cannot access `check_users_in_cohort`.
"""
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
self
.
_verify_non_staff_cannot_access
(
add_users_to_cohort
,
"POST"
,
[
self
.
course
.
id
.
to_deprecated_string
(),
cohort
.
id
]
)
def
test_empty
(
self
):
def
test_empty
(
self
):
self
.
check_request
(
""
)
"""
Verify that adding an empty list of users to a cohort has no result.
"""
response_dict
=
self
.
request_add_users_to_cohort
(
""
,
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[],
expected_changed
=
[],
expected_present
=
[],
expected_unknown
=
[]
)
def
test_only_added
(
self
):
def
test_only_added
(
self
):
self
.
check_request
(
"""
Verify that we can add users to their first cohort.
"""
response_dict
=
self
.
request_add_users_to_cohort
(
","
.
join
([
user
.
username
for
user
in
self
.
cohortless_users
]),
","
.
join
([
user
.
username
for
user
in
self
.
cohortless_users
]),
expected_added
=
self
.
cohortless_users
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
self
.
cohortless_users
,
expected_changed
=
[],
expected_present
=
[],
expected_unknown
=
[]
)
)
def
test_only_changed
(
self
):
def
test_only_changed
(
self
):
self
.
check_request
(
"""
Verify that we can move users to a different cohort.
"""
response_dict
=
self
.
request_add_users_to_cohort
(
","
.
join
([
user
.
username
for
user
in
self
.
cohort2_users
+
self
.
cohort3_users
]),
","
.
join
([
user
.
username
for
user
in
self
.
cohort2_users
+
self
.
cohort3_users
]),
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[],
expected_changed
=
(
expected_changed
=
(
[(
user
,
self
.
cohort2
.
name
)
for
user
in
self
.
cohort2_users
]
+
[(
user
,
self
.
cohort2
.
name
)
for
user
in
self
.
cohort2_users
]
+
[(
user
,
self
.
cohort3
.
name
)
for
user
in
self
.
cohort3_users
]
[(
user
,
self
.
cohort3
.
name
)
for
user
in
self
.
cohort3_users
]
)
),
expected_present
=
[],
expected_unknown
=
[]
)
)
def
test_only_present
(
self
):
def
test_only_present
(
self
):
"""
Verify that we can 'add' users to their current cohort.
"""
usernames
=
[
user
.
username
for
user
in
self
.
cohort1_users
]
usernames
=
[
user
.
username
for
user
in
self
.
cohort1_users
]
self
.
check_reques
t
(
response_dict
=
self
.
request_add_users_to_cohor
t
(
","
.
join
(
usernames
),
","
.
join
(
usernames
),
expected_present
=
[(
user
,
user
.
username
)
for
user
in
self
.
cohort1_users
]
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[],
expected_changed
=
[],
expected_present
=
[(
user
,
user
.
username
)
for
user
in
self
.
cohort1_users
],
expected_unknown
=
[]
)
)
def
test_only_unknown
(
self
):
def
test_only_unknown
(
self
):
"""
Verify that non-existent users are not added.
"""
usernames
=
[
"unknown_user{}"
.
format
(
i
)
for
i
in
range
(
3
)]
usernames
=
[
"unknown_user{}"
.
format
(
i
)
for
i
in
range
(
3
)]
self
.
check_reques
t
(
response_dict
=
self
.
request_add_users_to_cohor
t
(
","
.
join
(
usernames
),
","
.
join
(
usernames
),
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[],
expected_changed
=
[],
expected_present
=
[],
expected_unknown
=
usernames
expected_unknown
=
usernames
)
)
def
test_all
(
self
):
def
test_all
(
self
):
"""
Test all adding conditions together.
"""
unknowns
=
[
"unknown_user{}"
.
format
(
i
)
for
i
in
range
(
3
)]
unknowns
=
[
"unknown_user{}"
.
format
(
i
)
for
i
in
range
(
3
)]
self
.
check_reques
t
(
response_dict
=
self
.
request_add_users_to_cohor
t
(
","
.
join
(
","
.
join
(
unknowns
+
unknowns
+
[
[
...
@@ -147,41 +550,200 @@ class AddUsersToCohortTestCase(ModuleStoreTestCase):
...
@@ -147,41 +550,200 @@ class AddUsersToCohortTestCase(ModuleStoreTestCase):
for
user
in
self
.
cohortless_users
+
self
.
cohort1_users
+
self
.
cohort2_users
+
self
.
cohort3_users
for
user
in
self
.
cohortless_users
+
self
.
cohort1_users
+
self
.
cohort2_users
+
self
.
cohort3_users
]
]
),
),
self
.
cohortless_users
,
self
.
cohort1
,
(
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
self
.
cohortless_users
,
expected_changed
=
(
[(
user
,
self
.
cohort2
.
name
)
for
user
in
self
.
cohort2_users
]
+
[(
user
,
self
.
cohort2
.
name
)
for
user
in
self
.
cohort2_users
]
+
[(
user
,
self
.
cohort3
.
name
)
for
user
in
self
.
cohort3_users
]
[(
user
,
self
.
cohort3
.
name
)
for
user
in
self
.
cohort3_users
]
),
),
[(
user
,
user
.
username
)
for
user
in
self
.
cohort1_users
],
expected_present
=
[(
user
,
user
.
username
)
for
user
in
self
.
cohort1_users
],
unknowns
expected_unknown
=
unknowns
)
)
def
test_emails
(
self
):
def
test_emails
(
self
):
"""
Verify that we can use emails to identify users.
"""
unknown
=
"unknown_user@example.com"
unknown
=
"unknown_user@example.com"
self
.
check_reques
t
(
response_dict
=
self
.
request_add_users_to_cohor
t
(
","
.
join
([
","
.
join
([
self
.
cohort1_users
[
0
]
.
email
,
self
.
cohort1_users
[
0
]
.
email
,
self
.
cohort2_users
[
0
]
.
email
,
self
.
cohort2_users
[
0
]
.
email
,
self
.
cohortless_users
[
0
]
.
email
,
self
.
cohortless_users
[
0
]
.
email
,
unknown
unknown
]),
]),
[
self
.
cohortless_users
[
0
]],
self
.
cohort1
,
[(
self
.
cohort2_users
[
0
],
self
.
cohort2
.
name
)],
self
.
course
[(
self
.
cohort1_users
[
0
],
self
.
cohort1_users
[
0
]
.
email
)],
)
[
unknown
]
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[
self
.
cohortless_users
[
0
]],
expected_changed
=
[(
self
.
cohort2_users
[
0
],
self
.
cohort2
.
name
)],
expected_present
=
[(
self
.
cohort1_users
[
0
],
self
.
cohort1_users
[
0
]
.
email
)],
expected_unknown
=
[
unknown
]
)
)
def
test_delimiters
(
self
):
def
test_delimiters
(
self
):
"""
Verify that we can use different types of whitespace to delimit
usernames in the user string.
"""
unknown
=
"unknown_user"
unknown
=
"unknown_user"
self
.
check_reques
t
(
response_dict
=
self
.
request_add_users_to_cohor
t
(
" {} {}
\t
{},
\r\n
{}"
.
format
(
" {} {}
\t
{},
\r\n
{}"
.
format
(
unknown
,
unknown
,
self
.
cohort1_users
[
0
]
.
username
,
self
.
cohort1_users
[
0
]
.
username
,
self
.
cohort2_users
[
0
]
.
username
,
self
.
cohort2_users
[
0
]
.
username
,
self
.
cohortless_users
[
0
]
.
username
self
.
cohortless_users
[
0
]
.
username
),
),
[
self
.
cohortless_users
[
0
]],
self
.
cohort1
,
[(
self
.
cohort2_users
[
0
],
self
.
cohort2
.
name
)],
self
.
course
[(
self
.
cohort1_users
[
0
],
self
.
cohort1_users
[
0
]
.
username
)],
)
[
unknown
]
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
[
self
.
cohortless_users
[
0
]],
expected_changed
=
[(
self
.
cohort2_users
[
0
],
self
.
cohort2
.
name
)],
expected_present
=
[(
self
.
cohort1_users
[
0
],
self
.
cohort1_users
[
0
]
.
username
)],
expected_unknown
=
[
unknown
]
)
def
test_can_cohort_unenrolled_users
(
self
):
"""
Verify that users can be added to a cohort of a course they're not
enrolled in. This feature is currently used to pre-cohort users that
are expected to enroll in a course.
"""
unenrolled_usernames
=
[
user
.
username
for
user
in
self
.
unenrolled_users
]
response_dict
=
self
.
request_add_users_to_cohort
(
","
.
join
(
unenrolled_usernames
),
self
.
cohort1
,
self
.
course
)
self
.
verify_added_users_to_cohort
(
response_dict
,
self
.
cohort1
,
self
.
course
,
expected_added
=
self
.
unenrolled_users
,
expected_changed
=
[],
expected_present
=
[],
expected_unknown
=
[]
)
def
test_non_existent_cohort
(
self
):
"""
Verify that an error is raised when trying to add users to a cohort
which does not belong to the given course.
"""
users
=
[
UserFactory
.
create
(
username
=
"user{0}"
.
format
(
i
))
for
i
in
range
(
3
)]
usernames
=
[
user
.
username
for
user
in
users
]
wrong_course_key
=
SlashSeparatedCourseKey
(
"some"
,
"arbitrary"
,
"course"
)
wrong_course_cohort
=
CohortFactory
.
create
(
name
=
"wrong_cohort"
,
course_id
=
wrong_course_key
,
users
=
[])
self
.
request_add_users_to_cohort
(
","
.
join
(
usernames
),
wrong_course_cohort
,
self
.
course
,
should_raise_404
=
True
)
)
class
RemoveUserFromCohortTestCase
(
CohortViewsTestCase
):
"""
Tests the `remove_user_from_cohort` view.
"""
def
request_remove_user_from_cohort
(
self
,
username
,
cohort
):
"""
Call `remove_user_from_cohort` with the given username and cohort.
"""
if
username
is
not
None
:
request
=
RequestFactory
()
.
post
(
"dummy_url"
,
{
"username"
:
username
})
else
:
request
=
RequestFactory
()
.
post
(
"dummy_url"
)
request
.
user
=
self
.
staff_user
response
=
remove_user_from_cohort
(
request
,
self
.
course
.
id
.
to_deprecated_string
(),
cohort
.
id
)
self
.
assertEqual
(
response
.
status_code
,
200
)
return
json
.
loads
(
response
.
content
)
def
verify_removed_user_from_cohort
(
self
,
username
,
response_dict
,
cohort
,
expected_error_msg
=
None
):
"""
Check that `remove_user_from_cohort` properly removes a user from a
cohort and returns appropriate success. If the removal should fail,
verify that the returned error message matches the expected one.
"""
if
expected_error_msg
is
None
:
self
.
assertTrue
(
response_dict
.
get
(
"success"
))
self
.
assertIsNone
(
response_dict
.
get
(
"msg"
))
self
.
assertFalse
(
self
.
_user_in_cohort
(
username
,
cohort
))
else
:
self
.
assertFalse
(
response_dict
.
get
(
"success"
))
self
.
assertEqual
(
response_dict
.
get
(
"msg"
),
expected_error_msg
)
def
test_non_staff
(
self
):
"""
Verify that non-staff users cannot access `check_users_in_cohort`.
"""
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
self
.
_verify_non_staff_cannot_access
(
remove_user_from_cohort
,
"POST"
,
[
self
.
course
.
id
.
to_deprecated_string
(),
cohort
.
id
]
)
def
test_no_username_given
(
self
):
"""
Verify that we get an error message when omitting a username.
"""
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
response_dict
=
self
.
request_remove_user_from_cohort
(
None
,
cohort
)
self
.
verify_removed_user_from_cohort
(
None
,
response_dict
,
cohort
,
expected_error_msg
=
'No username specified'
)
def
test_user_does_not_exist
(
self
):
"""
Verify that we get an error message when the requested user to remove
does not exist.
"""
username
=
"bogus"
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
response_dict
=
self
.
request_remove_user_from_cohort
(
username
,
cohort
)
self
.
verify_removed_user_from_cohort
(
username
,
response_dict
,
cohort
,
expected_error_msg
=
'No user
\'
{0}
\'
'
.
format
(
username
)
)
def
test_can_remove_user_not_in_cohort
(
self
):
"""
Verify that we can "remove" a user from a cohort even if they are not a
member of that cohort.
"""
user
=
UserFactory
.
create
()
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[])
response_dict
=
self
.
request_remove_user_from_cohort
(
user
.
username
,
cohort
)
self
.
verify_removed_user_from_cohort
(
user
.
username
,
response_dict
,
cohort
)
def
test_can_remove_user_from_cohort
(
self
):
"""
Verify that we can remove a user from a cohort.
"""
user
=
UserFactory
.
create
()
cohort
=
CohortFactory
.
create
(
course_id
=
self
.
course
.
id
,
users
=
[
user
])
response_dict
=
self
.
request_remove_user_from_cohort
(
user
.
username
,
cohort
)
self
.
verify_removed_user_from_cohort
(
user
.
username
,
response_dict
,
cohort
)
common/djangoapps/course_groups/views.py
View file @
8a7fef07
...
@@ -3,7 +3,8 @@ from django.views.decorators.http import require_POST
...
@@ -3,7 +3,8 @@ from django.views.decorators.http import require_POST
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.paginator
import
Paginator
,
EmptyPage
,
PageNotAnInteger
from
django.core.paginator
import
Paginator
,
EmptyPage
,
PageNotAnInteger
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.http
import
HttpResponse
from
django.http
import
Http404
,
HttpResponse
,
HttpResponseBadRequest
from
django.utils.translation
import
ugettext
as
_
import
json
import
json
import
logging
import
logging
import
re
import
re
...
@@ -13,7 +14,7 @@ from courseware.courses import get_course_with_access
...
@@ -13,7 +14,7 @@ from courseware.courses import get_course_with_access
from
edxmako.shortcuts
import
render_to_response
from
edxmako.shortcuts
import
render_to_response
from
.
import
cohorts
from
.
import
cohorts
from
.models
import
CourseUserGroup
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -115,17 +116,18 @@ def users_in_cohort(request, course_key_string, cohort_id):
...
@@ -115,17 +116,18 @@ def users_in_cohort(request, course_key_string, cohort_id):
cohort
=
cohorts
.
get_cohort_by_id
(
course_key
,
int
(
cohort_id
))
cohort
=
cohorts
.
get_cohort_by_id
(
course_key
,
int
(
cohort_id
))
paginator
=
Paginator
(
cohort
.
users
.
all
(),
100
)
paginator
=
Paginator
(
cohort
.
users
.
all
(),
100
)
page
=
request
.
GET
.
get
(
'page'
)
try
:
try
:
users
=
paginator
.
page
(
page
)
page
=
int
(
request
.
GET
.
get
(
'page'
))
except
PageNotAnInteger
:
except
(
TypeError
,
ValueError
):
# return the first page
return
HttpResponseBadRequest
(
_
(
'Requested page must be numeric'
))
page
=
1
else
:
if
page
<
0
:
return
HttpResponseBadRequest
(
_
(
'Requested page must be greater than zero'
))
try
:
users
=
paginator
.
page
(
page
)
users
=
paginator
.
page
(
page
)
except
EmptyPage
:
except
EmptyPage
:
# Page is out of range. Return last page
users
=
[]
# When page > number of pages, return a blank page
page
=
paginator
.
num_pages
contacts
=
paginator
.
page
(
page
)
user_info
=
[{
'username'
:
u
.
username
,
user_info
=
[{
'username'
:
u
.
username
,
'email'
:
u
.
email
,
'email'
:
u
.
email
,
...
@@ -154,12 +156,20 @@ def add_users_to_cohort(request, course_key_string, cohort_id):
...
@@ -154,12 +156,20 @@ def add_users_to_cohort(request, course_key_string, cohort_id):
'previous_cohort': ...}, ...],
'previous_cohort': ...}, ...],
'present': [str1, str2, ...], # already there
'present': [str1, str2, ...], # already there
'unknown': [str1, str2, ...]}
'unknown': [str1, str2, ...]}
Raises Http404 if the cohort cannot be found for the given course.
"""
"""
# this is a string when we get it here
# this is a string when we get it here
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_key_string
)
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_key_string
)
get_course_with_access
(
request
.
user
,
'staff'
,
course_key
)
get_course_with_access
(
request
.
user
,
'staff'
,
course_key
)
cohort
=
cohorts
.
get_cohort_by_id
(
course_key
,
cohort_id
)
try
:
cohort
=
cohorts
.
get_cohort_by_id
(
course_key
,
cohort_id
)
except
CourseUserGroup
.
DoesNotExist
:
raise
Http404
(
"Cohort (ID {cohort_id}) not found for {course_key_string}"
.
format
(
cohort_id
=
cohort_id
,
course_key_string
=
course_key_string
))
users
=
request
.
POST
.
get
(
'users'
,
''
)
users
=
request
.
POST
.
get
(
'users'
,
''
)
added
=
[]
added
=
[]
...
...
common/static/js/course_groups/cohorts.js
View file @
8a7fef07
...
@@ -220,7 +220,7 @@ var CohortManager = (function ($) {
...
@@ -220,7 +220,7 @@ var CohortManager = (function ($) {
});
});
}
else
if
(
state
==
state_detail
)
{
}
else
if
(
state
==
state_detail
)
{
detail_header
.
text
(
"Members of "
+
cohort_title
);
detail_header
.
text
(
"Members of "
+
cohort_title
);
$
.
ajax
(
detail_url
).
done
(
show_users
).
fail
(
function
()
{
$
.
ajax
(
detail_url
,
{
data
:
{
page
:
1
}}
).
done
(
show_users
).
fail
(
function
()
{
log_error
(
"Error trying to load users in cohort"
);
log_error
(
"Error trying to load users in cohort"
);
});
});
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment