diff --git a/lms/djangoapps/certificates/admin.py b/lms/djangoapps/certificates/admin.py index 2a4bad1a1b3df0c0d921a0f60a9933fedabd46fc..669b5125fde714e6cc91b520e5d1b5c678e3913b 100644 --- a/lms/djangoapps/certificates/admin.py +++ b/lms/djangoapps/certificates/admin.py @@ -13,6 +13,7 @@ from django.utils.safestring import mark_safe from organizations.api import get_organizations from lms.djangoapps.certificates.models import ( + AllowListGenerationConfiguration, CertificateGenerationConfiguration, CertificateGenerationCourseSetting, CertificateHtmlViewConfiguration, @@ -90,6 +91,10 @@ class CertificateGenerationCourseSettingAdmin(admin.ModelAdmin): show_full_result_count = False +@admin.register(AllowListGenerationConfiguration) +class AllowListGenerationConfigurationAdmin(ConfigurationModelAdmin): + pass + admin.site.register(CertificateGenerationConfiguration) admin.site.register(CertificateGenerationCourseSetting, CertificateGenerationCourseSettingAdmin) admin.site.register(CertificateHtmlViewConfiguration, ConfigurationModelAdmin) diff --git a/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py b/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py index 1009d776b01542c083b64d6c7d2449178bc55af4..5e184d9d039a267a5bc49db87adbe5905b1f8025 100644 --- a/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py +++ b/lms/djangoapps/certificates/management/commands/cert_allowlist_generation.py @@ -3,6 +3,7 @@ Management command to generate allowlist certificates for one or more users in a """ import logging +import shlex from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand, CommandError @@ -10,6 +11,7 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from lms.djangoapps.certificates.generation_handler import generate_allowlist_certificate_task +from lms.djangoapps.certificates.models import AllowListGenerationConfiguration User = get_user_model() log = logging.getLogger(__name__) @@ -33,23 +35,44 @@ class Command(BaseCommand): nargs="+", metavar='USER', dest='user', - required=True, help='user or space-separated list of users for whom to generate allowlist certificates' ) parser.add_argument( '-c', '--course-key', metavar='COURSE_KEY', dest='course_key', - required=True, help="course run key" ) + parser.add_argument( + '--args-from-database', + action='store_true', + help='Use arguments from the AllowListGenerationConfiguration model instead of the command line.', + ) + + def get_args_from_database(self): + """ Returns an options dictionary from the current AllowListGenerationConfiguration model. """ + config = AllowListGenerationConfiguration.current() + if not config.enabled: + raise CommandError("AllowListGenerationConfiguration is disabled, but --args-from-database was requested.") + + argv = shlex.split(config.arguments) + parser = self.create_parser("manage.py", "cert_allowlist_generation") + return vars(parser.parse_args(argv)) # we want a dictionary, not a non-iterable Namespace object def handle(self, *args, **options): - # Parse the serialized course key into a CourseKey + # database args will override cmd line args + if options['args_from_database']: + options = self.get_args_from_database() + + # Since we're optionally using database args we can't simply make users required in the arguments + if not options["user"]: + raise CommandError("You must specify a list of users") + course_key = options['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: 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 1c2151b1eae4b7fc22f8d00b296fd2120fbd4cb0..4228f0815695c4105c9fd2778747bcf639566e72 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 @@ -57,7 +57,7 @@ class CertAllowlistGenerationTests(ModuleStoreTestCase): """ Verify command with a missing param """ - with pytest.raises(CommandError, match="Error: the following arguments are required: -c/--course-key"): + with pytest.raises(CommandError, match="You must specify a course-key"): call_command("cert_allowlist_generation", "--u", self.user.username) def test_command_with_invalid_key(self): diff --git a/lms/djangoapps/certificates/migrations/0019_allowlistgenerationconfiguration.py b/lms/djangoapps/certificates/migrations/0019_allowlistgenerationconfiguration.py new file mode 100644 index 0000000000000000000000000000000000000000..8d063e6289c8589cfaa6dc91f4a82a1a36ac2686 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0019_allowlistgenerationconfiguration.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.18 on 2021-02-16 20:16 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('certificates', '0018_historicalcertificateinvalidation'), + ] + + operations = [ + migrations.CreateModel( + name='AllowListGenerationConfiguration', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')), + ('enabled', models.BooleanField(default=False, verbose_name='Enabled')), + ('arguments', models.TextField(blank=True, default='', help_text='Arguments to be passted to cert_allowlist_generation management command. Specify like `-u edx verified -c course-v1:edX+DemoX+Demo_Course`')), + ('changed_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Changed by')), + ], + options={ + 'verbose_name': 'cert_allowlist_generation argument', + }, + ), + ] diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index 6b3dbbc3ec4e55c6a6a15212286e3faf32e5fd9e..fb91237e1fc54dd2eda5246b225ce5dbcf65119a 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -1259,3 +1259,27 @@ def create_course_group_badge(sender, user, course_key, status, **kwargs): # py Standard signal hook to create badges when a user has completed a prespecified set of courses. """ course_group_check(user, course_key) + + +class AllowListGenerationConfiguration(ConfigurationModel): + """ + Manages configuration for a run of the cert_allowlist_generation management command. + + .. no_pii: + """ + + class Meta(object): + app_label = 'certificates' + verbose_name = 'cert_allowlist_generation argument' + + arguments = models.TextField( + blank=True, + help_text=( + "Arguments to be passted to cert_allowlist_generation management command. " + + "Specify like `-u edx verified -c course-v1:edX+DemoX+Demo_Course`" + ), + default='', + ) + + def __str__(self): + return str(self.arguments)