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
d9e47a6b
Commit
d9e47a6b
authored
Oct 23, 2014
by
Stephen Sanchez
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5695 from edx/sanchez/enrollment-tests-and-cleanup
enrollment tests and cleanup
parents
aba600cf
e7570c52
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
950 additions
and
101 deletions
+950
-101
common/djangoapps/enrollment/api.py
+234
-83
common/djangoapps/enrollment/data.py
+62
-5
common/djangoapps/enrollment/serializers.py
+37
-5
common/djangoapps/enrollment/tests/fake_data_api.py
+96
-0
common/djangoapps/enrollment/tests/test_api.py
+151
-0
common/djangoapps/enrollment/tests/test_data.py
+172
-0
common/djangoapps/enrollment/tests/test_views.py
+152
-0
common/djangoapps/enrollment/views.py
+46
-8
No files found.
common/djangoapps/enrollment/api.py
View file @
d9e47a6b
...
@@ -3,7 +3,11 @@ Enrollment API for creating, updating, and deleting enrollments. Also provides a
...
@@ -3,7 +3,11 @@ Enrollment API for creating, updating, and deleting enrollments. Also provides a
course level, such as available course modes.
course level, such as available course modes.
"""
"""
from
enrollment
import
data
from
django.utils
import
importlib
import
logging
from
django.conf
import
settings
log
=
logging
.
getLogger
(
__name__
)
class
CourseEnrollmentError
(
Exception
):
class
CourseEnrollmentError
(
Exception
):
...
@@ -12,8 +16,25 @@ class CourseEnrollmentError(Exception):
...
@@ -12,8 +16,25 @@ class CourseEnrollmentError(Exception):
Describes any error that may occur when reading or updating enrollment information for a student or a course.
Describes any error that may occur when reading or updating enrollment information for a student or a course.
"""
"""
def
__init__
(
self
,
msg
,
data
=
None
):
super
(
Exception
,
self
)
.
__init__
(
msg
)
# Corresponding information to help resolve the error.
self
.
data
=
data
class
CourseModeNotFoundError
(
CourseEnrollmentError
):
pass
class
EnrollmentNotFoundError
(
CourseEnrollmentError
):
pass
class
EnrollmentApiLoadError
(
CourseEnrollmentError
):
pass
pass
DEFAULT_DATA_API
=
'enrollment.data'
def
get_enrollments
(
student_id
):
def
get_enrollments
(
student_id
):
""" Retrieves all the courses a student is enrolled in.
""" Retrieves all the courses a student is enrolled in.
...
@@ -31,36 +52,58 @@ def get_enrollments(student_id):
...
@@ -31,36 +52,58 @@ def get_enrollments(student_id):
>>> get_enrollments("Bob")
>>> get_enrollments("Bob")
[
[
{
{
course_id: "edX/DemoX/2014T2",
"created": "2014-10-20T20:18:00Z",
is_active: True,
"mode": "honor",
mode: "honor",
"is_active": True,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/DemoX/2014T2",
"honor"
"enrollment_end": 2014-12-20T20:18:00Z,
],
"course_modes": [
enrollment_start: 2014-04-07,
{
enrollment_end: 2014-06-07,
"slug": "honor",
invite_only: False
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": False
}
},
},
{
{
course_id: "edX/edX-Insider/2014T2",
"created": "2014-10-25T20:18:00Z",
is_active: True,
"mode": "verified",
mode: "honor",
"is_active": True,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/edX-Insider/2014T2",
"honor",
"enrollment_end": 2014-12-20T20:18:00Z,
"verified"
"course_modes": [
],
{
enrollment_start: 2014-05-01,
"slug": "honor",
enrollment_end: 2014-06-01,
"name": "Honor Code Certificate",
invite_only: True
"min_price": 0,
},
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": True
}
}
]
]
"""
"""
return
data
.
get_course_enrollments
(
student_id
)
enrollments
=
_data_api
()
.
get_course_enrollments
(
student_id
)
for
enrollment
in
enrollments
:
enrollment
[
'student'
]
=
student_id
return
enrollments
def
get_enrollment
(
student_id
,
course_id
):
def
get_enrollment
(
student_id
,
course_id
):
...
@@ -76,23 +119,35 @@ def get_enrollment(student_id, course_id):
...
@@ -76,23 +119,35 @@ def get_enrollment(student_id, course_id):
A serializable dictionary of the course enrollment.
A serializable dictionary of the course enrollment.
Example:
Example:
>>>
add
_enrollment("Bob", "edX/DemoX/2014T2")
>>>
get
_enrollment("Bob", "edX/DemoX/2014T2")
{
{
course_id: "edX/DemoX/2014T2",
"created": "2014-10-20T20:18:00Z",
is_active: True,
"mode": "honor",
mode: "honor",
"is_active": True,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/DemoX/2014T2",
"honor"
"enrollment_end": 2014-12-20T20:18:00Z,
],
"course_modes": [
enrollment_start: 2014-04-07,
{
enrollment_end: 2014-06-07,
"slug": "honor",
invite_only: False
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": False
}
}
}
"""
"""
return
data
.
get_course_enrollment
(
student_id
,
course_id
)
enrollment
=
_data_api
()
.
get_course_enrollment
(
student_id
,
course_id
)
enrollment
[
'student'
]
=
student_id
return
enrollment
def
add_enrollment
(
student_id
,
course_id
,
mode
=
'honor'
,
is_active
=
True
):
def
add_enrollment
(
student_id
,
course_id
,
mode
=
'honor'
,
is_active
=
True
):
...
@@ -114,20 +169,33 @@ def add_enrollment(student_id, course_id, mode='honor', is_active=True):
...
@@ -114,20 +169,33 @@ def add_enrollment(student_id, course_id, mode='honor', is_active=True):
Example:
Example:
>>> add_enrollment("Bob", "edX/DemoX/2014T2", mode="audit")
>>> add_enrollment("Bob", "edX/DemoX/2014T2", mode="audit")
{
{
course_id: "edX/DemoX/2014T2",
"created": "2014-10-20T20:18:00Z",
is_active: True,
"mode": "honor",
mode: "audit",
"is_active": True,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/DemoX/2014T2",
"honor"
"enrollment_end": 2014-12-20T20:18:00Z,
],
"course_modes": [
enrollment_start: 2014-04-07,
{
enrollment_end: 2014-06-07,
"slug": "honor",
invite_only: False
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": False
}
}
}
"""
"""
return
data
.
update_course_enrollment
(
student_id
,
course_id
,
mode
=
mode
,
is_active
=
is_active
)
_validate_course_mode
(
course_id
,
mode
)
enrollment
=
_data_api
()
.
update_course_enrollment
(
student_id
,
course_id
,
mode
=
mode
,
is_active
=
is_active
)
enrollment
[
'student'
]
=
student_id
return
enrollment
def
deactivate_enrollment
(
student_id
,
course_id
):
def
deactivate_enrollment
(
student_id
,
course_id
):
...
@@ -146,20 +214,38 @@ def deactivate_enrollment(student_id, course_id):
...
@@ -146,20 +214,38 @@ def deactivate_enrollment(student_id, course_id):
Example:
Example:
>>> deactivate_enrollment("Bob", "edX/DemoX/2014T2")
>>> deactivate_enrollment("Bob", "edX/DemoX/2014T2")
{
{
course_id: "edX/DemoX/2014T2",
"created": "2014-10-20T20:18:00Z",
mode: "honor",
"mode": "honor",
is_active: False,
"is_active": False,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/DemoX/2014T2",
"honor"
"enrollment_end": 2014-12-20T20:18:00Z,
],
"course_modes": [
enrollment_start: 2014-04-07,
{
enrollment_end: 2014-06-07,
"slug": "honor",
invite_only: False
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": False
}
}
}
"""
"""
return
data
.
update_course_enrollment
(
student_id
,
course_id
,
is_active
=
False
)
# Check to see if there is an enrollment. We do not want to create a deactivated enrollment.
if
not
_data_api
()
.
get_course_enrollment
(
student_id
,
course_id
):
raise
EnrollmentNotFoundError
(
u"No enrollment was found for student {student} in course {course}"
.
format
(
student
=
student_id
,
course
=
course_id
)
)
enrollment
=
_data_api
()
.
update_course_enrollment
(
student_id
,
course_id
,
is_active
=
False
)
enrollment
[
'student'
]
=
student_id
return
enrollment
def
update_enrollment
(
student_id
,
course_id
,
mode
):
def
update_enrollment
(
student_id
,
course_id
,
mode
):
...
@@ -178,21 +264,34 @@ def update_enrollment(student_id, course_id, mode):
...
@@ -178,21 +264,34 @@ def update_enrollment(student_id, course_id, mode):
Example:
Example:
>>> update_enrollment("Bob", "edX/DemoX/2014T2", "honor")
>>> update_enrollment("Bob", "edX/DemoX/2014T2", "honor")
{
{
course_id: "edX/DemoX/2014T2",
"created": "2014-10-20T20:18:00Z",
mode: "honor",
"mode": "honor",
is_active: True,
"is_active": True,
student: "Bob",
"student": "Bob",
course_modes: [
"course": {
"audit",
"course_id": "edX/DemoX/2014T2",
"honor"
"enrollment_end": 2014-12-20T20:18:00Z,
],
"course_modes": [
enrollment_start: 2014-04-07,
{
enrollment_end: 2014-06-07,
"slug": "honor",
invite_only: False
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
"enrollment_start": 2014-10-15T20:18:00Z,
"invite_only": False
}
}
}
"""
"""
return
data
.
update_course_enrollment
(
student_id
,
course_id
,
mode
)
_validate_course_mode
(
course_id
,
mode
)
enrollment
=
_data_api
()
.
update_course_enrollment
(
student_id
,
course_id
,
mode
)
enrollment
[
'student'
]
=
student_id
return
enrollment
def
get_course_enrollment_details
(
course_id
):
def
get_course_enrollment_details
(
course_id
):
...
@@ -209,15 +308,67 @@ def get_course_enrollment_details(course_id):
...
@@ -209,15 +308,67 @@ def get_course_enrollment_details(course_id):
Example:
Example:
>>> get_course_enrollment_details("edX/DemoX/2014T2")
>>> get_course_enrollment_details("edX/DemoX/2014T2")
{
{
course_id: "edX/DemoX/2014T2",
"course_id": "edX/DemoX/2014T2",
course_modes: [
"enrollment_end": 2014-12-20T20:18:00Z,
"audit",
"course_modes": [
"honor"
{
"slug": "honor",
"name": "Honor Code Certificate",
"min_price": 0,
"suggested_prices": "",
"currency": "usd",
"expiration_datetime": null,
"description": null
}
],
],
enrollment_start: 2014-04-01,
"enrollment_start": 2014-10-15T20:18:00Z,
enrollment_end: 2014-06-01,
"invite_only": False
invite_only: False
}
}
"""
"""
pass
return
_data_api
()
.
get_course_enrollment_info
(
course_id
)
def
_validate_course_mode
(
course_id
,
mode
):
"""Checks to see if the specified course mode is valid for the course.
If the requested course mode is not available for the course, raise an error with corresponding
course enrollment information.
'honor' is special cased. If there are no course modes configured, and the specified mode is
'honor', return true, allowing the enrollment to be 'honor' even if the mode is not explicitly
set for the course.
Args:
course_id (str): The course to check against for available course modes.
mode (str): The slug for the course mode specified in the enrollment.
Returns:
None
Raises:
CourseModeNotFound: raised if the course mode is not found.
"""
course_enrollment_info
=
_data_api
()
.
get_course_enrollment_info
(
course_id
)
course_modes
=
course_enrollment_info
[
"course_modes"
]
if
mode
not
in
(
mode
[
'slug'
]
for
mode
in
course_modes
):
msg
=
u"Specified course mode unavailable for course {course_id}"
.
format
(
course_id
=
course_id
)
log
.
warn
(
msg
)
error
=
CourseModeNotFoundError
(
msg
,
course_enrollment_info
)
raise
error
def
_data_api
():
"""Returns a Data API.
This relies on Django settings to find the appropriate data API.
"""
# We retrieve the settings in-line here (rather than using the
# top-level constant), so that @override_settings will work
# in the test suite.
api_path
=
getattr
(
settings
,
"ENROLLMENT_DATA_API"
,
DEFAULT_DATA_API
)
try
:
return
importlib
.
import_module
(
api_path
)
except
(
ImportError
,
ValueError
):
raise
EnrollmentApiLoadError
(
api_path
)
common/djangoapps/enrollment/data.py
View file @
d9e47a6b
...
@@ -3,13 +3,29 @@ Data Aggregation Layer of the Enrollment API. Collects all enrollment specific d
...
@@ -3,13 +3,29 @@ Data Aggregation Layer of the Enrollment API. Collects all enrollment specific d
source to be used throughout the API.
source to be used throughout the API.
"""
"""
import
logging
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
enrollment.serializers
import
CourseEnrollmentSerializer
from
xmodule.modulestore.django
import
modulestore
from
student.models
import
CourseEnrollment
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
enrollment.serializers
import
CourseEnrollmentSerializer
,
CourseField
from
student.models
import
CourseEnrollment
,
NonExistentCourseError
log
=
logging
.
getLogger
(
__name__
)
def
get_course_enrollments
(
student_id
):
def
get_course_enrollments
(
student_id
):
"""Retrieve a list representing all aggregated data for a student's course enrollments.
Construct a representation of all course enrollment data for a specific student..
Args:
student_id (str): The name of the student to retrieve course enrollment information for.
Returns:
A serializable list of dictionaries of all aggregated enrollment data for a student.
"""
qset
=
CourseEnrollment
.
objects
.
filter
(
qset
=
CourseEnrollment
.
objects
.
filter
(
user__username
=
student_id
,
is_active
=
True
user__username
=
student_id
,
is_active
=
True
)
.
order_by
(
'created'
)
)
.
order_by
(
'created'
)
...
@@ -17,6 +33,18 @@ def get_course_enrollments(student_id):
...
@@ -17,6 +33,18 @@ def get_course_enrollments(student_id):
def
get_course_enrollment
(
student_id
,
course_id
):
def
get_course_enrollment
(
student_id
,
course_id
):
"""Retrieve an object representing all aggregated data for a student's course enrollment.
Get the course enrollment information for a specific student and course.
Args:
student_id (str): The name of the student to retrieve course enrollment information for.
course_id (str): The course to retrieve course enrollment information for.
Returns:
A serializable dictionary representing the course enrollment.
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
try
:
try
:
enrollment
=
CourseEnrollment
.
objects
.
get
(
enrollment
=
CourseEnrollment
.
objects
.
get
(
...
@@ -28,6 +56,20 @@ def get_course_enrollment(student_id, course_id):
...
@@ -28,6 +56,20 @@ def get_course_enrollment(student_id, course_id):
def
update_course_enrollment
(
student_id
,
course_id
,
mode
=
None
,
is_active
=
None
):
def
update_course_enrollment
(
student_id
,
course_id
,
mode
=
None
,
is_active
=
None
):
"""Modify a course enrollment for a student.
Allows updates to a specific course enrollment.
Args:
student_id (str): The name of the student to retrieve course enrollment information for.
course_id (str): The course to retrieve course enrollment information for.
mode (str): (Optional) The mode for the new enrollment.
is_active (boolean): (Optional) Determines if the enrollment is active.
Returns:
A serializable dictionary representing the modified course enrollment.
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
student
=
User
.
objects
.
get
(
username
=
student_id
)
student
=
User
.
objects
.
get
(
username
=
student_id
)
if
not
CourseEnrollment
.
is_enrolled
(
student
,
course_key
):
if
not
CourseEnrollment
.
is_enrolled
(
student
,
course_key
):
...
@@ -41,8 +83,23 @@ def update_course_enrollment(student_id, course_id, mode=None, is_active=None):
...
@@ -41,8 +83,23 @@ def update_course_enrollment(student_id, course_id, mode=None, is_active=None):
def
get_course_enrollment_info
(
course_id
):
def
get_course_enrollment_info
(
course_id
):
pass
"""Returns all course enrollment information for the given course.
Based on the course id, return all related course information..
def
get_course_enrollments_info
(
student_id
):
Args:
pass
course_id (str): The course to retrieve enrollment information for.
Returns:
A serializable dictionary representing the course's enrollment information.
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course
=
modulestore
()
.
get_course
(
course_key
)
if
course
is
None
:
log
.
warning
(
u"Requested enrollment information for unknown course {course}"
.
format
(
course
=
course_id
)
)
raise
NonExistentCourseError
return
CourseField
()
.
to_native
(
course
)
common/djangoapps/enrollment/serializers.py
View file @
d9e47a6b
...
@@ -3,12 +3,36 @@ Serializers for all Course Enrollment related return objects.
...
@@ -3,12 +3,36 @@ Serializers for all Course Enrollment related return objects.
"""
"""
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
rest_framework.fields
import
Field
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
course_modes.models
import
CourseMode
from
course_modes.models
import
CourseMode
class
StringListField
(
serializers
.
CharField
):
"""Custom Serializer for turning a comma delimited string into a list.
This field is designed to take a string such as "1,2,3" and turn it into an actual list
[1,2,3]
"""
def
field_to_native
(
self
,
obj
,
field_name
):
"""
Serialize the object's class name.
"""
if
not
obj
.
suggested_prices
:
return
[]
items
=
obj
.
suggested_prices
.
split
(
','
)
return
[
int
(
item
)
for
item
in
items
]
class
CourseField
(
serializers
.
RelatedField
):
class
CourseField
(
serializers
.
RelatedField
):
"""Custom field to wrap a CourseDescriptor object. Read-only."""
"""Read-Only representation of course enrollment information.
Aggregates course information from the CourseDescriptor as well as the Course Modes configured
for enrolling in the course.
"""
def
to_native
(
self
,
course
):
def
to_native
(
self
,
course
):
course_id
=
unicode
(
course
.
id
)
course_id
=
unicode
(
course
.
id
)
...
@@ -24,8 +48,10 @@ class CourseField(serializers.RelatedField):
...
@@ -24,8 +48,10 @@ class CourseField(serializers.RelatedField):
class
CourseEnrollmentSerializer
(
serializers
.
ModelSerializer
):
class
CourseEnrollmentSerializer
(
serializers
.
ModelSerializer
):
"""
"""Serializes CourseEnrollment models
Serializes CourseEnrollment models
Aggregates all data from the Course Enrollment table, and pulls in the serialization for
the Course Descriptor and course modes, to give a complete representation of course enrollment.
"""
"""
course
=
CourseField
()
course
=
CourseField
()
...
@@ -37,11 +63,17 @@ class CourseEnrollmentSerializer(serializers.ModelSerializer):
...
@@ -37,11 +63,17 @@ class CourseEnrollmentSerializer(serializers.ModelSerializer):
class
ModeSerializer
(
serializers
.
Serializer
):
class
ModeSerializer
(
serializers
.
Serializer
):
"""Serializes a course's 'Mode' tuples"""
"""Serializes a course's 'Mode' tuples
Returns a serialized representation of the modes available for course enrollment. The course
modes models are designed to return a tuple instead of the model object itself. This serializer
does not handle the model object itself, but the tuple.
"""
slug
=
serializers
.
CharField
(
max_length
=
100
)
slug
=
serializers
.
CharField
(
max_length
=
100
)
name
=
serializers
.
CharField
(
max_length
=
255
)
name
=
serializers
.
CharField
(
max_length
=
255
)
min_price
=
serializers
.
IntegerField
()
min_price
=
serializers
.
IntegerField
()
suggested_prices
=
serializers
.
Char
Field
(
max_length
=
255
)
suggested_prices
=
StringList
Field
(
max_length
=
255
)
currency
=
serializers
.
CharField
(
max_length
=
8
)
currency
=
serializers
.
CharField
(
max_length
=
8
)
expiration_datetime
=
serializers
.
DateTimeField
()
expiration_datetime
=
serializers
.
DateTimeField
()
description
=
serializers
.
CharField
()
description
=
serializers
.
CharField
()
common/djangoapps/enrollment/tests/fake_data_api.py
0 → 100644
View file @
d9e47a6b
"""
A Fake Data API for testing purposes.
"""
import
copy
import
datetime
_DEFAULT_FAKE_MODE
=
{
"slug"
:
"honor"
,
"name"
:
"Honor Code Certificate"
,
"min_price"
:
0
,
"suggested_prices"
:
""
,
"currency"
:
"usd"
,
"expiration_datetime"
:
None
,
"description"
:
None
}
_ENROLLMENTS
=
[]
_COURSES
=
[]
def
get_course_enrollments
(
student_id
):
"""Stubbed out Enrollment data request."""
return
_ENROLLMENTS
def
get_course_enrollment
(
student_id
,
course_id
):
"""Stubbed out Enrollment data request."""
return
_get_fake_enrollment
(
student_id
,
course_id
)
def
update_course_enrollment
(
student_id
,
course_id
,
mode
=
None
,
is_active
=
None
):
"""Stubbed out Enrollment data request."""
enrollment
=
_get_fake_enrollment
(
student_id
,
course_id
)
if
not
enrollment
:
enrollment
=
add_enrollment
(
student_id
,
course_id
)
if
mode
is
not
None
:
enrollment
[
'mode'
]
=
mode
if
is_active
is
not
None
:
enrollment
[
'is_active'
]
=
is_active
return
enrollment
def
get_course_enrollment_info
(
course_id
):
"""Stubbed out Enrollment data request."""
return
_get_fake_course_info
(
course_id
)
def
_get_fake_enrollment
(
student_id
,
course_id
):
for
enrollment
in
_ENROLLMENTS
:
if
student_id
==
enrollment
[
'student'
]
and
course_id
==
enrollment
[
'course'
][
'course_id'
]:
return
enrollment
def
_get_fake_course_info
(
course_id
):
for
course
in
_COURSES
:
if
course_id
==
course
[
'course_id'
]:
return
course
def
add_enrollment
(
student_id
,
course_id
,
is_active
=
True
,
mode
=
'honor'
):
enrollment
=
{
"created"
:
datetime
.
datetime
.
now
(),
"mode"
:
mode
,
"is_active"
:
is_active
,
"course"
:
_get_fake_course_info
(
course_id
),
"student"
:
student_id
}
_ENROLLMENTS
.
append
(
enrollment
)
return
enrollment
def
add_course
(
course_id
,
enrollment_start
=
None
,
enrollment_end
=
None
,
invite_only
=
False
,
course_modes
=
None
):
course_info
=
{
"course_id"
:
course_id
,
"enrollment_end"
:
enrollment_end
,
"course_modes"
:
[],
"enrollment_start"
:
enrollment_start
,
"invite_only"
:
invite_only
,
}
if
not
course_modes
:
course_info
[
'course_modes'
]
.
append
(
_DEFAULT_FAKE_MODE
)
else
:
for
mode
in
course_modes
:
new_mode
=
copy
.
deepcopy
(
_DEFAULT_FAKE_MODE
)
new_mode
[
'slug'
]
=
mode
course_info
[
'course_modes'
]
.
append
(
new_mode
)
_COURSES
.
append
(
course_info
)
def
reset
():
global
_COURSES
_COURSES
=
[]
global
_ENROLLMENTS
_ENROLLMENTS
=
[]
common/djangoapps/enrollment/tests/test_api.py
0 → 100644
View file @
d9e47a6b
"""
Tests for student enrollment.
"""
import
ddt
from
nose.tools
import
raises
import
unittest
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
django.conf
import
settings
from
enrollment
import
api
from
enrollment.tests
import
fake_data_api
@ddt.ddt
@override_settings
(
ENROLLMENT_DATA_API
=
"enrollment.tests.fake_data_api"
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
class
EnrollmentTest
(
TestCase
):
"""
Test student enrollment, especially with different course modes.
"""
USERNAME
=
"Bob"
COURSE_ID
=
"some/great/course"
def
setUp
(
self
):
fake_data_api
.
reset
()
@ddt.data
(
# Default (no course modes in the database)
# Expect automatically being enrolled as "honor".
([],
'honor'
),
# Audit / Verified / Honor
# We should always go to the "choose your course" page.
# We should also be enrolled as "honor" by default.
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
# Check for professional ed happy path.
([
'professional'
],
'professional'
)
)
@ddt.unpack
def
test_enroll
(
self
,
course_modes
,
mode
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
course_modes
)
# Enroll in the course and verify the URL we get sent to
result
=
api
.
add_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
mode
)
self
.
assertIsNotNone
(
result
)
self
.
assertEquals
(
result
[
'student'
],
self
.
USERNAME
)
self
.
assertEquals
(
result
[
'course'
][
'course_id'
],
self
.
COURSE_ID
)
self
.
assertEquals
(
result
[
'mode'
],
mode
)
get_result
=
api
.
get_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
)
self
.
assertEquals
(
result
,
get_result
)
@raises
(
api
.
CourseModeNotFoundError
)
def
test_prof_ed_enroll
(
self
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
[
'professional'
])
# Enroll in the course and verify the URL we get sent to
api
.
add_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
'verified'
)
@ddt.data
(
# Default (no course modes in the database)
# Expect that users are automatically enrolled as "honor".
([],
'honor'
),
# Audit / Verified / Honor
# We should always go to the "choose your course" page.
# We should also be enrolled as "honor" by default.
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
# Check for professional ed happy path.
([
'professional'
],
'professional'
)
)
@ddt.unpack
def
test_unenroll
(
self
,
course_modes
,
mode
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
course_modes
)
# Enroll in the course and verify the URL we get sent to
result
=
api
.
add_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
mode
)
self
.
assertIsNotNone
(
result
)
self
.
assertEquals
(
result
[
'student'
],
self
.
USERNAME
)
self
.
assertEquals
(
result
[
'course'
][
'course_id'
],
self
.
COURSE_ID
)
self
.
assertEquals
(
result
[
'mode'
],
mode
)
self
.
assertTrue
(
result
[
'is_active'
])
result
=
api
.
deactivate_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
)
self
.
assertIsNotNone
(
result
)
self
.
assertEquals
(
result
[
'student'
],
self
.
USERNAME
)
self
.
assertEquals
(
result
[
'course'
][
'course_id'
],
self
.
COURSE_ID
)
self
.
assertEquals
(
result
[
'mode'
],
mode
)
self
.
assertFalse
(
result
[
'is_active'
])
@raises
(
api
.
EnrollmentNotFoundError
)
def
test_unenroll_not_enrolled_in_course
(
self
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
[
'honor'
])
api
.
deactivate_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
)
@ddt.data
(
# Simple test of honor and verified.
([
{
'course_id'
:
'the/first/course'
,
'course_modes'
:
[],
'mode'
:
'honor'
},
{
'course_id'
:
'the/second/course'
,
'course_modes'
:
[
'honor'
,
'verified'
],
'mode'
:
'verified'
}
]),
# No enrollments
([]),
# One Enrollment
([
{
'course_id'
:
'the/third/course'
,
'course_modes'
:
[
'honor'
,
'verified'
,
'audit'
],
'mode'
:
'audit'
}
]),
)
def
test_get_all_enrollments
(
self
,
enrollments
):
for
enrollment
in
enrollments
:
fake_data_api
.
add_course
(
enrollment
[
'course_id'
],
course_modes
=
enrollment
[
'course_modes'
])
api
.
add_enrollment
(
self
.
USERNAME
,
enrollment
[
'course_id'
],
enrollment
[
'mode'
])
result
=
api
.
get_enrollments
(
self
.
USERNAME
)
self
.
assertEqual
(
len
(
enrollments
),
len
(
result
))
for
result_enrollment
in
result
:
self
.
assertIn
(
result_enrollment
[
'course'
][
'course_id'
],
[
enrollment
[
'course_id'
]
for
enrollment
in
enrollments
]
)
def
test_update_enrollment
(
self
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
[
'honor'
,
'verified'
,
'audit'
])
# Enroll in the course and verify the URL we get sent to
result
=
api
.
add_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
'audit'
)
get_result
=
api
.
get_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
)
self
.
assertEquals
(
result
,
get_result
)
result
=
api
.
update_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
'honor'
)
self
.
assertEquals
(
'honor'
,
result
[
'mode'
])
result
=
api
.
update_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
'verified'
)
self
.
assertEquals
(
'verified'
,
result
[
'mode'
])
def
test_get_course_details
(
self
):
# Add a fake course enrollment information to the fake data API
fake_data_api
.
add_course
(
self
.
COURSE_ID
,
course_modes
=
[
'honor'
,
'verified'
,
'audit'
])
result
=
api
.
get_course_enrollment_details
(
self
.
COURSE_ID
)
self
.
assertEquals
(
result
[
'course_id'
],
self
.
COURSE_ID
)
self
.
assertEquals
(
3
,
len
(
result
[
'course_modes'
]))
@override_settings
(
ENROLLMENT_DATA_API
=
'foo.bar.biz.baz'
)
@raises
(
api
.
EnrollmentApiLoadError
)
def
test_data_api_config_error
(
self
):
# Enroll in the course and verify the URL we get sent to
api
.
add_enrollment
(
self
.
USERNAME
,
self
.
COURSE_ID
,
mode
=
'audit'
)
common/djangoapps/enrollment/tests/test_data.py
0 → 100644
View file @
d9e47a6b
"""
Test the Data Aggregation Layer for Course Enrollments.
"""
import
ddt
from
nose.tools
import
raises
import
unittest
from
django.test.utils
import
override_settings
from
django.conf
import
settings
from
xmodule.modulestore.tests.django_utils
import
(
ModuleStoreTestCase
,
mixed_store_config
)
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
student.tests.factories
import
UserFactory
,
CourseModeFactory
from
student.models
import
CourseEnrollment
,
NonExistentCourseError
from
enrollment
import
data
# Since we don't need any XML course fixtures, use a modulestore configuration
# that disables the XML modulestore.
MODULESTORE_CONFIG
=
mixed_store_config
(
settings
.
COMMON_TEST_DATA_ROOT
,
{},
include_xml
=
False
)
@ddt.ddt
@override_settings
(
MODULESTORE
=
MODULESTORE_CONFIG
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
class
EnrollmentDataTest
(
ModuleStoreTestCase
):
"""
Test course enrollment data aggregation.
"""
USERNAME
=
"Bob"
EMAIL
=
"bob@example.com"
PASSWORD
=
"edx"
def
setUp
(
self
):
""" Create a course and user, then log in. """
super
(
EnrollmentDataTest
,
self
)
.
setUp
()
self
.
course
=
CourseFactory
.
create
()
self
.
user
=
UserFactory
.
create
(
username
=
self
.
USERNAME
,
email
=
self
.
EMAIL
,
password
=
self
.
PASSWORD
)
self
.
client
.
login
(
username
=
self
.
USERNAME
,
password
=
self
.
PASSWORD
)
@ddt.data
(
# Default (no course modes in the database)
# Expect that users are automatically enrolled as "honor".
([],
'honor'
),
# Audit / Verified / Honor
# We should always go to the "choose your course" page.
# We should also be enrolled as "honor" by default.
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
)
@ddt.unpack
def
test_enroll
(
self
,
course_modes
,
enrollment_mode
):
# Create the course modes (if any) required for this test case
self
.
_create_course_modes
(
course_modes
)
enrollment
=
data
.
update_course_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
),
mode
=
enrollment_mode
,
is_active
=
True
)
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
,
enrollment_mode
)
# Confirm the returned enrollment and the data match up.
self
.
assertEqual
(
course_mode
,
enrollment
[
'mode'
])
self
.
assertEqual
(
is_active
,
enrollment
[
'is_active'
])
def
test_unenroll
(
self
):
# Enroll the student in the course
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course
.
id
,
mode
=
"honor"
)
enrollment
=
data
.
update_course_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
),
is_active
=
False
)
# Determine that the returned enrollment is inactive.
self
.
assertFalse
(
enrollment
[
'is_active'
])
# Expect that we're no longer enrolled
self
.
assertFalse
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course
.
id
))
@ddt.data
(
# No course modes, no course enrollments.
([]),
# Audit / Verified / Honor course modes, with three course enrollments.
([
'honor'
,
'verified'
,
'audit'
]),
)
def
test_get_course_info
(
self
,
course_modes
):
self
.
_create_course_modes
(
course_modes
,
course
=
self
.
course
)
result_course
=
data
.
get_course_enrollment_info
(
unicode
(
self
.
course
.
id
))
result_slugs
=
[
mode
[
'slug'
]
for
mode
in
result_course
[
'course_modes'
]]
for
course_mode
in
course_modes
:
self
.
assertIn
(
course_mode
,
result_slugs
)
@ddt.data
(
# No course modes, no course enrollments.
([],
[]),
# Audit / Verified / Honor course modes, with three course enrollments.
([
'honor'
,
'verified'
,
'audit'
],
[
'1'
,
'2'
,
'3'
]),
)
@ddt.unpack
def
test_get_course_enrollments
(
self
,
course_modes
,
course_numbers
):
# Create all the courses
created_courses
=
[]
for
course_number
in
course_numbers
:
created_courses
.
append
(
CourseFactory
.
create
(
number
=
course_number
))
created_enrollments
=
[]
for
course
in
created_courses
:
self
.
_create_course_modes
(
course_modes
,
course
=
course
)
# Create the original enrollment.
created_enrollments
.
append
(
data
.
update_course_enrollment
(
self
.
user
.
username
,
unicode
(
course
.
id
),
))
# Compare the created enrollments with the results
# from the get enrollments request.
results
=
data
.
get_course_enrollments
(
self
.
user
.
username
)
self
.
assertEqual
(
results
,
created_enrollments
)
@ddt.data
(
# Default (no course modes in the database)
# Expect that users are automatically enrolled as "honor".
([],
'honor'
),
# Audit / Verified / Honor
# We should always go to the "choose your course" page.
# We should also be enrolled as "honor" by default.
([
'honor'
,
'verified'
,
'audit'
],
'verified'
),
)
@ddt.unpack
def
test_get_course_enrollment
(
self
,
course_modes
,
enrollment_mode
):
self
.
_create_course_modes
(
course_modes
)
# Try to get an enrollment before it exists.
result
=
data
.
get_course_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
))
self
.
assertIsNone
(
result
)
# Create the original enrollment.
enrollment
=
data
.
update_course_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
),
mode
=
enrollment_mode
,
is_active
=
True
)
# Get the enrollment and compare it to the original.
result
=
data
.
get_course_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
))
self
.
assertEqual
(
enrollment
,
result
)
@raises
(
NonExistentCourseError
)
def
test_non_existent_course
(
self
):
data
.
get_course_enrollment_info
(
"this/is/bananas"
)
def
_create_course_modes
(
self
,
course_modes
,
course
=
None
):
course_id
=
course
.
id
if
course
else
self
.
course
.
id
for
mode_slug
in
course_modes
:
CourseModeFactory
.
create
(
course_id
=
course_id
,
mode_slug
=
mode_slug
,
mode_display_name
=
mode_slug
,
)
common/djangoapps/enrollment/tests/test_views.py
0 → 100644
View file @
d9e47a6b
"""
Tests for student enrollment.
"""
import
ddt
import
json
import
unittest
from
django.test.utils
import
override_settings
from
django.core.urlresolvers
import
reverse
from
rest_framework.test
import
APITestCase
from
rest_framework
import
status
from
django.conf
import
settings
from
xmodule.modulestore.tests.django_utils
import
(
ModuleStoreTestCase
,
mixed_store_config
)
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
student.tests.factories
import
UserFactory
,
CourseModeFactory
from
student.models
import
CourseEnrollment
# Since we don't need any XML course fixtures, use a modulestore configuration
# that disables the XML modulestore.
MODULESTORE_CONFIG
=
mixed_store_config
(
settings
.
COMMON_TEST_DATA_ROOT
,
{},
include_xml
=
False
)
@ddt.ddt
@override_settings
(
MODULESTORE
=
MODULESTORE_CONFIG
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
class
EnrollmentTest
(
ModuleStoreTestCase
,
APITestCase
):
"""
Test student enrollment, especially with different course modes.
"""
USERNAME
=
"Bob"
EMAIL
=
"bob@example.com"
PASSWORD
=
"edx"
def
setUp
(
self
):
""" Create a course and user, then log in. """
super
(
EnrollmentTest
,
self
)
.
setUp
()
self
.
course
=
CourseFactory
.
create
()
self
.
user
=
UserFactory
.
create
(
username
=
self
.
USERNAME
,
email
=
self
.
EMAIL
,
password
=
self
.
PASSWORD
)
self
.
client
.
login
(
username
=
self
.
USERNAME
,
password
=
self
.
PASSWORD
)
@ddt.data
(
# Default (no course modes in the database)
# Expect that users are automatically enrolled as "honor".
([],
'honor'
),
# Audit / Verified / Honor
# We should always go to the "choose your course" page.
# We should also be enrolled as "honor" by default.
([
'honor'
,
'verified'
,
'audit'
],
'honor'
),
)
@ddt.unpack
def
test_enroll
(
self
,
course_modes
,
enrollment_mode
):
# Create the course modes (if any) required for this test case
for
mode_slug
in
course_modes
:
CourseModeFactory
.
create
(
course_id
=
self
.
course
.
id
,
mode_slug
=
mode_slug
,
mode_display_name
=
mode_slug
,
)
# Enroll in the course and verify the URL we get sent to
self
.
_create_enrollment
()
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
,
enrollment_mode
)
def
test_enroll_prof_ed
(
self
):
# Create the prod ed mode.
CourseModeFactory
.
create
(
course_id
=
self
.
course
.
id
,
mode_slug
=
'professional'
,
mode_display_name
=
'Professional Education'
,
)
# Enroll in the course, this will fail if the mode is not explicitly professional.
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}))
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
# While the enrollment wrong is invalid, the response content should have
# all the valid enrollment modes.
data
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
unicode
(
self
.
course
.
id
),
data
[
'course_id'
])
self
.
assertEqual
(
1
,
len
(
data
[
'course_modes'
]))
self
.
assertEqual
(
'professional'
,
data
[
'course_modes'
][
0
][
'slug'
])
def
test_unenroll
(
self
):
# Create a course mode.
CourseModeFactory
.
create
(
course_id
=
self
.
course
.
id
,
mode_slug
=
'honor'
,
mode_display_name
=
'Honor'
,
)
# Create an enrollment
resp
=
self
.
_create_enrollment
()
# Deactivate the enrollment in the course and verify the URL we get sent to
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}
),
{
'deactivate'
:
True
})
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_200_OK
)
data
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
unicode
(
self
.
course
.
id
),
data
[
'course'
][
'course_id'
])
self
.
assertEqual
(
'honor'
,
data
[
'mode'
])
self
.
assertFalse
(
data
[
'is_active'
])
def
test_user_not_authenticated
(
self
):
# Log out, so we're no longer authenticated
self
.
client
.
logout
()
# Try to enroll, this should fail.
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}))
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_401_UNAUTHORIZED
)
def
test_unenroll_not_enrolled_in_course
(
self
):
# Deactivate the enrollment in the course and verify the URL we get sent to
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}
),
{
'deactivate'
:
True
})
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
def
test_invalid_enrollment_mode
(
self
):
# Request an enrollment with verified mode, which does not exist for this course.
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}),
{
'mode'
:
'verified'
}
)
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
data
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
unicode
(
self
.
course
.
id
),
data
[
'course_id'
])
self
.
assertEqual
(
'honor'
,
data
[
'course_modes'
][
0
][
'slug'
])
def
test_with_invalid_course_id
(
self
):
# Create an enrollment
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
'entirely/fake/course'
}))
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
def
_create_enrollment
(
self
):
resp
=
self
.
client
.
post
(
reverse
(
'courseenrollment'
,
kwargs
=
{
'course_id'
:
(
unicode
(
self
.
course
.
id
))}))
self
.
assertEqual
(
resp
.
status_code
,
status
.
HTTP_200_OK
)
data
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
unicode
(
self
.
course
.
id
),
data
[
'course'
][
'course_id'
])
self
.
assertEqual
(
'honor'
,
data
[
'mode'
])
self
.
assertTrue
(
data
[
'is_active'
])
return
resp
common/djangoapps/enrollment/views.py
View file @
d9e47a6b
...
@@ -3,12 +3,14 @@ The Enrollment API Views should be simple, lean HTTP endpoints for API access. T
...
@@ -3,12 +3,14 @@ The Enrollment API Views should be simple, lean HTTP endpoints for API access. T
consist primarily of authentication, request validation, and serialization.
consist primarily of authentication, request validation, and serialization.
"""
"""
from
rest_framework
import
status
from
rest_framework.authentication
import
OAuth2Authentication
,
SessionAuthentication
from
rest_framework.authentication
import
OAuth2Authentication
,
SessionAuthentication
from
rest_framework.decorators
import
api_view
,
authentication_classes
,
permission_classes
,
throttle_classes
from
rest_framework.decorators
import
api_view
,
authentication_classes
,
permission_classes
,
throttle_classes
from
rest_framework.permissions
import
IsAuthenticated
from
rest_framework.permissions
import
IsAuthenticated
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.throttling
import
UserRateThrottle
from
rest_framework.throttling
import
UserRateThrottle
from
enrollment
import
api
from
enrollment
import
api
from
student.models
import
NonExistentCourseError
class
EnrollmentUserThrottle
(
UserRateThrottle
):
class
EnrollmentUserThrottle
(
UserRateThrottle
):
...
@@ -20,6 +22,17 @@ class EnrollmentUserThrottle(UserRateThrottle):
...
@@ -20,6 +22,17 @@ class EnrollmentUserThrottle(UserRateThrottle):
@permission_classes
((
IsAuthenticated
,))
@permission_classes
((
IsAuthenticated
,))
@throttle_classes
([
EnrollmentUserThrottle
])
@throttle_classes
([
EnrollmentUserThrottle
])
def
list_student_enrollments
(
request
):
def
list_student_enrollments
(
request
):
"""List out all the enrollments for the current student
Returns a JSON response with all the course enrollments for the current student.
Args:
request (Request): The GET request for course enrollment listings.
Returns:
A JSON serialized representation of the student's course enrollments.
"""
return
Response
(
api
.
get_enrollments
(
request
.
user
.
username
))
return
Response
(
api
.
get_enrollments
(
request
.
user
.
username
))
...
@@ -28,11 +41,36 @@ def list_student_enrollments(request):
...
@@ -28,11 +41,36 @@ def list_student_enrollments(request):
@permission_classes
((
IsAuthenticated
,))
@permission_classes
((
IsAuthenticated
,))
@throttle_classes
([
EnrollmentUserThrottle
])
@throttle_classes
([
EnrollmentUserThrottle
])
def
get_course_enrollment
(
request
,
course_id
=
None
):
def
get_course_enrollment
(
request
,
course_id
=
None
):
if
'mode'
in
request
.
DATA
:
"""Create, read, or update enrollment information for a student.
return
Response
(
api
.
update_enrollment
(
request
.
user
.
username
,
course_id
,
request
.
DATA
[
'mode'
]))
elif
'deactivate'
in
request
.
DATA
:
HTTP Endpoint for all CRUD operations for a student course enrollment. Allows creation, reading, and
return
Response
(
api
.
deactivate_enrollment
(
request
.
user
.
username
,
course_id
))
updates of the current enrollment for a particular course.
elif
course_id
and
request
.
method
==
'POST'
:
return
Response
(
api
.
add_enrollment
(
request
.
user
.
username
,
course_id
))
Args:
else
:
request (Request): To get current course enrollment information, a GET request will return
return
Response
(
api
.
get_enrollment
(
request
.
user
.
username
,
course_id
))
information for the current user and the specified course. A POST request will create a
new course enrollment for the current user. If 'mode' or 'deactivate' are found in the
POST parameters, the mode can be modified, or the enrollment can be deactivated.
course_id (str): URI element specifying the course location. Enrollment information will be
returned, created, or updated for this particular course.
Return:
A JSON serialized representation of the course enrollment. If this is a new or modified enrollment,
the returned enrollment will reflect all changes.
"""
try
:
if
'mode'
in
request
.
DATA
:
return
Response
(
api
.
update_enrollment
(
request
.
user
.
username
,
course_id
,
request
.
DATA
[
'mode'
]))
elif
'deactivate'
in
request
.
DATA
:
return
Response
(
api
.
deactivate_enrollment
(
request
.
user
.
username
,
course_id
))
elif
course_id
and
request
.
method
==
'POST'
:
return
Response
(
api
.
add_enrollment
(
request
.
user
.
username
,
course_id
))
else
:
return
Response
(
api
.
get_enrollment
(
request
.
user
.
username
,
course_id
))
except
api
.
CourseModeNotFoundError
as
error
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
,
data
=
error
.
data
)
except
NonExistentCourseError
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
except
api
.
EnrollmentNotFoundError
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
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