Currently, we are working on removing the rest_framework_auth library from edx-platform. For this push, we need to remove the oauth2Authentication class. This PR creates a new class oauth2AuthenticationDeprecated that adds additional new relic metrics. The metrics would allow us to see how often this class is used and its success rate. The hope is that this information will help us with transitioning to a different authentication class.
124 lines
5.6 KiB
Python
124 lines
5.6 KiB
Python
"""
|
|
API views for Bulk Enrollment
|
|
"""
|
|
|
|
|
|
import json
|
|
|
|
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
|
from opaque_keys import InvalidKeyError
|
|
from opaque_keys.edx.keys import CourseKey
|
|
from rest_framework import status
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from six.moves import zip_longest
|
|
|
|
from bulk_enroll.serializers import BulkEnrollmentSerializer
|
|
from lms.djangoapps.instructor.views.api import students_update_enrollment
|
|
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, get_cohort_by_name
|
|
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
|
|
from openedx.core.djangoapps.enrollments.views import EnrollmentUserThrottle
|
|
from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated
|
|
from openedx.core.lib.api.permissions import IsStaff
|
|
from util.disable_rate_limit import can_disable_rate_limit
|
|
|
|
|
|
@can_disable_rate_limit
|
|
class BulkEnrollView(APIView):
|
|
"""
|
|
**Use Case**
|
|
|
|
Enroll multiple users in one or more courses.
|
|
|
|
**Example Request**
|
|
|
|
POST /api/bulk_enroll/v1/bulk_enroll/ {
|
|
"auto_enroll": true,
|
|
"email_students": true,
|
|
"action": "enroll",
|
|
"courses": "course-v1:edX+Demo+123,course-v1:edX+Demo2+456",
|
|
"cohorts": "cohortA,cohortA",
|
|
"identifiers": "brandon@example.com,yamilah@example.com"
|
|
}
|
|
|
|
**POST Parameters**
|
|
|
|
A POST request can include the following parameters.
|
|
|
|
* auto_enroll: When set to `true`, students will be enrolled as soon
|
|
as they register.
|
|
* email_students: When set to `true`, students will be sent email
|
|
notifications upon enrollment.
|
|
* action: Can either be set to "enroll" or "unenroll". This determines the behavior
|
|
* cohorts: Optional. If provided, the number of items in the list should be equal to
|
|
the number of courses. first cohort coressponds with the first course and so on.
|
|
The learners will be added to the corresponding cohort.
|
|
|
|
**Response Values**
|
|
|
|
If the supplied course data is valid and the enrollments were
|
|
successful, an HTTP 200 "OK" response is returned.
|
|
|
|
The HTTP 200 response body contains a list of response data for each
|
|
enrollment. (See the `instructor.views.api.students_update_enrollment`
|
|
docstring for the specifics of the response data available for each
|
|
enrollment)
|
|
|
|
If a cohorts list is provided, additional 'cohort' keys will be added
|
|
to the 'before' and 'after' states.
|
|
"""
|
|
|
|
authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated,)
|
|
permission_classes = (IsStaff,)
|
|
throttle_classes = (EnrollmentUserThrottle,)
|
|
|
|
def post(self, request):
|
|
serializer = BulkEnrollmentSerializer(data=request.data)
|
|
if serializer.is_valid():
|
|
# Setting the content type to be form data makes Django Rest Framework v3.6.3 treat all passed JSON data as
|
|
# POST parameters. This is necessary because this request is forwarded on to the student_update_enrollment
|
|
# view, which requires all of the parameters to be passed in via POST parameters.
|
|
metadata = request._request.META # pylint: disable=protected-access
|
|
metadata['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
|
|
|
|
response_dict = {
|
|
'auto_enroll': serializer.data.get('auto_enroll'),
|
|
'email_students': serializer.data.get('email_students'),
|
|
'action': serializer.data.get('action'),
|
|
'courses': {}
|
|
}
|
|
for course_id, cohort_name in zip_longest(serializer.data.get('courses'),
|
|
serializer.data.get('cohorts', [])):
|
|
response = students_update_enrollment(self.request, course_id=course_id)
|
|
response_content = json.loads(response.content.decode('utf-8'))
|
|
|
|
if cohort_name:
|
|
try:
|
|
course_key = CourseKey.from_string(course_id)
|
|
cohort = get_cohort_by_name(course_key=course_key, name=cohort_name)
|
|
except (CourseUserGroup.DoesNotExist, InvalidKeyError) as exc:
|
|
return Response(exc.message, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
for user_data in response_content['results']:
|
|
if "after" in user_data and (
|
|
user_data["after"].get("enrollment", False) is True or
|
|
user_data["after"].get("allowed", False) is True
|
|
):
|
|
user_id = user_data['identifier']
|
|
try:
|
|
_user_obj, previous_cohort, _pre_assigned = add_user_to_cohort(cohort, user_id)
|
|
except ValueError:
|
|
# User already present in cohort
|
|
previous_cohort = cohort_name
|
|
|
|
if previous_cohort:
|
|
user_data['before']['cohort'] = previous_cohort
|
|
else:
|
|
user_data['before']['cohort'] = None
|
|
user_data['after']['cohort'] = cohort_name
|
|
|
|
response_dict['courses'][course_id] = response_content
|
|
return Response(data=response_dict, status=status.HTTP_200_OK)
|
|
else:
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|