From 75d8448ef5de39869aa057b0c15a3a999da23b5c Mon Sep 17 00:00:00 2001 From: Ahtisham Shahid Date: Wed, 9 Feb 2022 15:43:22 +0500 Subject: [PATCH] refactor: Moved ProgramDiscussion/Live Configuration Models to program's app (#29871) refactor: Moved ProgramDiscussion/Live Configuration Models to program's app --- lms/djangoapps/learner_dashboard/programs.py | 7 +- .../learner_dashboard/tests/test_views.py | 2 +- openedx/core/djangoapps/discussions/admin.py | 54 +---------- .../migrations/0010_auto_20220203_2134.py | 41 +++++++++ openedx/core/djangoapps/discussions/models.py | 52 ----------- openedx/core/djangoapps/programs/admin.py | 55 ++++++++++- .../{discussions => programs}/forms.py | 0 ...veconfiguration_programdiscussionsconfi.py | 92 +++++++++++++++++++ openedx/core/djangoapps/programs/models.py | 55 +++++++++++ 9 files changed, 249 insertions(+), 109 deletions(-) create mode 100644 openedx/core/djangoapps/discussions/migrations/0010_auto_20220203_2134.py rename openedx/core/djangoapps/{discussions => programs}/forms.py (100%) create mode 100644 openedx/core/djangoapps/programs/migrations/0015_historicalprogramdiscussionsconfiguration_historicalprogramliveconfiguration_programdiscussionsconfi.py diff --git a/lms/djangoapps/learner_dashboard/programs.py b/lms/djangoapps/learner_dashboard/programs.py index d415843fe7..a7b665bc7c 100644 --- a/lms/djangoapps/learner_dashboard/programs.py +++ b/lms/djangoapps/learner_dashboard/programs.py @@ -23,9 +23,12 @@ from lms.djangoapps.learner_dashboard.utils import FAKE_COURSE_KEY, program_tab_ from openedx.core.djangoapps.catalog.constants import PathwayType from openedx.core.djangoapps.catalog.utils import get_pathways, get_programs from openedx.core.djangoapps.credentials.utils import get_credentials_records_url -from openedx.core.djangoapps.discussions.models import ProgramDiscussionsConfiguration, ProgramLiveConfiguration from openedx.core.djangoapps.plugin_api.views import EdxFragmentView -from openedx.core.djangoapps.programs.models import ProgramsApiConfig +from openedx.core.djangoapps.programs.models import ( + ProgramDiscussionsConfiguration, + ProgramLiveConfiguration, + ProgramsApiConfig +) from openedx.core.djangoapps.programs.utils import ( ProgramDataExtender, ProgramProgressMeter, diff --git a/lms/djangoapps/learner_dashboard/tests/test_views.py b/lms/djangoapps/learner_dashboard/tests/test_views.py index eeeebd569f..d38b2876f5 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_views.py +++ b/lms/djangoapps/learner_dashboard/tests/test_views.py @@ -17,7 +17,7 @@ from lms.djangoapps.learner_dashboard.config.waffle import ENABLE_PROGRAM_TAB_VI from lms.djangoapps.program_enrollments.rest_api.v1.tests.test_views import ProgramCacheMixin from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory -from openedx.core.djangoapps.discussions.models import ProgramDiscussionsConfiguration +from openedx.core.djangoapps.programs.models import ProgramDiscussionsConfiguration @ddt.ddt diff --git a/openedx/core/djangoapps/discussions/admin.py b/openedx/core/djangoapps/discussions/admin.py index 8e35be116a..eb61942abf 100644 --- a/openedx/core/djangoapps/discussions/admin.py +++ b/openedx/core/djangoapps/discussions/admin.py @@ -7,8 +7,7 @@ from simple_history.admin import SimpleHistoryAdmin from openedx.core.djangoapps.config_model_utils.admin import StackedConfigModelAdmin -from .forms import ProgramDiscussionsConfigurationForm, ProgramLiveConfigurationForm -from .models import DiscussionsConfiguration, ProgramDiscussionsConfiguration, ProgramLiveConfiguration +from .models import DiscussionsConfiguration from .models import ProviderFilter @@ -28,30 +27,6 @@ class DiscussionsConfigurationAdmin(SimpleHistoryAdmin): ) -class ProgramDiscussionsConfigurationAdmin(SimpleHistoryAdmin): - """ - Customize the admin interface for the program discussions configuration - """ - form = ProgramDiscussionsConfigurationForm - - fieldsets = ( - (None, { - 'fields': ('program_uuid', 'enabled', 'lti_configuration', 'pii_share_username', 'pii_share_email', - 'provider_type'), - }), - ) - - search_fields = ( - 'program_uuid', - 'enabled', - 'provider_type', - ) - list_filter = ( - 'enabled', - 'provider_type', - ) - - class AllowListFilter(SimpleListFilter): """ Customize the admin interface for the AllowList @@ -111,32 +86,5 @@ class ProviderFilterAdmin(StackedConfigModelAdmin): DenyListFilter, ) - -class ProgramLiveConfigurationAdmin(SimpleHistoryAdmin): - """ - Customize the admin interface for the program live configuration - """ - form = ProgramLiveConfigurationForm - - fieldsets = ( - (None, { - 'fields': ('program_uuid', 'enabled', 'lti_configuration', 'pii_share_username', 'pii_share_email', - 'provider_type'), - }), - ) - - search_fields = ( - 'program_uuid', - 'enabled', - 'provider_type', - ) - list_filter = ( - 'enabled', - 'provider_type', - ) - - admin.site.register(DiscussionsConfiguration, DiscussionsConfigurationAdmin) -admin.site.register(ProgramDiscussionsConfiguration, ProgramDiscussionsConfigurationAdmin) -admin.site.register(ProgramLiveConfiguration, ProgramLiveConfigurationAdmin) admin.site.register(ProviderFilter, ProviderFilterAdmin) diff --git a/openedx/core/djangoapps/discussions/migrations/0010_auto_20220203_2134.py b/openedx/core/djangoapps/discussions/migrations/0010_auto_20220203_2134.py new file mode 100644 index 0000000000..87d4b713a7 --- /dev/null +++ b/openedx/core/djangoapps/discussions/migrations/0010_auto_20220203_2134.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.11 on 2022-02-03 21:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('discussions', '0009_discussiontopiclink_ordering'), + ] + + operations = [ + migrations.RemoveField( + model_name='historicalprogramliveconfiguration', + name='history_user', + ), + migrations.RemoveField( + model_name='historicalprogramliveconfiguration', + name='lti_configuration', + ), + migrations.RemoveField( + model_name='programdiscussionsconfiguration', + name='lti_configuration', + ), + migrations.RemoveField( + model_name='programliveconfiguration', + name='lti_configuration', + ), + migrations.DeleteModel( + name='HistoricalProgramDiscussionsConfiguration', + ), + migrations.DeleteModel( + name='HistoricalProgramLiveConfiguration', + ), + migrations.DeleteModel( + name='ProgramDiscussionsConfiguration', + ), + migrations.DeleteModel( + name='ProgramLiveConfiguration', + ), + ] diff --git a/openedx/core/djangoapps/discussions/models.py b/openedx/core/djangoapps/discussions/models.py index 7774a7f86a..25571b045b 100644 --- a/openedx/core/djangoapps/discussions/models.py +++ b/openedx/core/djangoapps/discussions/models.py @@ -522,50 +522,6 @@ class DiscussionsConfiguration(TimeStampedModel): ) -class AbstractProgramLTIConfiguration(TimeStampedModel): - """ - Associates a program with a LTI provider and configuration - """ - class Meta: - abstract = True - - program_uuid = models.CharField( - primary_key=True, - db_index=True, - max_length=50, - verbose_name=_("Program UUID"), - ) - enabled = models.BooleanField( - default=True, - help_text=_("If disabled, the LTI in the associated program will be disabled.") - ) - lti_configuration = models.ForeignKey( - LtiConfiguration, - on_delete=models.SET_NULL, - blank=True, - null=True, - help_text=_("The LTI configuration data for this program/provider."), - ) - provider_type = models.CharField( - blank=False, - max_length=50, - verbose_name=_("LTI provider"), - help_text=_("The LTI provider's id"), - ) - - def __str__(self): - return f"Configuration(uuid='{self.program_uuid}', provider='{self.provider_type}', enabled={self.enabled})" - - @classmethod - def get(cls, program_uuid): - """ - Lookup a program discussion configuration by program uuid. - """ - return cls.objects.filter( - program_uuid=program_uuid - ).first() - - class DiscussionTopicLink(models.Model): """ A model linking discussion topics ids to the part of a course they are linked to. @@ -623,11 +579,3 @@ class DiscussionTopicLink(models.Model): f'enabled_in_context={self.enabled_in_context}' f')' ) - - -class ProgramLiveConfiguration(AbstractProgramLTIConfiguration): - history = HistoricalRecords() - - -class ProgramDiscussionsConfiguration(AbstractProgramLTIConfiguration): - history = HistoricalRecords() diff --git a/openedx/core/djangoapps/programs/admin.py b/openedx/core/djangoapps/programs/admin.py index af9cb0597d..6f0949b424 100644 --- a/openedx/core/djangoapps/programs/admin.py +++ b/openedx/core/djangoapps/programs/admin.py @@ -5,12 +5,65 @@ django admin pages for program support models from config_models.admin import ConfigurationModelAdmin from django.contrib import admin +from simple_history.admin import SimpleHistoryAdmin -from openedx.core.djangoapps.programs.models import ProgramsApiConfig +from openedx.core.djangoapps.programs.forms import ProgramDiscussionsConfigurationForm, ProgramLiveConfigurationForm +from openedx.core.djangoapps.programs.models import ProgramsApiConfig, ProgramDiscussionsConfiguration, \ + ProgramLiveConfiguration class ProgramsApiConfigAdmin(ConfigurationModelAdmin): pass +class ProgramDiscussionsConfigurationAdmin(SimpleHistoryAdmin): + """ + Customize the admin interface for the program discussions configuration + """ + form = ProgramDiscussionsConfigurationForm + + fieldsets = ( + (None, { + 'fields': ('program_uuid', 'enabled', 'lti_configuration', 'pii_share_username', 'pii_share_email', + 'provider_type'), + }), + ) + + search_fields = ( + 'program_uuid', + 'enabled', + 'provider_type', + ) + list_filter = ( + 'enabled', + 'provider_type', + ) + + +class ProgramLiveConfigurationAdmin(SimpleHistoryAdmin): + """ + Customize the admin interface for the program live configuration + """ + form = ProgramLiveConfigurationForm + + fieldsets = ( + (None, { + 'fields': ('program_uuid', 'enabled', 'lti_configuration', 'pii_share_username', 'pii_share_email', + 'provider_type'), + }), + ) + + search_fields = ( + 'program_uuid', + 'enabled', + 'provider_type', + ) + list_filter = ( + 'enabled', + 'provider_type', + ) + + admin.site.register(ProgramsApiConfig, ProgramsApiConfigAdmin) +admin.site.register(ProgramDiscussionsConfiguration, ProgramDiscussionsConfigurationAdmin) +admin.site.register(ProgramLiveConfiguration, ProgramLiveConfigurationAdmin) diff --git a/openedx/core/djangoapps/discussions/forms.py b/openedx/core/djangoapps/programs/forms.py similarity index 100% rename from openedx/core/djangoapps/discussions/forms.py rename to openedx/core/djangoapps/programs/forms.py diff --git a/openedx/core/djangoapps/programs/migrations/0015_historicalprogramdiscussionsconfiguration_historicalprogramliveconfiguration_programdiscussionsconfi.py b/openedx/core/djangoapps/programs/migrations/0015_historicalprogramdiscussionsconfiguration_historicalprogramliveconfiguration_programdiscussionsconfi.py new file mode 100644 index 0000000000..59ad6186f9 --- /dev/null +++ b/openedx/core/djangoapps/programs/migrations/0015_historicalprogramdiscussionsconfiguration_historicalprogramliveconfiguration_programdiscussionsconfi.py @@ -0,0 +1,92 @@ +# Generated by Django 3.2.11 on 2022-02-03 21:34 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lti_consumer', '0013_auto_20210712_1352'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('programs', '0014_delete_customprogramsconfig'), + ] + + operations = [ + migrations.CreateModel( + name='ProgramLiveConfiguration', + fields=[ + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('program_uuid', models.CharField(db_index=True, max_length=50, primary_key=True, serialize=False, verbose_name='Program UUID')), + ('enabled', models.BooleanField(default=True, help_text='If disabled, the LTI in the associated program will be disabled.')), + ('provider_type', models.CharField(help_text="The LTI provider's id", max_length=50, verbose_name='LTI provider')), + ('lti_configuration', models.ForeignKey(blank=True, help_text='The LTI configuration data for this program/provider.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='lti_consumer.lticonfiguration')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ProgramDiscussionsConfiguration', + fields=[ + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('program_uuid', models.CharField(db_index=True, max_length=50, primary_key=True, serialize=False, verbose_name='Program UUID')), + ('enabled', models.BooleanField(default=True, help_text='If disabled, the LTI in the associated program will be disabled.')), + ('provider_type', models.CharField(help_text="The LTI provider's id", max_length=50, verbose_name='LTI provider')), + ('lti_configuration', models.ForeignKey(blank=True, help_text='The LTI configuration data for this program/provider.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='lti_consumer.lticonfiguration')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HistoricalProgramLiveConfiguration', + fields=[ + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('program_uuid', models.CharField(db_index=True, max_length=50, verbose_name='Program UUID')), + ('enabled', models.BooleanField(default=True, help_text='If disabled, the LTI in the associated program will be disabled.')), + ('provider_type', models.CharField(help_text="The LTI provider's id", max_length=50, verbose_name='LTI provider')), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('lti_configuration', models.ForeignKey(blank=True, db_constraint=False, help_text='The LTI configuration data for this program/provider.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='lti_consumer.lticonfiguration')), + ], + options={ + 'verbose_name': 'historical program live configuration', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalProgramDiscussionsConfiguration', + fields=[ + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('program_uuid', models.CharField(db_index=True, max_length=50, verbose_name='Program UUID')), + ('enabled', models.BooleanField(default=True, help_text='If disabled, the LTI in the associated program will be disabled.')), + ('provider_type', models.CharField(help_text="The LTI provider's id", max_length=50, verbose_name='LTI provider')), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('lti_configuration', models.ForeignKey(blank=True, db_constraint=False, help_text='The LTI configuration data for this program/provider.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='lti_consumer.lticonfiguration')), + ], + options={ + 'verbose_name': 'historical program discussions configuration', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + ] diff --git a/openedx/core/djangoapps/programs/models.py b/openedx/core/djangoapps/programs/models.py index 81345829eb..302ad553f2 100644 --- a/openedx/core/djangoapps/programs/models.py +++ b/openedx/core/djangoapps/programs/models.py @@ -2,6 +2,9 @@ from config_models.models import ConfigurationModel from django.db import models from django.utils.translation import gettext_lazy as _ +from simple_history.models import HistoricalRecords +from lti_consumer.models import LtiConfiguration +from model_utils.models import TimeStampedModel class ProgramsApiConfig(ConfigurationModel): @@ -22,3 +25,55 @@ class ProgramsApiConfig(ConfigurationModel): 'Path used to construct URLs to programs marketing pages (e.g., "/foo").' ) ) + + +class AbstractProgramLTIConfiguration(TimeStampedModel): + """ + Associates a program with a LTI provider and configuration + """ + class Meta: + abstract = True + + program_uuid = models.CharField( + primary_key=True, + db_index=True, + max_length=50, + verbose_name=_("Program UUID"), + ) + enabled = models.BooleanField( + default=True, + help_text=_("If disabled, the LTI in the associated program will be disabled.") + ) + lti_configuration = models.ForeignKey( + LtiConfiguration, + on_delete=models.SET_NULL, + blank=True, + null=True, + help_text=_("The LTI configuration data for this program/provider."), + ) + provider_type = models.CharField( + blank=False, + max_length=50, + verbose_name=_("LTI provider"), + help_text=_("The LTI provider's id"), + ) + + def __str__(self): + return f"Configuration(uuid='{self.program_uuid}', provider='{self.provider_type}', enabled={self.enabled})" + + @classmethod + def get(cls, program_uuid): + """ + Lookup a program discussion configuration by program uuid. + """ + return cls.objects.filter( + program_uuid=program_uuid + ).first() + + +class ProgramLiveConfiguration(AbstractProgramLTIConfiguration): + history = HistoricalRecords() + + +class ProgramDiscussionsConfiguration(AbstractProgramLTIConfiguration): + history = HistoricalRecords()