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
7eecfd89
Commit
7eecfd89
authored
May 19, 2015
by
Renzo Lucioni
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8094 from edx/renzo/enrollment-api-unenrollment
Allow enrollment API to deactivate enrollments
parents
0dbdbfef
960b02cf
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
85 additions
and
19 deletions
+85
-19
common/djangoapps/enrollment/api.py
+2
-1
common/djangoapps/enrollment/tests/test_views.py
+59
-3
common/djangoapps/enrollment/views.py
+24
-15
No files found.
common/djangoapps/enrollment/api.py
View file @
7eecfd89
...
...
@@ -225,7 +225,8 @@ def update_enrollment(user_id, course_id, mode=None, is_active=None):
}
"""
_validate_course_mode
(
course_id
,
mode
)
if
mode
is
not
None
:
_validate_course_mode
(
course_id
,
mode
)
enrollment
=
_data_api
()
.
update_course_enrollment
(
user_id
,
course_id
,
mode
=
mode
,
is_active
=
is_active
)
if
enrollment
is
None
:
msg
=
u"Course Enrollment not found for user {user} in course {course}"
.
format
(
user
=
user_id
,
course
=
course_id
)
...
...
common/djangoapps/enrollment/tests/test_views.py
View file @
7eecfd89
...
...
@@ -43,6 +43,7 @@ class EnrollmentTestMixin(object):
email_opt_in
=
None
,
as_server
=
False
,
mode
=
CourseMode
.
HONOR
,
is_active
=
None
,
):
"""
Enroll in the course and verify the response's status code. If the expected status is 200, also validates
...
...
@@ -61,6 +62,10 @@ class EnrollmentTestMixin(object):
},
'user'
:
username
}
if
is_active
is
not
None
:
data
[
'is_active'
]
=
is_active
if
email_opt_in
is
not
None
:
data
[
'email_opt_in'
]
=
email_opt_in
...
...
@@ -72,14 +77,32 @@ class EnrollmentTestMixin(object):
response
=
self
.
client
.
post
(
url
,
json
.
dumps
(
data
),
content_type
=
'application/json'
,
**
extra
)
self
.
assertEqual
(
response
.
status_code
,
expected_status
)
if
expected_status
in
[
status
.
HTTP_200_OK
,
status
.
HTTP_200_OK
]
:
if
expected_status
==
status
.
HTTP_200_OK
:
data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
course_id
,
data
[
'course_details'
][
'course_id'
])
self
.
assertEqual
(
mode
,
data
[
'mode'
])
self
.
assertTrue
(
data
[
'is_active'
])
if
mode
is
not
None
:
self
.
assertEqual
(
mode
,
data
[
'mode'
])
if
is_active
is
not
None
:
self
.
assertEqual
(
is_active
,
data
[
'is_active'
])
else
:
self
.
assertTrue
(
data
[
'is_active'
])
return
response
def
assert_enrollment_activation
(
self
,
expected_activation
,
expected_mode
=
CourseMode
.
VERIFIED
):
"""Change an enrollment's activation and verify its activation and mode are as expected."""
self
.
assert_enrollment_status
(
as_server
=
True
,
mode
=
None
,
is_active
=
expected_activation
,
expected_status
=
status
.
HTTP_200_OK
)
actual_mode
,
actual_activation
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
.
id
)
self
.
assertEqual
(
actual_activation
,
expected_activation
)
self
.
assertEqual
(
actual_mode
,
expected_mode
)
@override_settings
(
EDX_API_KEY
=
"i am a key"
)
@ddt.ddt
...
...
@@ -503,6 +526,39 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
self
.
assertTrue
(
is_active
)
self
.
assertEqual
(
course_mode
,
CourseMode
.
HONOR
)
def
test_deactivate_enrollment
(
self
):
"""With the right API key, deactivate (i.e., unenroll from) an existing enrollment."""
# Create an honor and verified mode for a course. This allows an update.
for
mode
in
[
CourseMode
.
HONOR
,
CourseMode
.
VERIFIED
]:
CourseModeFactory
.
create
(
course_id
=
self
.
course
.
id
,
mode_slug
=
mode
,
mode_display_name
=
mode
,
)
# Create a 'verified' enrollment
self
.
assert_enrollment_status
(
as_server
=
True
,
mode
=
CourseMode
.
VERIFIED
)
# Check that the enrollment is 'verified' and active.
self
.
assertTrue
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course
.
id
))
course_mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course
.
id
)
self
.
assertTrue
(
is_active
)
self
.
assertEqual
(
course_mode
,
CourseMode
.
VERIFIED
)
# Verify that a non-Boolean enrollment status is treated as invalid.
self
.
assert_enrollment_status
(
as_server
=
True
,
mode
=
None
,
is_active
=
'foo'
,
expected_status
=
status
.
HTTP_400_BAD_REQUEST
)
# Verify that the enrollment has been deactivated, and that the mode is unchanged.
self
.
assert_enrollment_activation
(
False
)
# Verify that enrollment deactivation is idempotent.
self
.
assert_enrollment_activation
(
False
)
def
test_change_mode_from_user
(
self
):
"""Users should not be able to alter the enrollment mode on an enrollment. """
# Create an honor and verified mode for a course. This allows an update.
...
...
common/djangoapps/enrollment/views.py
View file @
7eecfd89
...
...
@@ -257,13 +257,16 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
* user: The user ID of the currently logged in user. Optional. You cannot use the command to enroll a different user.
* mode: The Course Mode for the enrollment. Individual users cannot upgrade their enrollment mode from
'honor'. Only server to server requests can enroll with other modes. Optional.
'honor'. Only server-to-server requests can enroll with other modes. Optional.
* is_active: A Boolean indicating whether the enrollment is active. Only server-to-server requests are
allowed to deactivate an enrollment. Optional.
* course details: A collection that contains:
* course_id: The unique identifier for the course.
* email_opt_in: A
b
oolean indicating whether the user
* email_opt_in: A
B
oolean indicating whether the user
wishes to opt into email from the organization running this course. Optional.
**Response Values**
...
...
@@ -313,9 +316,7 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
# cross-domain CSRF.
@method_decorator
(
ensure_csrf_cookie_cross_domain
)
def
get
(
self
,
request
):
"""
Gets a list of all course enrollments for the currently logged in user.
"""
"""Gets a list of all course enrollments for the currently logged in user."""
username
=
request
.
GET
.
get
(
'user'
,
request
.
user
.
username
)
if
request
.
user
.
username
!=
username
and
not
self
.
has_api_key_permissions
(
request
):
# Return a 404 instead of a 403 (Unauthorized). If one user is looking up
...
...
@@ -334,8 +335,10 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
)
def
post
(
self
,
request
):
"""
Enrolls the currently logged in user in a course.
"""Enrolls the currently logged-in user in a course.
Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
"""
# Get the User, Course ID, and Mode from the request.
username
=
request
.
DATA
.
get
(
'user'
,
request
.
user
.
username
)
...
...
@@ -407,22 +410,28 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
)
try
:
# Check if the user is currently enrolled, and if it is the same as the current enrolled mode. We do not
# have to check if it is inactive or not, because if it is, we are still upgrading if the mode is different,
# and either path will re-activate the enrollment.
#
# Only server-to-server calls will currently be allowed to modify the mode for existing enrollments. All
# other requests will go through add_enrollment(), which will allow creating of new enrollments, and
# re-activating enrollments
is_active
=
request
.
DATA
.
get
(
'is_active'
)
# Check if the requested activation status is None or a Boolean
if
is_active
is
not
None
and
not
isinstance
(
is_active
,
bool
):
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
,
data
=
{
'message'
:
(
u"'{value}' is an invalid enrollment activation status."
)
.
format
(
value
=
is_active
)
}
)
enrollment
=
api
.
get_enrollment
(
username
,
unicode
(
course_id
))
if
has_api_key_permissions
and
enrollment
and
enrollment
[
'mode'
]
!=
mode
:
response
=
api
.
update_enrollment
(
username
,
unicode
(
course_id
),
mode
=
mode
)
response
=
api
.
update_enrollment
(
username
,
unicode
(
course_id
),
mode
=
mode
,
is_active
=
is_active
)
else
:
# Will reactivate inactive enrollments.
response
=
api
.
add_enrollment
(
username
,
unicode
(
course_id
),
mode
=
mode
)
email_opt_in
=
request
.
DATA
.
get
(
'email_opt_in'
,
None
)
if
email_opt_in
is
not
None
:
org
=
course_id
.
org
update_email_opt_in
(
request
.
user
,
org
,
email_opt_in
)
return
Response
(
response
)
except
CourseModeNotFoundError
as
error
:
return
Response
(
...
...
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