Merge pull request #5695 from edx/sanchez/enrollment-tests-and-cleanup
enrollment tests and cleanup
This commit is contained in:
@@ -3,7 +3,11 @@ Enrollment API for creating, updating, and deleting enrollments. Also provides a
|
||||
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):
|
||||
@@ -12,9 +16,26 @@ class CourseEnrollmentError(Exception):
|
||||
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
|
||||
|
||||
DEFAULT_DATA_API = 'enrollment.data'
|
||||
|
||||
|
||||
def get_enrollments(student_id):
|
||||
""" Retrieves all the courses a student is enrolled in.
|
||||
|
||||
@@ -31,36 +52,58 @@ def get_enrollments(student_id):
|
||||
>>> get_enrollments("Bob")
|
||||
[
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
is_active: True,
|
||||
mode: "honor",
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
],
|
||||
enrollment_start: 2014-04-07,
|
||||
enrollment_end: 2014-06-07,
|
||||
invite_only: False
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"is_active": True,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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",
|
||||
is_active: True,
|
||||
mode: "honor",
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor",
|
||||
"verified"
|
||||
],
|
||||
enrollment_start: 2014-05-01,
|
||||
enrollment_end: 2014-06-01,
|
||||
invite_only: True
|
||||
},
|
||||
"created": "2014-10-25T20:18:00Z",
|
||||
"mode": "verified",
|
||||
"is_active": True,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/edX-Insider/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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": 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):
|
||||
@@ -76,23 +119,35 @@ def get_enrollment(student_id, course_id):
|
||||
A serializable dictionary of the course enrollment.
|
||||
|
||||
Example:
|
||||
>>> add_enrollment("Bob", "edX/DemoX/2014T2")
|
||||
>>> get_enrollment("Bob", "edX/DemoX/2014T2")
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
is_active: True,
|
||||
mode: "honor",
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
],
|
||||
enrollment_start: 2014-04-07,
|
||||
enrollment_end: 2014-06-07,
|
||||
invite_only: False
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"is_active": True,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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):
|
||||
@@ -114,20 +169,33 @@ def add_enrollment(student_id, course_id, mode='honor', is_active=True):
|
||||
Example:
|
||||
>>> add_enrollment("Bob", "edX/DemoX/2014T2", mode="audit")
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
is_active: True,
|
||||
mode: "audit",
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
],
|
||||
enrollment_start: 2014-04-07,
|
||||
enrollment_end: 2014-06-07,
|
||||
invite_only: False
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"is_active": True,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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):
|
||||
@@ -146,20 +214,38 @@ def deactivate_enrollment(student_id, course_id):
|
||||
Example:
|
||||
>>> deactivate_enrollment("Bob", "edX/DemoX/2014T2")
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
mode: "honor",
|
||||
is_active: False,
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
],
|
||||
enrollment_start: 2014-04-07,
|
||||
enrollment_end: 2014-06-07,
|
||||
invite_only: False
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"is_active": False,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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):
|
||||
@@ -178,21 +264,34 @@ def update_enrollment(student_id, course_id, mode):
|
||||
Example:
|
||||
>>> update_enrollment("Bob", "edX/DemoX/2014T2", "honor")
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
mode: "honor",
|
||||
is_active: True,
|
||||
student: "Bob",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
],
|
||||
enrollment_start: 2014-04-07,
|
||||
enrollment_end: 2014-06-07,
|
||||
invite_only: False
|
||||
"created": "2014-10-20T20:18:00Z",
|
||||
"mode": "honor",
|
||||
"is_active": True,
|
||||
"student": "Bob",
|
||||
"course": {
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"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):
|
||||
@@ -209,15 +308,67 @@ def get_course_enrollment_details(course_id):
|
||||
Example:
|
||||
>>> get_course_enrollment_details("edX/DemoX/2014T2")
|
||||
{
|
||||
course_id: "edX/DemoX/2014T2",
|
||||
course_modes: [
|
||||
"audit",
|
||||
"honor"
|
||||
"course_id": "edX/DemoX/2014T2",
|
||||
"enrollment_end": 2014-12-20T20:18:00Z,
|
||||
"course_modes": [
|
||||
{
|
||||
"slug": "honor",
|
||||
"name": "Honor Code Certificate",
|
||||
"min_price": 0,
|
||||
"suggested_prices": "",
|
||||
"currency": "usd",
|
||||
"expiration_datetime": null,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
enrollment_start: 2014-04-01,
|
||||
enrollment_end: 2014-06-01,
|
||||
invite_only: False
|
||||
"enrollment_start": 2014-10-15T20:18:00Z,
|
||||
"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)
|
||||
|
||||
@@ -3,13 +3,29 @@ Data Aggregation Layer of the Enrollment API. Collects all enrollment specific d
|
||||
source to be used throughout the API.
|
||||
|
||||
"""
|
||||
import logging
|
||||
from django.contrib.auth.models import User
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from enrollment.serializers import CourseEnrollmentSerializer
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.modulestore.django import modulestore
|
||||
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):
|
||||
"""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(
|
||||
user__username=student_id, is_active=True
|
||||
).order_by('created')
|
||||
@@ -17,6 +33,18 @@ def get_course_enrollments(student_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)
|
||||
try:
|
||||
enrollment = CourseEnrollment.objects.get(
|
||||
@@ -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):
|
||||
"""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)
|
||||
student = User.objects.get(username=student_id)
|
||||
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):
|
||||
|
||||
|
||||
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):
|
||||
pass
|
||||
Args:
|
||||
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)
|
||||
|
||||
@@ -3,12 +3,36 @@ Serializers for all Course Enrollment related return objects.
|
||||
|
||||
"""
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import Field
|
||||
from student.models import CourseEnrollment
|
||||
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):
|
||||
"""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):
|
||||
course_id = unicode(course.id)
|
||||
@@ -24,8 +48,10 @@ class CourseField(serializers.RelatedField):
|
||||
|
||||
|
||||
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()
|
||||
@@ -37,11 +63,17 @@ class CourseEnrollmentSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
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)
|
||||
name = serializers.CharField(max_length=255)
|
||||
min_price = serializers.IntegerField()
|
||||
suggested_prices = serializers.CharField(max_length=255)
|
||||
suggested_prices = StringListField(max_length=255)
|
||||
currency = serializers.CharField(max_length=8)
|
||||
expiration_datetime = serializers.DateTimeField()
|
||||
description = serializers.CharField()
|
||||
|
||||
96
common/djangoapps/enrollment/tests/fake_data_api.py
Normal file
96
common/djangoapps/enrollment/tests/fake_data_api.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
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 = []
|
||||
151
common/djangoapps/enrollment/tests/test_api.py
Normal file
151
common/djangoapps/enrollment/tests/test_api.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
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')
|
||||
172
common/djangoapps/enrollment/tests/test_data.py
Normal file
172
common/djangoapps/enrollment/tests/test_data.py
Normal file
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
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,
|
||||
)
|
||||
152
common/djangoapps/enrollment/tests/test_views.py
Normal file
152
common/djangoapps/enrollment/tests/test_views.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
"""
|
||||
from rest_framework import status
|
||||
from rest_framework.authentication import OAuth2Authentication, SessionAuthentication
|
||||
from rest_framework.decorators import api_view, authentication_classes, permission_classes, throttle_classes
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import UserRateThrottle
|
||||
from enrollment import api
|
||||
from student.models import NonExistentCourseError
|
||||
|
||||
|
||||
class EnrollmentUserThrottle(UserRateThrottle):
|
||||
@@ -20,6 +22,17 @@ class EnrollmentUserThrottle(UserRateThrottle):
|
||||
@permission_classes((IsAuthenticated,))
|
||||
@throttle_classes([EnrollmentUserThrottle])
|
||||
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))
|
||||
|
||||
|
||||
@@ -28,11 +41,36 @@ def list_student_enrollments(request):
|
||||
@permission_classes((IsAuthenticated,))
|
||||
@throttle_classes([EnrollmentUserThrottle])
|
||||
def get_course_enrollment(request, course_id=None):
|
||||
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))
|
||||
"""Create, read, or update enrollment information for a student.
|
||||
|
||||
HTTP Endpoint for all CRUD operations for a student course enrollment. Allows creation, reading, and
|
||||
updates of the current enrollment for a particular course.
|
||||
|
||||
Args:
|
||||
request (Request): To get current course enrollment information, a GET request will return
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user