diff --git a/lms/djangoapps/certificates/admin.py b/lms/djangoapps/certificates/admin.py index 69fb1826e0..cae857bce7 100644 --- a/lms/djangoapps/certificates/admin.py +++ b/lms/djangoapps/certificates/admin.py @@ -15,6 +15,7 @@ from organizations.api import get_organizations from lms.djangoapps.certificates.models import ( AllowListGenerationConfiguration, CertificateGenerationConfiguration, + CertificateGenerationCommandConfiguration, CertificateGenerationCourseSetting, CertificateHtmlViewConfiguration, CertificateTemplate, @@ -95,6 +96,11 @@ class CertificateGenerationCourseSettingAdmin(admin.ModelAdmin): class AllowListGenerationConfigurationAdmin(ConfigurationModelAdmin): pass + +@admin.register(CertificateGenerationCommandConfiguration) +class CertificateGenerationCommandConfigurationAdmin(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_generation.py b/lms/djangoapps/certificates/management/commands/cert_generation.py index 1d0f8ecd3e..3b9edf56e4 100644 --- a/lms/djangoapps/certificates/management/commands/cert_generation.py +++ b/lms/djangoapps/certificates/management/commands/cert_generation.py @@ -3,6 +3,7 @@ Management command to generate course certificates for one or more users in a gi """ 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_certificate_task +from lms.djangoapps.certificates.models import CertificateGenerationCommandConfiguration User = get_user_model() log = logging.getLogger(__name__) @@ -33,18 +35,40 @@ class Command(BaseCommand): 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' ) + parser.add_argument( + '--args-from-database', + action='store_true', + help='Use arguments from the CertificateGenerationCommandConfiguration model instead of the command line' + ) + + def get_args_from_database(self): + """ + Returns an options dictionary from the current CertificateGenerationCommandConfiguration model. + """ + config = CertificateGenerationCommandConfiguration.current() + if not config.enabled: + raise CommandError( + "CertificateGenerationCommandConfiguration is disabled, but --args-from-database was requested" + ) + + args = shlex.split(config.arguments) + parser = self.create_parser("manage.py", "cert_generation") + + return vars(parser.parse_args(args)) def handle(self, *args, **options): + # database args will override cmd line args + if options['args_from_database']: + options = self.get_args_from_database() + if not options.get('user'): raise CommandError('You must specify a list of users') diff --git a/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py b/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py index 5e1879fa53..9e09cbd585 100644 --- a/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py +++ b/lms/djangoapps/certificates/management/commands/tests/test_cert_generation.py @@ -43,13 +43,20 @@ class CertGenerationTests(ModuleStoreTestCase): mode="verified", ) - def test_command_with_missing_param(self): + def test_command_with_missing_param_course_key(self): """ - Verify command with a missing param + Verify command with a missing param -- course key. """ - with pytest.raises(CommandError, match="Error: the following arguments are required"): + with pytest.raises(CommandError, match="You must specify a course-key"): call_command("cert_generation", "--u", self.user.username) + def test_command_with_missing_param_users(self): + """ + Verify command with a missing param -- users. + """ + with pytest.raises(CommandError, match="You must specify a list of users"): + call_command("cert_generation", "--c", "blah") + def test_command_with_invalid_key(self): """ Verify command with an invalid course run key diff --git a/lms/djangoapps/certificates/migrations/0023_certificategenerationcommandconfiguration.py b/lms/djangoapps/certificates/migrations/0023_certificategenerationcommandconfiguration.py new file mode 100644 index 0000000000..c1ac0eb55b --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0023_certificategenerationcommandconfiguration.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.19 on 2021-03-30 19:37 + +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', '0022_add_unique_constraints_to_certificatewhitelist_model'), + ] + + operations = [ + migrations.CreateModel( + name='CertificateGenerationCommandConfiguration', + 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 for the 'cert_generation' management command. Specify like '-u -c '")), + ('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_generation argument', + }, + ), + ] diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index 52405da668..77518d99f3 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -1245,3 +1245,26 @@ class AllowListGenerationConfiguration(ConfigurationModel): def __str__(self): return str(self.arguments) + + +class CertificateGenerationCommandConfiguration(ConfigurationModel): + """ + Manages configuration for a run of the cert_generation management command. + + .. no_pii: + """ + + class Meta(object): + app_label = "certificates" + verbose_name = "cert_generation argument" + + arguments = models.TextField( + blank=True, + help_text=( + "Arguments for the 'cert_generation' management command. Specify like '-u -c '" + ), + default="", + ) + + def __str__(self): + return str(self.arguments)