From dabad9fdfdebd6b8c3f16eca745f318ac64cdd3b Mon Sep 17 00:00:00 2001
From: Christie Rice <8483753+crice100@users.noreply.github.com>
Date: Thu, 25 Mar 2021 11:01:41 -0400
Subject: [PATCH] feat: Add cert_generation management command (#27131)

MICROBA-1078
---
 .../commands/cert_allowlist_generation.py     |  2 +-
 .../management/commands/cert_generation.py    | 75 ++++++++++++++++++
 .../tests/test_cert_allowlist_generation.py   |  4 +-
 .../commands/tests/test_cert_generation.py    | 76 +++++++++++++++++++
 4 files changed, 154 insertions(+), 3 deletions(-)
 create mode 100644 lms/djangoapps/certificates/management/commands/cert_generation.py
 create mode 100644 lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py

diff --git a/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py b/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py
index 7843daaea51..3de9e2ad1cc 100644
--- a/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py
+++ b/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py
@@ -22,7 +22,7 @@ class Command(BaseCommand):
     Management command to generate allowlist certificates for one or more users in a given course run.
 
     Example usage:
-    ./manage.py lms cert_allowlist_generation -u 123 verified -c course-v1:edX+DemoX+Demo_Course
+    ./manage.py lms cert_allowlist_generation -u 123 456 -c course-v1:edX+DemoX+Demo_Course
     """
 
     help = """
diff --git a/lms/djangoapps/certificates/management/commands/cert_generation.py b/lms/djangoapps/certificates/management/commands/cert_generation.py
new file mode 100644
index 00000000000..1d0f8ecd3e8
--- /dev/null
+++ b/lms/djangoapps/certificates/management/commands/cert_generation.py
@@ -0,0 +1,75 @@
+"""
+Management command to generate course certificates for one or more users in a given course run.
+"""
+
+import logging
+
+from django.contrib.auth import get_user_model
+from django.core.management.base import BaseCommand, CommandError
+from opaque_keys import InvalidKeyError
+from opaque_keys.edx.keys import CourseKey
+
+from lms.djangoapps.certificates.generation_handler import generate_certificate_task
+
+User = get_user_model()
+log = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+    """
+    Management command to generate course certificates for one or more users in a given course run.
+
+    Example usage:
+    ./manage.py lms cert_generation -u 123 456 -c course-v1:edX+DemoX+Demo_Course
+    """
+
+    help = """
+    Generate course certificates for one or more users in a given course run.
+    """
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            '-u', '--user',
+            nargs='+',
+            metavar='USER',
+            dest='user',
+            required=True,
+            help='user_id or space-separated list of user_ids for whom to generate course certificates'
+        )
+        parser.add_argument(
+            '-c', '--course-key',
+            metavar='COURSE_KEY',
+            dest='course_key',
+            required=True,
+            help='course run key'
+        )
+
+    def handle(self, *args, **options):
+        if not options.get('user'):
+            raise CommandError('You must specify a list of users')
+
+        course_key = options.get('course_key')
+        if not course_key:
+            raise CommandError('You must specify a course-key')
+
+        # Parse the serialized course key into a CourseKey
+        try:
+            course_key = CourseKey.from_string(course_key)
+        except InvalidKeyError as e:
+            raise CommandError('You must specify a valid course-key') from e
+
+        # Loop over each user, and ask that a cert be generated for them
+        users_str = options['user']
+        for user_id in users_str:
+            user = None
+            try:
+                user = User.objects.get(id=user_id)
+            except User.DoesNotExist:
+                log.warning('User {user} could not be found'.format(user=user_id))
+            if user is not None:
+                log.info(
+                    'Calling generate_certificate_task for {user} : {course}'.format(
+                        user=user.id,
+                        course=course_key
+                    ))
+                generate_certificate_task(user, course_key)
diff --git a/lms/djangoapps/certificates/management/commands/tests/test_cert_allowlist_generation.py b/lms/djangoapps/certificates/management/commands/tests/test_cert_allowlist_generation.py
index a2cd0bdd399..a273bf4a334 100644
--- a/lms/djangoapps/certificates/management/commands/tests/test_cert_allowlist_generation.py
+++ b/lms/djangoapps/certificates/management/commands/tests/test_cert_allowlist_generation.py
@@ -1,5 +1,5 @@
 """
-Tests for the cert_allowlist command
+Tests for the cert_allowlist_generation command
 """
 
 from unittest import mock
@@ -79,6 +79,6 @@ class CertAllowlistGenerationTests(ModuleStoreTestCase):
                      "--u",
                      self.user.id,
                      self.user2.id,
-                     "999999",  # non-existant userid
+                     "999999",  # non-existent userid
                      "--c",
                      self.course_run_key)
diff --git a/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py b/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py
new file mode 100644
index 00000000000..5e1879fa534
--- /dev/null
+++ b/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py
@@ -0,0 +1,76 @@
+"""
+Tests for the cert_generation command
+"""
+
+from unittest import mock
+
+import pytest
+from django.core.management import CommandError, call_command
+from waffle.testutils import override_switch
+
+from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
+from lms.djangoapps.certificates.tests.test_generation_handler import AUTO_GENERATION_SWITCH_NAME, ID_VERIFIED_METHOD
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
+from xmodule.modulestore.tests.factories import CourseFactory
+
+
+@override_switch(AUTO_GENERATION_SWITCH_NAME, active=True)
+@mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=True))
+class CertGenerationTests(ModuleStoreTestCase):
+    """
+    Tests for the cert_generation management command
+    """
+
+    def setUp(self):
+        super().setUp()
+
+        # Create users, a course run, and enrollments
+        self.user = UserFactory()
+        self.course_run = CourseFactory()
+        self.course_run_key = self.course_run.id  # pylint: disable=no-member
+        self.enrollment = CourseEnrollmentFactory(
+            user=self.user,
+            course_id=self.course_run_key,
+            is_active=True,
+            mode="verified",
+        )
+
+        self.user2 = UserFactory()
+        self.enrollment2 = CourseEnrollmentFactory(
+            user=self.user2,
+            course_id=self.course_run_key,
+            is_active=True,
+            mode="verified",
+        )
+
+    def test_command_with_missing_param(self):
+        """
+        Verify command with a missing param
+        """
+        with pytest.raises(CommandError, match="Error: the following arguments are required"):
+            call_command("cert_generation", "--u", self.user.username)
+
+    def test_command_with_invalid_key(self):
+        """
+        Verify command with an invalid course run key
+        """
+        with pytest.raises(CommandError, match="You must specify a valid course-key"):
+            call_command("cert_generation", "--u", self.user.username, "--c", "blah")
+
+    def test_successful_generation(self):
+        """
+        Test generation for 1 user
+        """
+        call_command("cert_generation", "--u", self.user.id, "--c", self.course_run_key)
+
+    def test_successful_generation_multiple_users(self):
+        """
+        Test generation for multiple user
+        """
+        call_command("cert_generation",
+                     "--u",
+                     self.user.id,
+                     self.user2.id,
+                     "999999",  # non-existent userid
+                     "--c",
+                     self.course_run_key)
-- 
GitLab