Commit 4527a67d by Chris Dodge

add ability to enroll users into courses

parent f59612ce
...@@ -16,5 +16,6 @@ urlpatterns = patterns( ...@@ -16,5 +16,6 @@ urlpatterns = patterns(
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/updates$', 'course_updates'), url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/updates$', 'course_updates'),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/static_tabs/(?P<tab_id>[a-zA-Z0-9/_:]+)$', 'static_tab_detail'), url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/static_tabs/(?P<tab_id>[a-zA-Z0-9/_:]+)$', 'static_tab_detail'),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/static_tabs$', 'static_tabs_list'), url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/static_tabs$', 'static_tabs_list'),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/users$', 'course_users_list'),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)$', 'courses_detail'), url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)$', 'courses_detail'),
) )
""" API implementation for course-oriented interactions. """ """ API implementation for course-oriented interactions. """
from django.contrib.auth.models import Group from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from lxml import etree from lxml import etree
from StringIO import StringIO from StringIO import StringIO
...@@ -18,6 +18,7 @@ from xmodule.modulestore import Location, InvalidLocationError ...@@ -18,6 +18,7 @@ from xmodule.modulestore import Location, InvalidLocationError
from courseware.courses import get_course_about_section, get_course_info_section from courseware.courses import get_course_about_section, get_course_info_section
from courseware.views import get_static_tab_contents from courseware.views import get_static_tab_contents
from student.models import CourseEnrollment, CourseEnrollmentAllowed
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -549,3 +550,72 @@ def static_tab_detail(request, course_id, tab_id): ...@@ -549,3 +550,72 @@ def static_tab_detail(request, course_id, tab_id):
return Response({}, status=status.HTTP_404_NOT_FOUND) return Response({}, status=status.HTTP_404_NOT_FOUND)
return Response(response_data) return Response(response_data)
@api_view(['GET', 'POST', 'DELETE'])
@permission_classes((ApiKeyHeaderPermission,))
def course_users_list(request, course_id):
"""
GET returns a list of users enrolled in the course_id
POST enrolls a student in the course. Note, this can be a user_id or just an email, in case
the user does not exist in the system
"""
store = modulestore()
response_data = OrderedDict()
try:
# find the course
course_module = store.get_course(course_id)
if not course_module:
return Response({}, status=status.HTTP_404_NOT_FOUND)
except InvalidLocationError:
return Response({}, status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
# Get a list of all enrolled students
users = CourseEnrollment.users_enrolled_in(course_id)
response_data['enrollments'] = []
for user in users:
user_data = OrderedDict()
user_data['id'] = user.id
user_data['email'] = user.email
user_data['username'] = user.username
# @TODO: Should we create a URI resourse that points to user?!? But that's in a different URL subpath
response_data['enrollments'].append(user_data)
# Then list all enrollments which are pending. These are enrollments for students that have not yet
# created an account
pending_enrollments = CourseEnrollmentAllowed.objects.filter(course_id=course_id)
if pending_enrollments:
response_data['pending_enrollments'] = []
for cea in pending_enrollments:
response_data['pending_enrollments'].append(cea.email)
return Response(response_data)
elif request.method == 'POST':
if 'user_id' in request.DATA:
user_id = request.DATA['user_id']
try:
existing_user = User.objects.get(id=user_id)
CourseEnrollment.enroll(existing_user, course_id)
except ObjectDoesNotExist:
return Response({'err': 'user_does_not_exist'}, status=status.HTTP_400_BAD_REQUEST)
elif 'email' in request.DATA:
# If caller passed in an email, then let's look up user by email address
# if it doesn't exist then we need to assume that the student does not exist
# in our database and that the instructor is pre-enrolling ment
email = request.DATA['email']
try:
existing_user = User.objects.get(email=email)
CourseEnrollment.enroll(existing_user, course_id)
except ObjectDoesNotExist:
if not request.DATA.get('allow_pending', False):
return Response({'err': 'user_does_not_exist'}, status=status.HTTP_400_BAD_REQUEST)
# In this case we can pre-enroll a non-existing student. This is what the
# CourseEnrollmentAllowed table is for
# NOTE: This logic really should live in CourseEnrollment.....
cea, _ = CourseEnrollmentAllowed.objects.get_or_create(course_id=course_id, email=email)
cea.auto_enroll = True
cea.save()
return Response({}, status.HTTP_201_CREATED)
...@@ -7,6 +7,7 @@ Run these tests @ Devstack: ...@@ -7,6 +7,7 @@ Run these tests @ Devstack:
import simplejson as json import simplejson as json
import unittest import unittest
import uuid import uuid
from random import randint
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase, Client from django.test import TestCase, Client
...@@ -450,3 +451,73 @@ class CoursesApiTests(TestCase): ...@@ -450,3 +451,73 @@ class CoursesApiTests(TestCase):
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_course_enrollments(self):
test_uri = self.base_courses_uri + '/' + self.test_course_id + '/users'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
# assert that there is no enrolled students
enrollments = response.data['enrollments']
self.assertEqual(len(enrollments), 0)
self.assertNotIn('pending_enrollments', response.data)
# enroll a non-existing student
# first, don't allow non-existing
post_data = {}
post_data['email'] = 'test+pending@tester.com'
post_data['allow_pending'] = False
response = self.do_post(test_uri, post_data)
self.assertEqual(response.status_code, 400)
post_data['allow_pending'] = True
response = self.do_post(test_uri, post_data)
self.assertEqual(response.status_code, 201)
# re-run query
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
# assert that we just have a single pending enrollment
enrollments = response.data['enrollments']
self.assertEqual(len(enrollments), 0)
self.assertIn('pending_enrollments', response.data)
pending = response.data['pending_enrollments']
self.assertEqual(len(pending), 1)
self.assertEqual(pending[0], 'test+pending@tester.com')
# create a new user (note, this calls into the /users/ subsystem)
test_user_uri = '/api/users'
local_username = "some_test_user" + str(randint(11, 99))
local_email = "test+notpending@tester.com"
data = {
'email': local_email,
'username': local_username,
'password': 'fooabr',
'first_name': 'Joe',
'last_name': 'Brown'
}
response = self.do_post(test_user_uri, data)
self.assertEqual(response.status_code, 201)
self.assertGreater(response.data['id'], 0)
created_user_id = response.data['id']
# now register this user
post_data = {}
post_data['user_id'] = created_user_id
response = self.do_post(test_uri, post_data)
self.assertEqual(response.status_code, 201)
# now re-query, we should see it listed now in the list of enrollments
# re-run query
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertGreater(len(response.data), 0)
# assert that we just have a single pending enrollment
enrollments = response.data['enrollments']
self.assertEqual(len(enrollments), 1)
self.assertEqual(enrollments[0]['id'], created_user_id)
self.assertEqual(enrollments[0]['email'], local_email)
self.assertEqual(enrollments[0]['username'], local_username)
...@@ -105,6 +105,9 @@ def user_list(request): ...@@ -105,6 +105,9 @@ def user_list(request):
user.last_name = last_name user.last_name = last_name
user.save() user.save()
# CDODGE: @TODO: We will have to extend this to look in the CourseEnrollmentAllowed table and
# auto-enroll students when they create a new account. Also be sure to remove from
# the CourseEnrollmentAllow table after the auto-registration has taken place
if user: if user:
status_code = status.HTTP_201_CREATED status_code = status.HTTP_201_CREATED
response_data = _serialize_user(response_data, user) response_data = _serialize_user(response_data, user)
......
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