From 551eaaf706093b2f6f4c202275542a4556c934f5 Mon Sep 17 00:00:00 2001 From: Ben Patterson Date: Mon, 25 Jan 2016 20:04:00 -0500 Subject: [PATCH] Enroll already-existing user to a course via management command. Our current sandbox provisioning uses a create_user management command, which creates and enrolls a user into a course. However, for TE-1128, we will need a way to enroll the same user to multiple courses (which are dynamic at time of provisioning). Using the existing management command is no longer feasible for that, so we are using one that accesses the enrollment API, which is the new standard for new enrollment capabilities. --- .../enrollment/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/enroll_user_in_course.py | 52 +++++++++++ .../enrollment/management/tests/__init__.py | 0 .../tests/test_enroll_user_in_course.py | 87 +++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 common/djangoapps/enrollment/management/__init__.py create mode 100644 common/djangoapps/enrollment/management/commands/__init__.py create mode 100644 common/djangoapps/enrollment/management/commands/enroll_user_in_course.py create mode 100644 common/djangoapps/enrollment/management/tests/__init__.py create mode 100644 common/djangoapps/enrollment/management/tests/test_enroll_user_in_course.py diff --git a/common/djangoapps/enrollment/management/__init__.py b/common/djangoapps/enrollment/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/enrollment/management/commands/__init__.py b/common/djangoapps/enrollment/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/enrollment/management/commands/enroll_user_in_course.py b/common/djangoapps/enrollment/management/commands/enroll_user_in_course.py new file mode 100644 index 0000000000..033ed26565 --- /dev/null +++ b/common/djangoapps/enrollment/management/commands/enroll_user_in_course.py @@ -0,0 +1,52 @@ +""" +Management command for enrolling a user into a course via the enrollment api +""" +from django.contrib.auth.models import User +from django.core.management.base import BaseCommand +from enrollment.data import CourseEnrollmentExistsError +from enrollment.api import add_enrollment + + +class Command(BaseCommand): + """ + Enroll a user into a course + """ + help = """ + This enrolls a user into a given course with the default mode (e.g., 'honor', 'audit', etc). + + User email and course ID are required. + + example: + # Enroll a user test@example.com into the demo course + manage.py ... enroll_user_in_course -e test@example.com -c edX/Open_DemoX/edx_demo_course + + This command can be run multiple times on the same user+course (i.e. it is idempotent). + """ + + def add_arguments(self, parser): + + parser.add_argument( + '-e', '--email', + nargs=1, + required=True, + help='Email for user' + ) + parser.add_argument( + '-c', '--course', + nargs=1, + required=True, + help='course ID to enroll the user in') + + def handle(self, *args, **options): + """ + Get and enroll a user in the given course. Mode is optional and defers to the enrollment API for defaults. + """ + email = options['email'][0] + course = options['course'][0] + + user = User.objects.get(email=email) + try: + add_enrollment(user.username, course) + except CourseEnrollmentExistsError: + # If the user is already enrolled in the course, do nothing. + pass diff --git a/common/djangoapps/enrollment/management/tests/__init__.py b/common/djangoapps/enrollment/management/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/enrollment/management/tests/test_enroll_user_in_course.py b/common/djangoapps/enrollment/management/tests/test_enroll_user_in_course.py new file mode 100644 index 0000000000..9f36019666 --- /dev/null +++ b/common/djangoapps/enrollment/management/tests/test_enroll_user_in_course.py @@ -0,0 +1,87 @@ +""" Test the change_enrollment command line script.""" + +import ddt +import unittest +from uuid import uuid4 + +from django.conf import settings +from django.core.management import call_command +from django.core.management.base import CommandError + +from enrollment.api import get_enrollment +from student.tests.factories import UserFactory + +from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory + + +@ddt.ddt +@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') +class EnrollManagementCommandTest(SharedModuleStoreTestCase): + """ + Test the enroll_user_in_course management command + """ + + @classmethod + def setUpClass(cls): + super(EnrollManagementCommandTest, cls).setUpClass() + cls.course = CourseFactory.create(org='fooX', number='007') + + def setUp(self): + super(EnrollManagementCommandTest, self).setUp() + self.course_id = unicode(self.course.id) + self.username = 'ralph' + uuid4().hex + self.user_email = self.username + '@example.com' + + UserFactory(username=self.username, email=self.user_email) + + def test_enroll_user(self): + + command_args = [ + '--course', self.course_id, + '--email', self.user_email, + ] + + call_command( + 'enroll_user_in_course', + *command_args + ) + + user_enroll = get_enrollment(self.username, self.course_id) + self.assertTrue(user_enroll['is_active']) + + def test_enroll_user_twice(self): + """ + Ensures the command is idempotent. + """ + + command_args = [ + '--course', self.course_id, + '--email', self.user_email, + ] + + for _ in range(2): + call_command( + 'enroll_user_in_course', + *command_args + ) + + # Second run does not impact the first run (i.e., the + # user is still enrolled, no exception was raised, etc) + user_enroll = get_enrollment(self.username, self.course_id) + self.assertTrue(user_enroll['is_active']) + + @ddt.data(['--email', 'foo'], ['--course', 'bar'], ['--bad-param', 'baz']) + def test_not_enough_args(self, arg): + """ + When the command is missing certain arguments, it should + raise an exception + """ + + command_args = arg + + with self.assertRaises(CommandError): + call_command( + 'enroll_user_in_course', + *command_args + )