Files
edx-platform/lms/djangoapps/completion/api/v1/views.py
2017-12-14 16:40:15 -06:00

137 lines
4.8 KiB
Python

""" API v1 views. """
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.utils.translation import ugettext as _
from django.db import DatabaseError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
from rest_framework import status
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys import InvalidKeyError
from lms.djangoapps.completion.models import BlockCompletion
from openedx.core.djangoapps.content.course_structures.models import CourseStructure
from openedx.core.lib.api.permissions import IsStaffOrOwner
from student.models import CourseEnrollment
from completion import waffle
class CompletionBatchView(APIView):
"""
Handles API requests to submit batch completions.
"""
permission_classes = (permissions.IsAuthenticated, IsStaffOrOwner,)
REQUIRED_KEYS = ['username', 'course_key', 'blocks']
def _validate_and_parse(self, batch_object):
"""
Performs validation on the batch object to make sure it is in the proper format.
Parameters:
* batch_object: The data provided to a POST. The expected format is the following:
{
"username": "username",
"course_key": "course-key",
"blocks": {
"block_key1": 0.0,
"block_key2": 1.0,
"block_key3": 1.0,
}
}
Return Value:
* tuple: (User, CourseKey, List of tuples (UsageKey, completion_float)
Raises:
django.core.exceptions.ValidationError:
If any aspect of validation fails a ValidationError is raised.
ObjectDoesNotExist:
If a database object cannot be found an ObjectDoesNotExist is raised.
"""
if not waffle.waffle().is_enabled(waffle.ENABLE_COMPLETION_TRACKING):
raise ValidationError(
_("BlockCompletion.objects.submit_batch_completion should not be called when the feature is disabled.")
)
for key in self.REQUIRED_KEYS:
if key not in batch_object:
raise ValidationError(_("Key '{key}' not found.".format(key=key)))
username = batch_object['username']
user = User.objects.get(username=username)
course_key = batch_object['course_key']
try:
course_key_obj = CourseKey.from_string(course_key)
except InvalidKeyError:
raise ValidationError(_("Invalid course key: {}").format(course_key))
course_structure = CourseStructure.objects.get(course_id=course_key_obj)
if not CourseEnrollment.is_enrolled(user, course_key_obj):
raise ValidationError(_('User is not enrolled in course.'))
blocks = batch_object['blocks']
block_objs = []
for block_key in blocks:
if block_key not in course_structure.structure['blocks'].keys():
raise ValidationError(_("Block with key: '{key}' is not in course {course}")
.format(key=block_key, course=course_key))
block_key_obj = UsageKey.from_string(block_key)
completion = float(blocks[block_key])
block_objs.append((block_key_obj, completion))
return user, course_key_obj, block_objs
def post(self, request, *args, **kwargs):
"""
Inserts a batch of completions.
REST Endpoint Format:
{
"username": "username",
"course_key": "course-key",
"blocks": {
"block_key1": 0.0,
"block_key2": 1.0,
"block_key3": 1.0,
}
}
**Returns**
A Response object, with an appropriate status code.
If successful, status code is 200.
{
"detail" : _("ok")
}
Otherwise, a 400 or 404 may be returned, and the "detail" content will explain the error.
"""
batch_object = request.data or {}
try:
user, course_key, blocks = self._validate_and_parse(batch_object)
BlockCompletion.objects.submit_batch_completion(user, course_key, blocks)
except (ValidationError, ValueError) as exc:
return Response({
"detail": exc.message,
}, status=status.HTTP_400_BAD_REQUEST)
except ObjectDoesNotExist as exc:
return Response({
"detail": exc.message,
}, status=status.HTTP_404_NOT_FOUND)
except DatabaseError as exc:
return Response({
"detail": exc.message,
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response({"detail": _("ok")}, status=status.HTTP_200_OK)