Files
edx-platform/common/djangoapps/enrollment/views.py
stephensanchez 22a18e350d Updating the design of the DRF views for enrollments.
Consolidate PUT and POST on the RESTful Layer.

Change URLs for API

Test cleanup.

Adding a course details URL to the enrollment API.

Change student to user

Change to v1, remove feature flag from API URLs

Updating student to user in tests

Re-ordering redirect urls to be evaluated last.

Adding pagination and testing.

Adding Django REST settings for pagination.

Revert "Re-ordering redirect urls to be evaluated last."

This reverts commit 4c9502daa383e49b46f8abec5456c271e5e24ccb.

Re-ordering redirect urls to be evaluated last.

Conflicts:
	common/djangoapps/enrollment/urls.py

Revert "Adding Django REST settings for pagination."

This reverts commit 9f8a54c41f34caa24818c88f1e75ac59f6ce5259.

Conflicts:
	common/djangoapps/enrollment/urls.py

Revert "Adding pagination and testing."

This reverts commit 0b2d46262abb78f5ad170700205e7fd28b6af942.

Additional testing, logging, and error messages.
2014-12-18 15:39:01 +00:00

201 lines
8.4 KiB
Python

"""
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 import status
from rest_framework.authentication import OAuth2Authentication
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
from enrollment import api
from enrollment.errors import CourseNotFoundError, CourseEnrollmentError, CourseModeNotFoundError
from util.authentication import SessionAuthenticationAllowInactiveUser
class EnrollmentUserThrottle(UserRateThrottle):
"""Limit the number of requests users can make to the enrollment API."""
# TODO Limit significantly after performance testing. # pylint: disable=fixme
rate = '50/second'
class EnrollmentView(APIView):
""" Enrollment API View for creating, updating, and viewing course enrollments. """
authentication_classes = OAuth2Authentication, SessionAuthenticationAllowInactiveUser
permission_classes = permissions.IsAuthenticated,
throttle_classes = EnrollmentUserThrottle,
def get(self, request, course_id=None, user=None):
"""Create, read, or update enrollment information for a user.
HTTP Endpoint for all CRUD operations for a user 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.
course_id (str): URI element specifying the course location. Enrollment information will be
returned, created, or updated for this particular course.
user (str): The user username associated with this enrollment request.
Return:
A JSON serialized representation of the course enrollment.
"""
if request.user.username != user:
# Return a 404 instead of a 403 (Unauthorized). If one user is looking up
# other users, do not let them deduce the existence of an enrollment.
return Response(status=status.HTTP_404_NOT_FOUND)
try:
return Response(api.get_enrollment(user, course_id))
except CourseEnrollmentError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": (
u"An error occurred while retrieving enrollments for user "
u"'{user}' in course '{course_id}'"
).format(user=user, course_id=course_id)
}
)
class EnrollmentCourseDetailView(APIView):
""" Enrollment API View for viewing course enrollment details. """
authentication_classes = []
permission_classes = []
throttle_classes = EnrollmentUserThrottle,
def get(self, request, course_id=None):
"""Read enrollment information for a particular course.
HTTP Endpoint for retrieving course level enrollment information.
Args:
request (Request): To get current course enrollment information, a GET request will return
information for the specified course.
course_id (str): URI element specifying the course location. Enrollment information will be
returned.
Return:
A JSON serialized representation of the course enrollment details.
"""
try:
return Response(api.get_course_enrollment_details(course_id))
except CourseNotFoundError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": (
u"No course found for course ID '{course_id}'"
).format(course_id=course_id)
}
)
class EnrollmentListView(APIView):
""" Enrollment API List View for viewing all course enrollments for a user. """
authentication_classes = OAuth2Authentication, SessionAuthenticationAllowInactiveUser
permission_classes = permissions.IsAuthenticated,
throttle_classes = EnrollmentUserThrottle,
def get(self, request):
"""List out all the enrollments for the current user
Returns a JSON response with all the course enrollments for the current user.
Args:
request (Request): The GET request for course enrollment listings.
user (str): Get all enrollments for the specified user's username.
Returns:
A JSON serialized representation of the user's course enrollments.
"""
user = request.GET.get('user', request.user.username)
if request.user.username != user:
# Return a 404 instead of a 403 (Unauthorized). If one user is looking up
# other users, do not let them deduce the existence of an enrollment.
return Response(status=status.HTTP_404_NOT_FOUND)
try:
return Response(api.get_enrollments(user))
except CourseEnrollmentError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": (
u"An error occurred while retrieving enrollments for user '{user}'"
).format(user=user)
}
)
def post(self, request):
"""Create a new enrollment
Creates a new enrollment based on the data posted. Currently all that can be specified is
the course id. All other attributes will be determined by the server, and cannot be updated
through this endpoint.
By default, this will attempt to create the enrollment with course mode 'honor'. If the course
does not have an 'honor' course mode, it will fail as a bad request and list the available
course modes.
Args:
request (Request): The POST request to create a new enrollment. POST DATA should contain
'course_details' with an attribute 'course_id' to identify which course to enroll in.
'user' may be specified as well, but must match the username of the authenticated user.
Ex. {'user': 'Bob', 'course_details': { 'course_id': 'edx/demo/T2014' } }
Returns:
A JSON serialized representation of the user's new course enrollment.
"""
user = request.DATA.get('user', request.user.username)
if not user:
user = request.user.username
if user != request.user.username:
# Return a 404 instead of a 403 (Unauthorized). If one user is looking up
# other users, do not let them deduce the existence of an enrollment.
return Response(status=status.HTTP_404_NOT_FOUND)
if 'course_details' not in request.DATA or 'course_id' not in request.DATA['course_details']:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={"message": u"Course ID must be specified to create a new enrollment."}
)
course_id = request.DATA['course_details']['course_id']
try:
return Response(api.add_enrollment(user, course_id))
except CourseModeNotFoundError as error:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": (
u"The course mode '{mode}' is not available for course '{course_id}'."
).format(mode="honor", course_id=course_id),
"course_details": error.data
})
except CourseNotFoundError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
}
)
except CourseEnrollmentError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": (
u"An error occurred while creating the new course enrollment for user "
u"'{user}' in course '{course_id}'"
).format(user=user, course_id=course_id)
}
)