Commit f01e5268 by stephensanchez

Initial setup for new enrollment API

Quick tweaks to the API

Aggregating course modes data.

Fixing the POST request endpoints.
parent ed0c383a
"""
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 ...@@ -73,6 +73,9 @@ urlpatterns = ('', # nopep8
# Feedback Form endpoint # Feedback Form endpoint
url(r'^submit_feedback$', 'util.views.submit_feedback'), 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"]: 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