Commit f9d04e42 by Stephen Sanchez

Merge pull request #5606 from edx/sanchez/create-enrollment-api

WIP Enrollment API -- Merge to Reg-Login Form branch only
parents ed0c383a f01e5268
"""
Enrollment API for creating, updating, and deleting enrollments. Also provides access to enrollment information at a
course level, such as available course modes.
"""
from enrollment import data
class CourseEnrollmentError(Exception):
""" Generic Course Enrollment Error.
Describes any error that may occur when reading or updating enrollment information for a student or a course.
"""
pass
def get_enrollments(student_id):
""" Retrieves all the courses a student is enrolled in.
Takes a student and retrieves all relative enrollments. Includes information regarding how the student is enrolled
in the the course.
Args:
student_id (str): The ID of the student we want to retrieve course enrollment information for.
Returns:
A list of enrollment information for the given student.
Examples:
>>> 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
},
{
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
},
]
"""
return data.get_course_enrollments(student_id)
def get_enrollment(student_id, course_id):
""" Retrieves all enrollment information for the student in respect to a specific course.
Gets all the course enrollment information specific to a student in a course.
Args:
student_id (str): The student to get course enrollment information for.
course_id (str): The course to get enrollment information for.
Returns:
A serializable dictionary of the course enrollment.
Example:
>>> add_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
}
"""
return data.get_course_enrollment(student_id, course_id)
def add_enrollment(student_id, course_id, mode='honor', is_active=True):
""" Enrolls a student in a course.
Enrolls a student in a course. If the mode is not specified, this will default to 'honor'.
Args:
student_id (str): The student to enroll.
course_id (str): The course to enroll the student in.
mode (str): Optional argument for the type of enrollment to create. Ex. 'audit', 'honor', 'verified',
'professional'. If not specified, this defaults to 'honor'.
is_active (boolean): Optional argument for making the new enrollment inactive. If not specified, is_active
defaults to True.
Returns:
A serializable dictionary of the new course enrollment.
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
}
"""
return data.update_course_enrollment(student_id, course_id, mode=mode, is_active=is_active)
def deactivate_enrollment(student_id, course_id):
""" Un-enrolls a student in a course
Deactivate the enrollment of a student in a course. We will not remove the enrollment data, but simply flag it
as inactive.
Args:
student_id (str): The student associated with the deactivated enrollment.
course_id (str): The course associated with the deactivated enrollment.
Returns:
A serializable dictionary representing the deactivated course enrollment for the student.
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
}
"""
return data.update_course_enrollment(student_id, course_id, is_active=False)
def update_enrollment(student_id, course_id, mode):
""" Updates the course mode for the enrolled user.
Update a course enrollment for the given student and course.
Args:
student_id (str): The student associated with the updated enrollment.
course_id (str): The course associated with the updated enrollment.
mode (str): The new course mode for this enrollment.
Returns:
A serializable dictionary representing the updated enrollment.
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
}
"""
return data.update_course_enrollment(student_id, course_id, mode)
def get_course_enrollment_details(course_id):
""" Get the course modes for course. Also get enrollment start and end date, invite only, etc.
Given a course_id, return a serializable dictionary of properties describing course enrollment information.
Args:
course_id (str): The Course to get enrollment information for.
Returns:
A serializable dictionary of course enrollment information.
Example:
>>> get_course_enrollment_details("edX/DemoX/2014T2")
{
course_id: "edX/DemoX/2014T2",
course_modes: [
"audit",
"honor"
],
enrollment_start: 2014-04-01,
enrollment_end: 2014-06-01,
invite_only: False
}
"""
pass
"""
Data Aggregation Layer of the Enrollment API. Collects all enrollment specific data into a single
source to be used throughout the API.
"""
from django.contrib.auth.models import User
from opaque_keys.edx.keys import CourseKey
from enrollment.serializers import CourseEnrollmentSerializer
from student.models import CourseEnrollment
def get_course_enrollments(student_id):
qset = CourseEnrollment.objects.filter(
user__username=student_id, is_active=True
).order_by('created')
return CourseEnrollmentSerializer(qset).data
def get_course_enrollment(student_id, course_id):
course_key = CourseKey.from_string(course_id)
try:
enrollment = CourseEnrollment.objects.get(
user__username=student_id, course_id=course_key
)
return CourseEnrollmentSerializer(enrollment).data
except CourseEnrollment.DoesNotExist:
return None
def update_course_enrollment(student_id, course_id, mode=None, is_active=None):
course_key = CourseKey.from_string(course_id)
student = User.objects.get(username=student_id)
if not CourseEnrollment.is_enrolled(student, course_key):
enrollment = CourseEnrollment.enroll(student, course_key)
else:
enrollment = CourseEnrollment.objects.get(user=student, course_id=course_key)
enrollment.update_enrollment(is_active=is_active, mode=mode)
enrollment.save()
return CourseEnrollmentSerializer(enrollment).data
def get_course_enrollment_info(course_id):
pass
def get_course_enrollments_info(student_id):
pass
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
"""
Serializers for all Course Enrollment related return objects.
"""
from rest_framework import serializers
from student.models import CourseEnrollment
from course_modes.models import CourseMode
class CourseField(serializers.RelatedField):
"""Custom field to wrap a CourseDescriptor object. Read-only."""
def to_native(self, course):
course_id = unicode(course.id)
course_modes = ModeSerializer(CourseMode.modes_for_course(course.id)).data
return {
"course_id": course_id,
"enrollment_start": course.enrollment_start,
"enrollment_end": course.enrollment_end,
"invite_only": course.invitation_only,
"course_modes": course_modes,
}
class CourseEnrollmentSerializer(serializers.ModelSerializer):
"""
Serializes CourseEnrollment models
"""
course = CourseField()
class Meta: # pylint: disable=C0111
model = CourseEnrollment
fields = ('created', 'mode', 'is_active', 'course')
lookup_field = 'username'
class ModeSerializer(serializers.Serializer):
"""Serializes a course's 'Mode' tuples"""
slug = serializers.CharField(max_length=100)
name = serializers.CharField(max_length=255)
min_price = serializers.IntegerField()
suggested_prices = serializers.CharField(max_length=255)
currency = serializers.CharField(max_length=8)
expiration_datetime = serializers.DateTimeField()
description = serializers.CharField()
"""
URLs for the Enrollment API
"""
from django.conf import settings
from django.conf.urls import patterns, url
from .views import get_course_enrollment, list_student_enrollments
urlpatterns = patterns(
'enrollment.views',
url(r'^student$', list_student_enrollments, name='courseenrollments'),
url(
r'^course/{course_key}$'.format(course_key=settings.COURSE_ID_PATTERN),
get_course_enrollment,
name='courseenrollment'
),
)
"""
The Enrollment API Views should be simple, lean HTTP endpoints for API access. This should
consist primarily of authentication, request validation, and serialization.
"""
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
class EnrollmentUserThrottle(UserRateThrottle):
rate = '50/second' # TODO Limit significantly after performance testing.
@api_view(['GET'])
@authentication_classes((OAuth2Authentication, SessionAuthentication))
@permission_classes((IsAuthenticated,))
@throttle_classes([EnrollmentUserThrottle])
def list_student_enrollments(request):
return Response(api.get_enrollments(request.user.username))
@api_view(['GET', 'POST'])
@authentication_classes((OAuth2Authentication, SessionAuthentication))
@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))
......@@ -73,6 +73,9 @@ urlpatterns = ('', # nopep8
# Feedback Form endpoint
url(r'^submit_feedback$', 'util.views.submit_feedback'),
# Enrollment API RESTful endpoints
url(r'^enrollment/v0/', include('enrollment.urls')),
)
if settings.FEATURES["ENABLE_MOBILE_REST_API"]:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment