From 20ef6f337105522cabae81890ba909e2b2ea7efe Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Tue, 1 Nov 2022 14:53:54 -0400 Subject: [PATCH 1/9] Update admin Add the fields for modifying expiration_datetime_is_explicit --- common/djangoapps/course_modes/admin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 1b3ad262e47f..4d69348f3db6 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -161,6 +161,7 @@ def save(self, commit=True): course = self.cleaned_data.get("course") verification_deadline = self.cleaned_data.get("verification_deadline") mode_slug = self.cleaned_data.get("mode_slug") + upgrade_deadline = self.cleaned_data.get("_expiration_datetime") # Since the verification deadline is stored in a separate model, # we need to handle saving this ourselves. @@ -171,6 +172,7 @@ def save(self, commit=True): course.id, verification_deadline ) + self.expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self._expiration_datetime else self.expiration_datetime_is_explicit return super().save(commit=commit) @@ -189,6 +191,7 @@ class CourseModeAdmin(admin.ModelAdmin): 'min_price', 'currency', '_expiration_datetime', + 'expiration_datetime_is_explicit', 'verification_deadline', 'sku', 'bulk_sku' From 5a06a2da174c46f1a5c6d7da1083bbaeb1205864 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 09:33:16 -0400 Subject: [PATCH 2/9] update --- common/djangoapps/course_modes/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 4d69348f3db6..2c29829ec0b7 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -172,7 +172,7 @@ def save(self, commit=True): course.id, verification_deadline ) - self.expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self._expiration_datetime else self.expiration_datetime_is_explicit + self.expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self.instance._expiration_datetime else self.expiration_datetime_is_explicit return super().save(commit=commit) From 62a72abc1bc150f449b69824a6e6b74e5eedb8ae Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 11:01:23 -0400 Subject: [PATCH 3/9] up --- common/djangoapps/course_modes/admin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 2c29829ec0b7..46f2c9335154 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -115,6 +115,9 @@ def clean_verification_deadline(self): """ if self.cleaned_data.get("verification_deadline"): return self.cleaned_data.get("verification_deadline").replace(tzinfo=UTC) + + def clean_expiration_datetime_is_explicit(self): + return True if self.cleaned_data.get("_expiration_datetime") is not None and self.cleaned_data.get("_expiration_datetime") != self.instance._expiration_datetime else self.expiration_datetime_is_explicit def clean(self): """ @@ -172,7 +175,7 @@ def save(self, commit=True): course.id, verification_deadline ) - self.expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self.instance._expiration_datetime else self.expiration_datetime_is_explicit + expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self.instance._expiration_datetime else self.expiration_datetime_is_explicit return super().save(commit=commit) From 3557e8e7c9391667ceb403013314fcf91621aa54 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 11:08:14 -0400 Subject: [PATCH 4/9] remove --- common/djangoapps/course_modes/admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 46f2c9335154..0f2218d54af5 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -175,7 +175,6 @@ def save(self, commit=True): course.id, verification_deadline ) - expiration_datetime_is_explicit = True if upgrade_deadline is not None and upgrade_deadline != self.instance._expiration_datetime else self.expiration_datetime_is_explicit return super().save(commit=commit) From 2d06d95c1f540cfbe3eead8690672ffa0a9f3fd5 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 11:22:16 -0400 Subject: [PATCH 5/9] remove clean --- common/djangoapps/course_modes/admin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 0f2218d54af5..8c27415792f2 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -115,9 +115,6 @@ def clean_verification_deadline(self): """ if self.cleaned_data.get("verification_deadline"): return self.cleaned_data.get("verification_deadline").replace(tzinfo=UTC) - - def clean_expiration_datetime_is_explicit(self): - return True if self.cleaned_data.get("_expiration_datetime") is not None and self.cleaned_data.get("_expiration_datetime") != self.instance._expiration_datetime else self.expiration_datetime_is_explicit def clean(self): """ From 6585259158a87e4449349ebcc36f5793bead9e05 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 13:13:56 -0400 Subject: [PATCH 6/9] Added validation, description, test --- common/djangoapps/course_modes/admin.py | 7 ++++++- common/djangoapps/course_modes/models.py | 9 ++++++++- common/djangoapps/course_modes/tests/test_admin.py | 9 +++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 8c27415792f2..020a08327ae3 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -125,6 +125,12 @@ def clean(self): mode_slug = cleaned_data.get("mode_slug") upgrade_deadline = cleaned_data.get("_expiration_datetime") verification_deadline = cleaned_data.get("verification_deadline") + expiration_datetime_is_explicit = cleaned_data.get("expiration_datetime_is_explicit") + + if expiration_datetime_is_explicit and upgrade_deadline is None: + raise forms.ValidationError( + "An upgrade deadline must be specified when setting Expiration datetime is explicit to True." + ) # Allow upgrade deadlines ONLY for the "verified" mode # This avoids a nasty error condition in which the upgrade deadline is set @@ -161,7 +167,6 @@ def save(self, commit=True): course = self.cleaned_data.get("course") verification_deadline = self.cleaned_data.get("verification_deadline") mode_slug = self.cleaned_data.get("mode_slug") - upgrade_deadline = self.cleaned_data.get("_expiration_datetime") # Since the verification deadline is stored in a separate model, # we need to handle saving this ourselves. diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index 0b015a133c9e..5d74a465d528 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -87,7 +87,14 @@ class CourseMode(models.Model): # The system prefers to set this automatically based on default settings. But # if the field is set manually we want a way to indicate that so we don't # overwrite the manual setting of the field. - expiration_datetime_is_explicit = models.BooleanField(default=False) + expiration_datetime_is_explicit = models.BooleanField( + default=False, + verbose_name=_("Upgrade Deadline Explicitly Defined"), + help_text=_( + "OPTIONAL: Set to True if the upgrade deadline date is explicitly defined. " + "Set to False if there is no upgrade deadline or to use the default upgrade deadline." + ) + ) # DEPRECATED: the `expiration_date` field has been replaced by `expiration_datetime` expiration_date = models.DateField(default=None, null=True, blank=True) diff --git a/common/djangoapps/course_modes/tests/test_admin.py b/common/djangoapps/course_modes/tests/test_admin.py index b1fe8b7ef7d9..f03da788ff20 100644 --- a/common/djangoapps/course_modes/tests/test_admin.py +++ b/common/djangoapps/course_modes/tests/test_admin.py @@ -164,6 +164,14 @@ def test_validate_upgrade_deadline_only_for_verified(self, course_mode): 'For other modes, please set the enrollment end date in Studio.' )) + def test_validate_expiration_datetime_is_explicit_only_with_upgrade_deadline(self): + # Only allow the expiration_datetime_is_explicit to be True if the upgrade_deadline is + # defined with a date, otherwise cause a validation error. + form = self._admin_form("verified", upgrade_deadline=None) + self._assert_form_has_error(form, ( + "An upgrade deadline must be specified when setting Expiration datetime is explicit to True." + )) + @ddt.data("honor", "no-id-professional", "credit") def test_validate_verification_deadline_only_for_verified(self, course_mode): # Only the verified mode should have a verification deadline set. @@ -204,6 +212,7 @@ def _admin_form(self, mode, upgrade_deadline=None): "mode_slug": mode, "mode_display_name": mode, "_expiration_datetime": upgrade_deadline, + "expiration_datetime_is_explicit": True, "currency": "usd", "min_price": 10, }, instance=course_mode) From 035140bbdfd32b4891b2666555ce53ec480bf75b Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Wed, 2 Nov 2022 13:18:57 -0400 Subject: [PATCH 7/9] Fix test --- common/djangoapps/course_modes/tests/test_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/course_modes/tests/test_admin.py b/common/djangoapps/course_modes/tests/test_admin.py index f03da788ff20..e4d87536b5f7 100644 --- a/common/djangoapps/course_modes/tests/test_admin.py +++ b/common/djangoapps/course_modes/tests/test_admin.py @@ -167,7 +167,7 @@ def test_validate_upgrade_deadline_only_for_verified(self, course_mode): def test_validate_expiration_datetime_is_explicit_only_with_upgrade_deadline(self): # Only allow the expiration_datetime_is_explicit to be True if the upgrade_deadline is # defined with a date, otherwise cause a validation error. - form = self._admin_form("verified", upgrade_deadline=None) + form = self._admin_form("verified", expiration_datetime_is_explicit=True) self._assert_form_has_error(form, ( "An upgrade deadline must be specified when setting Expiration datetime is explicit to True." )) @@ -201,7 +201,7 @@ def _configure(self, mode, upgrade_deadline=None, verification_deadline=None): return CourseModeForm(instance=course_mode) - def _admin_form(self, mode, upgrade_deadline=None): + def _admin_form(self, mode, upgrade_deadline=None, expiration_datetime_is_explicit=False): """Load the course mode admin form. """ course_mode = CourseModeFactory.create( course_id=self.course.id, @@ -212,7 +212,7 @@ def _admin_form(self, mode, upgrade_deadline=None): "mode_slug": mode, "mode_display_name": mode, "_expiration_datetime": upgrade_deadline, - "expiration_datetime_is_explicit": True, + "expiration_datetime_is_explicit": expiration_datetime_is_explicit, "currency": "usd", "min_price": 10, }, instance=course_mode) From 26bb06865493fc177ee33569d13cf8ae2e99c3f7 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Mon, 7 Nov 2022 07:52:16 -0500 Subject: [PATCH 8/9] Remove whitespace Fix linting error --- common/djangoapps/course_modes/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 020a08327ae3..1895fa2f2d46 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -126,7 +126,7 @@ def clean(self): upgrade_deadline = cleaned_data.get("_expiration_datetime") verification_deadline = cleaned_data.get("verification_deadline") expiration_datetime_is_explicit = cleaned_data.get("expiration_datetime_is_explicit") - + if expiration_datetime_is_explicit and upgrade_deadline is None: raise forms.ValidationError( "An upgrade deadline must be specified when setting Expiration datetime is explicit to True." From 36965cfe1eaa5c905a3682797546b0fd574fdda3 Mon Sep 17 00:00:00 2001 From: Collin Preston Date: Mon, 7 Nov 2022 11:00:12 -0500 Subject: [PATCH 9/9] Add course mode migration migration to add description for upgrade deadline explicitly set. --- .../migrations/0014_auto_20221107_1559.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 common/djangoapps/course_modes/migrations/0014_auto_20221107_1559.py diff --git a/common/djangoapps/course_modes/migrations/0014_auto_20221107_1559.py b/common/djangoapps/course_modes/migrations/0014_auto_20221107_1559.py new file mode 100644 index 000000000000..f8028d7c0c72 --- /dev/null +++ b/common/djangoapps/course_modes/migrations/0014_auto_20221107_1559.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.16 on 2022-11-07 15:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course_modes', '0013_auto_20200115_2022'), + ] + + operations = [ + migrations.AlterField( + model_name='coursemode', + name='expiration_datetime_is_explicit', + field=models.BooleanField(default=False, help_text='OPTIONAL: Set to True if the upgrade deadline date is explicitly defined. Set to False if there is no upgrade deadline or to use the default upgrade deadline.', verbose_name='Upgrade Deadline Explicitly Defined'), + ), + migrations.AlterField( + model_name='historicalcoursemode', + name='expiration_datetime_is_explicit', + field=models.BooleanField(default=False, help_text='OPTIONAL: Set to True if the upgrade deadline date is explicitly defined. Set to False if there is no upgrade deadline or to use the default upgrade deadline.', verbose_name='Upgrade Deadline Explicitly Defined'), + ), + ]