Adds config models to pass arguments to management command
This would enable simulate_publish management command to fetch its arguments from config model. PROD-548
This commit is contained in:
@@ -8,7 +8,7 @@ from __future__ import absolute_import
|
||||
from config_models.admin import ConfigurationModelAdmin
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import CourseOverview, CourseOverviewImageConfig, CourseOverviewImageSet
|
||||
from .models import CourseOverview, CourseOverviewImageConfig, CourseOverviewImageSet, SimulateCoursePublishConfig
|
||||
|
||||
|
||||
class CourseOverviewAdmin(admin.ModelAdmin):
|
||||
@@ -73,6 +73,11 @@ class CourseOverviewImageSetAdmin(admin.ModelAdmin):
|
||||
fields = ('course_overview_id', 'small_url', 'large_url')
|
||||
|
||||
|
||||
class SimulateCoursePublishConfigAdmin(ConfigurationModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
admin.site.register(CourseOverview, CourseOverviewAdmin)
|
||||
admin.site.register(CourseOverviewImageConfig, CourseOverviewImageConfigAdmin)
|
||||
admin.site.register(CourseOverviewImageSet, CourseOverviewImageSetAdmin)
|
||||
admin.site.register(SimulateCoursePublishConfig, SimulateCoursePublishConfigAdmin)
|
||||
|
||||
@@ -21,10 +21,10 @@ import textwrap
|
||||
import time
|
||||
|
||||
import six
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from openedx.core.djangoapps.content.course_overviews.models import SimulateCoursePublishConfig
|
||||
from lms.djangoapps.ccx.tasks import course_published_handler as ccx_receiver_fn
|
||||
from xmodule.modulestore.django import SignalHandler, modulestore
|
||||
|
||||
@@ -42,10 +42,10 @@ class Command(BaseCommand):
|
||||
$ ./manage.py lms --settings=devstack_docker simulate_publish --delay 10
|
||||
|
||||
# Find all available listeners
|
||||
$ ./manage.py lms --settings=devstack_docker simulate_publish --show_listeners
|
||||
$ ./manage.py lms --settings=devstack_docker simulate_publish --show_receivers
|
||||
|
||||
# Send the publish signal to two courses and two listeners
|
||||
$ ./manage.py lms --settings=devstack_docker simulate_publish --listeners \
|
||||
$ ./manage.py lms --settings=devstack_docker simulate_publish --receivers \
|
||||
openedx.core.djangoapps.content.course_overviews.signals._listen_for_course_publish \
|
||||
openedx.core.djangoapps.bookmarks.signals.trigger_update_xblocks_cache_task \
|
||||
--courses course-v1:edX+DemoX+Demo_Course edX/MODULESTORE_100/2018
|
||||
@@ -157,8 +157,30 @@ class Command(BaseCommand):
|
||||
u"with this flag, so that CCX receivers are omitted."
|
||||
)
|
||||
),
|
||||
parser.add_argument(
|
||||
'--args-from-database',
|
||||
action='store_true',
|
||||
help='Use arguments from the SimulateCoursePublishConfig model instead of the command line.',
|
||||
),
|
||||
|
||||
def get_args_from_database(self):
|
||||
""" Returns an options dictionary from the current SimulateCoursePublishConfig model. """
|
||||
|
||||
config = SimulateCoursePublishConfig.current()
|
||||
if not config.enabled:
|
||||
raise CommandError('SimulateCourseConfigPublish is disabled, but --args-from-database was requested.')
|
||||
|
||||
# We don't need fancy shell-style whitespace/quote handling - none of our arguments are complicated
|
||||
argv = config.arguments.split()
|
||||
|
||||
parser = self.create_parser('manage.py', 'simulate_publish')
|
||||
return parser.parse_args(argv).__dict__ # we want a dictionary, not a non-iterable Namespace object
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
if options['args_from_database']:
|
||||
options = self.get_args_from_database()
|
||||
|
||||
if options['show_receivers']:
|
||||
return self.print_show_receivers()
|
||||
|
||||
|
||||
@@ -5,15 +5,21 @@ from __future__ import absolute_import
|
||||
|
||||
import six
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import CommandError
|
||||
from testfixtures import LogCapture
|
||||
|
||||
import lms.djangoapps.ccx.tasks
|
||||
import openedx.core.djangoapps.content.course_overviews.signals
|
||||
from openedx.core.djangoapps.content.course_overviews.management.commands.simulate_publish import Command, name_from_fn
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview, SimulateCoursePublishConfig
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import SwitchedSignal
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
LOGGER_NAME = 'simulate_publish'
|
||||
|
||||
|
||||
class TestSimulatePublish(SharedModuleStoreTestCase):
|
||||
"""Test simulate_publish, our fake course-publish signal command."""
|
||||
@@ -97,6 +103,7 @@ class TestSimulatePublish(SharedModuleStoreTestCase):
|
||||
delay=0,
|
||||
force_lms=False,
|
||||
skip_ccx=False,
|
||||
args_from_database=False
|
||||
)
|
||||
default_options.update(kwargs)
|
||||
return default_options
|
||||
@@ -147,3 +154,35 @@ class TestSimulatePublish(SharedModuleStoreTestCase):
|
||||
def sample_receiver_2(self, sender, course_key, **kwargs): # pylint: disable=unused-argument
|
||||
"""Custom receiver for testing."""
|
||||
self.received_2.append(course_key)
|
||||
|
||||
def test_args_from_database(self):
|
||||
"""Test management command arguments injected from config model."""
|
||||
# Nothing in the database, should default to disabled
|
||||
with self.assertRaisesRegex(CommandError, 'SimulateCourseConfigPublish is disabled.*'):
|
||||
call_command('simulate_publish', '--args-from-database')
|
||||
|
||||
# Add a config
|
||||
config = SimulateCoursePublishConfig.current()
|
||||
config.arguments = '--delay 20 --dry-run'
|
||||
config.enabled = True
|
||||
config.save()
|
||||
|
||||
with LogCapture(LOGGER_NAME) as log:
|
||||
call_command('simulate_publish')
|
||||
|
||||
log.check_present(
|
||||
(
|
||||
LOGGER_NAME, 'INFO',
|
||||
u"simulate_publish starting, dry-run={}, delay={} seconds".format('False', '0')
|
||||
),
|
||||
)
|
||||
|
||||
with LogCapture(LOGGER_NAME) as log:
|
||||
call_command('simulate_publish', '--args-from-database')
|
||||
|
||||
log.check_present(
|
||||
(
|
||||
LOGGER_NAME, 'INFO',
|
||||
u"simulate_publish starting, dry-run={}, delay={} seconds".format('True', '20')
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.23 on 2019-09-16 10:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
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),
|
||||
('course_overviews', '0015_historicalcourseoverview'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SimulateCoursePublishConfig',
|
||||
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=b'', help_text=b'Useful for manually running a Jenkins job. Specify like "--delay 10 --listeners A B C --courses X Y Z".')),
|
||||
('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': 'simulate_publish argument',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -896,3 +896,25 @@ class CourseOverviewImageConfig(ConfigurationModel):
|
||||
return u"CourseOverviewImageConfig(enabled={}, small={}, large={})".format(
|
||||
self.enabled, self.small, self.large
|
||||
)
|
||||
|
||||
|
||||
class SimulateCoursePublishConfig(ConfigurationModel):
|
||||
"""
|
||||
Manages configuration for a run of the simulate_publish management command.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
app_label = 'course_overviews'
|
||||
verbose_name = 'simulate_publish argument'
|
||||
|
||||
arguments = models.TextField(
|
||||
blank=True,
|
||||
help_text='Useful for manually running a Jenkins job. Specify like "--delay 10 --receivers A B C \
|
||||
--courses X Y Z".',
|
||||
default='',
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return six.text_type(self.arguments)
|
||||
|
||||
Reference in New Issue
Block a user