Files
edx-platform/lms/djangoapps/bulk_enroll/views.py
Kyle McCormick d1a775d3cd Use full names for lms.djangoapps imports (#25401)
* Use full LMS imports paths in LMS settings and urls modules
* Use full LMS import paths in Studio settings and urls modules
* Import from lms.djangoapps.badges instead of badges
* Import from lms.djangoapps.branding instead of branding
* Import from lms.djangoapps.bulk_email instead of bulk_email
* Import from lms.djangoapps.bulk_enroll instead of bulk_enroll
* Import from lms.djangoapps.ccx instead of ccx
* Import from lms.djangoapps.course_api instead of course_api
* Import from lms.djangoapps.course_blocks instead of course_blocks
* Import from lms.djangoapps.course_wiki instead of course_wiki
* Import from lms.djangoapps.courseware instead of courseware
* Import from lms.djangoapps.dashboard instead of dashboard
* Import from lms.djangoapps.discussion import discussion
* Import from lms.djangoapps.email_marketing instead of email_marketing
* Import from lms.djangoapps.experiments instead of experiments
* Import from lms.djangoapps.gating instead of gating
* Import from lms.djangoapps.grades instead of grades
* Import from lms.djangoapps.instructor_analytics instead of instructor_analytics
* Import form lms.djangoapps.lms_xblock instead of lms_xblock
* Import from lms.djangoapps.lti_provider instead of lti_provider
* Import from lms.djangoapps.mobile_api instead of mobile_api
* Import from lms.djangoapps.rss_proxy instead of rss_proxy
* Import from lms.djangoapps.static_template_view instead of static_template_view
* Import from lms.djangoapps.survey instead of survey
* Import from lms.djangoapps.verify_student instead of verify_student
* Stop suppressing EdxPlatformDeprecatedImportWarnings
2020-11-04 08:48:33 -05:00

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 lms.djangoapps.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 BearerAuthentication
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, BearerAuthentication,)
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)