Switch ContentTypeGatingConfig.enabled_as_of and CourseDurationLimitConfig.enabled_as_of to datetimes
This commit is contained in:
@@ -5,7 +5,7 @@ import itertools
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from datetime import timedelta, date
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
import ddt
|
||||
from completion.test_utils import submit_completions_for_testing, CompletionWaffleTestMixin
|
||||
@@ -730,7 +730,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
Links will be removed from the course title, course image and button (View Course/Resume Course).
|
||||
The course card should have an access expired message.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
self.override_waffle_switch(True)
|
||||
|
||||
course = CourseFactory.create(start=self.THREE_YEARS_AGO)
|
||||
|
||||
@@ -3,7 +3,7 @@ Test the partitions and partitions service
|
||||
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from django.test import TestCase
|
||||
from mock import Mock, patch
|
||||
|
||||
@@ -439,7 +439,7 @@ class PartitionServiceBaseClass(PartitionTestCase):
|
||||
|
||||
ContentTypeGatingConfig.objects.create(
|
||||
enabled=True,
|
||||
enabled_as_of=date(2018, 1, 1),
|
||||
enabled_as_of=datetime(2018, 1, 1),
|
||||
studio_override_enabled=True
|
||||
)
|
||||
self.course = Mock(id=CourseLocator('org_0', 'course_0', 'run_0'))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Performance tests for field overrides.
|
||||
"""
|
||||
import itertools
|
||||
from datetime import datetime, date
|
||||
from datetime import datetime
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
@@ -204,7 +204,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT
|
||||
"""
|
||||
ContentTypeGatingConfig.objects.create(
|
||||
enabled=True,
|
||||
enabled_as_of=date(2018, 1, 1),
|
||||
enabled_as_of=datetime(2018, 1, 1),
|
||||
)
|
||||
|
||||
providers = {
|
||||
|
||||
@@ -828,7 +828,7 @@ class CourseOverviewAccessTestCase(ModuleStoreTestCase):
|
||||
@ddt.unpack
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
def test_course_catalog_access_num_queries(self, user_attr_name, action, course_attr_name):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime.date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime.datetime(2018, 1, 1))
|
||||
|
||||
course = getattr(self, course_attr_name)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
Test the course_info xblock
|
||||
"""
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
import ddt
|
||||
import mock
|
||||
from django.conf import settings
|
||||
@@ -418,7 +418,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
|
||||
|
||||
def setUp(self):
|
||||
super(SelfPacedCourseInfoTestCase, self).setUp()
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
self.setup_user()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests courseware views.py
|
||||
import itertools
|
||||
import json
|
||||
import unittest
|
||||
from datetime import datetime, timedelta, date
|
||||
from datetime import datetime, timedelta
|
||||
from HTMLParser import HTMLParser
|
||||
from urllib import quote, urlencode
|
||||
from uuid import uuid4
|
||||
@@ -218,7 +218,7 @@ class IndexQueryTestCase(ModuleStoreTestCase):
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
with self.store.default_store(store_type):
|
||||
course = CourseFactory.create()
|
||||
with self.store.bulk_operations(course.id):
|
||||
@@ -1445,7 +1445,7 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
@ddt.unpack
|
||||
def test_progress_queries_paced_courses(self, self_paced, query_count):
|
||||
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
self.setup_course(self_paced=self_paced)
|
||||
with self.assertNumQueries(query_count, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST), check_mongo_calls(1):
|
||||
self._get_progress_page()
|
||||
@@ -1457,7 +1457,7 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_progress_queries(self, enable_waffle, initial, subsequent):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
self.setup_course()
|
||||
with grades_waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=enable_waffle):
|
||||
with self.assertNumQueries(
|
||||
@@ -1668,7 +1668,7 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
Verify that expired banner message appears on progress page, if learner is enrolled
|
||||
in audit mode.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
user = UserFactory.create()
|
||||
self.assertTrue(self.client.login(username=user.username, password='test'))
|
||||
add_course_mode(self.course, upgrade_deadline_expired=False)
|
||||
@@ -2715,7 +2715,7 @@ class TestIndexViewWithCourseDurationLimits(ModuleStoreTestCase):
|
||||
Test that the courseware contains the course expiration banner
|
||||
when course_duration_limits are enabled.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
self.assertTrue(self.client.login(username=self.user.username, password='test'))
|
||||
add_course_mode(self.course, upgrade_deadline_expired=False)
|
||||
response = self.client.get(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime, date
|
||||
from datetime import datetime
|
||||
|
||||
import ddt
|
||||
from django.urls import reverse
|
||||
@@ -456,7 +456,7 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
num_cached_sql_queries,
|
||||
mock_request
|
||||
):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
with modulestore().default_store(default_store):
|
||||
course = CourseFactory.create(discussion_topics={'dummy discussion': {'id': 'dummy_discussion_id'}})
|
||||
|
||||
|
||||
@@ -299,7 +299,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
|
||||
Test that expired courses are only returned in v1 of API
|
||||
when waffle flag enabled, and un-expired courses always returned
|
||||
'''
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime.date(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime.datetime(2018, 1, 1))
|
||||
courses = self._get_enrollment_data(api_version, expired)
|
||||
self._assert_enrollment_results(api_version, courses, num_courses_returned)
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.16 on 2018-11-28 19:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('content_type_gating', '0002_auto_20181119_0959'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contenttypegatingconfig',
|
||||
name='enabled_as_of',
|
||||
field=models.DateTimeField(blank=True, default=None, help_text='If the configuration is Enabled, then all enrollments created after this date (UTC) will be affected.', null=True, verbose_name='Enabled As Of'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.16 on 2018-11-28 20:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('content_type_gating', '0003_auto_20181128_1407'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contenttypegatingconfig',
|
||||
name='enabled_as_of',
|
||||
field=models.DateTimeField(blank=True, default=None, help_text='If the configuration is Enabled, then all enrollments created after this date and time (UTC) will be affected.', null=True, verbose_name='Enabled As Of'),
|
||||
),
|
||||
]
|
||||
@@ -5,11 +5,11 @@ Content Type Gating Configuration Models
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from experiments.models import ExperimentData
|
||||
from student.models import CourseEnrollment
|
||||
@@ -29,14 +29,14 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
|
||||
|
||||
STACKABLE_FIELDS = ('enabled', 'enabled_as_of', 'studio_override_enabled')
|
||||
|
||||
enabled_as_of = models.DateField(
|
||||
enabled_as_of = models.DateTimeField(
|
||||
default=None,
|
||||
null=True,
|
||||
verbose_name=_('Enabled As Of'),
|
||||
blank=True,
|
||||
help_text=_(
|
||||
'If the configuration is Enabled, then all enrollments '
|
||||
'created after this date (UTC) will be affected.'
|
||||
'created after this date and time (UTC) will be affected.'
|
||||
)
|
||||
)
|
||||
studio_override_enabled = models.NullBooleanField(
|
||||
@@ -87,7 +87,7 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
|
||||
# enrollment might be None if the user isn't enrolled. In that case,
|
||||
# return enablement as if the user enrolled today
|
||||
if enrollment is None:
|
||||
return cls.enabled_for_course(course_key=course_key, target_date=datetime.utcnow().date())
|
||||
return cls.enabled_for_course(course_key=course_key, target_datetime=timezone.now())
|
||||
else:
|
||||
# TODO: clean up as part of REV-100
|
||||
experiment_data_holdback_key = EXPERIMENT_DATA_HOLDBACK_KEY.format(user)
|
||||
@@ -104,48 +104,48 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
|
||||
if is_in_holdback:
|
||||
return False
|
||||
current_config = cls.current(course_key=enrollment.course_id)
|
||||
return current_config.enabled_as_of_date(target_date=enrollment.created.date())
|
||||
return current_config.enabled_as_of_datetime(target_datetime=enrollment.created)
|
||||
|
||||
@classmethod
|
||||
def enabled_for_course(cls, course_key, target_date=None):
|
||||
def enabled_for_course(cls, course_key, target_datetime=None):
|
||||
"""
|
||||
Return whether Content Type Gating is enabled for this course as of a particular date.
|
||||
|
||||
Content Type Gating is enabled for a course on a date if it is enabled either specifically,
|
||||
or via a containing context, such as the org, site, or globally, and if the configuration
|
||||
is specified to be ``enabled_as_of`` before ``target_date``.
|
||||
is specified to be ``enabled_as_of`` before ``target_datetime``.
|
||||
|
||||
Only one of enrollment and (user, course_key) may be specified at a time.
|
||||
|
||||
Arguments:
|
||||
course_key: The CourseKey of the course being queried.
|
||||
target_date: The date to checked enablement as of. Defaults to the current date.
|
||||
target_datetime: The datetime to checked enablement as of. Defaults to the current date and time.
|
||||
"""
|
||||
if CONTENT_TYPE_GATING_FLAG.is_enabled():
|
||||
return True
|
||||
|
||||
if target_date is None:
|
||||
target_date = datetime.utcnow().date()
|
||||
if target_datetime is None:
|
||||
target_datetime = timezone.now()
|
||||
|
||||
current_config = cls.current(course_key=course_key)
|
||||
return current_config.enabled_as_of_date(target_date=target_date)
|
||||
return current_config.enabled_as_of_datetime(target_datetime=target_datetime)
|
||||
|
||||
def clean(self):
|
||||
if self.enabled and self.enabled_as_of is None:
|
||||
raise ValidationError({'enabled_as_of': _('enabled_as_of must be set when enabled is True')})
|
||||
|
||||
def enabled_as_of_date(self, target_date):
|
||||
def enabled_as_of_datetime(self, target_datetime):
|
||||
"""
|
||||
Return whether this Content Type Gating configuration context is enabled as of a date.
|
||||
Return whether this Content Type Gating configuration context is enabled as of a date and time.
|
||||
|
||||
Arguments:
|
||||
target_date (:class:`datetime.date`): The date that ``enabled_as_of`` must be equal to or before
|
||||
target_datetime (:class:`datetime.datetime`): The datetime that ``enabled_as_of`` must be equal to or before
|
||||
"""
|
||||
if CONTENT_TYPE_GATING_FLAG.is_enabled():
|
||||
return True
|
||||
|
||||
# Explicitly cast this to bool, so that when self.enabled is None the method doesn't return None
|
||||
return bool(self.enabled and self.enabled_as_of <= target_date)
|
||||
return bool(self.enabled and self.enabled_as_of <= target_datetime)
|
||||
|
||||
def __str__(self):
|
||||
return "ContentTypeGatingConfig(enabled={!r}, enabled_as_of={!r}, studio_override_enabled={!r})".format(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Test audit user's access to various content based on content-gating features.
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.test.client import RequestFactory
|
||||
@@ -225,7 +225,7 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase):
|
||||
course_id=self.courses['audit_only']['course'].id,
|
||||
mode='audit'
|
||||
)
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
@classmethod
|
||||
def _create_course(cls, run, display_name, modes, component_types):
|
||||
@@ -478,7 +478,7 @@ class TestConditionalContentAccess(TestConditionalContent):
|
||||
def setUpClass(cls):
|
||||
super(TestConditionalContentAccess, cls).setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
def setUp(self):
|
||||
super(TestConditionalContentAccess, self).setUp()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ddt
|
||||
from datetime import timedelta, date, datetime, time
|
||||
from datetime import timedelta, datetime
|
||||
import itertools
|
||||
|
||||
import ddt
|
||||
from django.utils import timezone
|
||||
from mock import Mock
|
||||
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory
|
||||
@@ -38,12 +40,12 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
enrolled_before_enabled,
|
||||
):
|
||||
|
||||
# Tweak the day to enable the config so that it is either before
|
||||
# or after today (which is when the enrollment will be created)
|
||||
# Tweak the datetime to enable the config so that it is either before
|
||||
# or after now (which is when the enrollment will be created)
|
||||
if enrolled_before_enabled:
|
||||
enabled_as_of = date.today() + timedelta(days=1)
|
||||
enabled_as_of = datetime.now() + timedelta(days=1)
|
||||
else:
|
||||
enabled_as_of = date.today() - timedelta(days=1)
|
||||
enabled_as_of = datetime.now() - timedelta(days=1)
|
||||
|
||||
config = ContentTypeGatingConfig.objects.create(
|
||||
enabled=True,
|
||||
@@ -102,15 +104,15 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
config = ContentTypeGatingConfig.objects.create(
|
||||
enabled=True,
|
||||
course=self.course_overview,
|
||||
enabled_as_of=date(2018, 1, 1),
|
||||
enabled_as_of=timezone.now(),
|
||||
)
|
||||
|
||||
# Tweak the day to check for course enablement so it is either
|
||||
# Tweak the datetime to check for course enablement so it is either
|
||||
# before or after when the configuration was enabled
|
||||
if before_enabled:
|
||||
target_date = config.enabled_as_of - timedelta(days=1)
|
||||
target_datetime = config.enabled_as_of - timedelta(days=1)
|
||||
else:
|
||||
target_date = config.enabled_as_of + timedelta(days=1)
|
||||
target_datetime = config.enabled_as_of + timedelta(days=1)
|
||||
|
||||
course_key = self.course_overview.id
|
||||
|
||||
@@ -118,7 +120,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
not before_enabled,
|
||||
ContentTypeGatingConfig.enabled_for_course(
|
||||
course_key=course_key,
|
||||
target_date=target_date,
|
||||
target_datetime=target_datetime,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -141,21 +143,21 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
non_test_site_cfg_enabled = SiteConfigurationFactory.create(values={'course_org_filter': non_test_course_enabled.org})
|
||||
non_test_site_cfg_disabled = SiteConfigurationFactory.create(values={'course_org_filter': non_test_course_disabled.org})
|
||||
|
||||
ContentTypeGatingConfig.objects.create(course=non_test_course_enabled, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(course=non_test_course_enabled, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(course=non_test_course_disabled, enabled=False)
|
||||
ContentTypeGatingConfig.objects.create(org=non_test_course_enabled.org, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(org=non_test_course_enabled.org, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(org=non_test_course_disabled.org, enabled=False)
|
||||
ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_enabled.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_enabled.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_disabled.site, enabled=False)
|
||||
|
||||
# Set up test objects
|
||||
test_course = CourseOverviewFactory.create(org='test-org')
|
||||
test_site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': test_course.org})
|
||||
|
||||
ContentTypeGatingConfig.objects.create(enabled=global_setting, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(course=test_course, enabled=course_setting, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(org=test_course.org, enabled=org_setting, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(site=test_site_cfg.site, enabled=site_setting, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=global_setting, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(course=test_course, enabled=course_setting, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(org=test_course.org, enabled=org_setting, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(site=test_site_cfg.site, enabled=site_setting, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
all_settings = [global_setting, site_setting, org_setting, course_setting]
|
||||
expected_global_setting = self._resolve_settings([global_setting])
|
||||
@@ -169,7 +171,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
self.assertEqual(expected_course_setting, ContentTypeGatingConfig.current(course_key=test_course.id).enabled)
|
||||
|
||||
def test_caching_global(self):
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the global value is not retrieved from cache after save
|
||||
@@ -189,7 +191,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
|
||||
def test_caching_site(self):
|
||||
site_cfg = SiteConfigurationFactory()
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the site value is not retrieved from cache after save
|
||||
@@ -207,7 +209,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(1):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(site=site_cfg.site).enabled)
|
||||
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the site value is not updated in cache by changing the global value
|
||||
@@ -217,7 +219,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
def test_caching_org(self):
|
||||
course = CourseOverviewFactory.create(org='test-org')
|
||||
site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org})
|
||||
org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
org_config.save()
|
||||
|
||||
# Check that the org value is not retrieved from cache after save
|
||||
@@ -235,14 +237,14 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(2):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled)
|
||||
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the global value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled)
|
||||
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
@@ -252,7 +254,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
def test_caching_course(self):
|
||||
course = CourseOverviewFactory.create(org='test-org')
|
||||
site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org})
|
||||
course_config = ContentTypeGatingConfig(course=course, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
course_config = ContentTypeGatingConfig(course=course, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
course_config.save()
|
||||
|
||||
# Check that the org value is not retrieved from cache after save
|
||||
@@ -270,21 +272,21 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(2):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(course_key=course.id).enabled)
|
||||
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the global value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(course_key=course.id).enabled)
|
||||
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(ContentTypeGatingConfig.current(course_key=course.id).enabled)
|
||||
|
||||
org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
org_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from mock import Mock, patch
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
@@ -15,7 +15,7 @@ class TestContentTypeGatingPartition(CacheIsolationTestCase):
|
||||
def test_create_content_gating_partition_happy_path(self):
|
||||
|
||||
mock_course = Mock(id=self.course_key, user_partitions={})
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
with patch('openedx.features.content_type_gating.partitions.ContentTypeGatingPartitionScheme.create_user_partition') as mock_create:
|
||||
partition = create_content_gating_partition(mock_course)
|
||||
@@ -37,7 +37,7 @@ class TestContentTypeGatingPartition(CacheIsolationTestCase):
|
||||
|
||||
def test_create_content_gating_partition_no_scheme_installed(self):
|
||||
mock_course = Mock(id=self.course_key, user_partitions={})
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
with patch('openedx.features.content_type_gating.partitions.UserPartition.get_scheme', side_effect=UserPartitionError):
|
||||
partition = create_content_gating_partition(mock_course)
|
||||
@@ -46,7 +46,7 @@ class TestContentTypeGatingPartition(CacheIsolationTestCase):
|
||||
|
||||
def test_create_content_gating_partition_partition_id_used(self):
|
||||
mock_course = Mock(id=self.course_key, user_partitions={Mock(name='partition', id=CONTENT_GATING_PARTITION_ID): object()})
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
with patch('openedx.features.content_type_gating.partitions.LOG') as mock_log:
|
||||
partition = create_content_gating_partition(mock_course)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.16 on 2018-11-28 19:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('course_duration_limits', '0002_auto_20181119_0959'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='coursedurationlimitconfig',
|
||||
name='enabled_as_of',
|
||||
field=models.DateTimeField(blank=True, default=None, help_text='If the configuration is Enabled, then all enrollments created after this date (UTC) will be affected.', null=True, verbose_name='Enabled As Of'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.16 on 2018-11-28 20:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('course_duration_limits', '0003_auto_20181128_1407'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='coursedurationlimitconfig',
|
||||
name='enabled_as_of',
|
||||
field=models.DateTimeField(blank=True, default=None, help_text='If the configuration is Enabled, then all enrollments created after this date and time (UTC) will be affected.', null=True, verbose_name='Enabled As Of'),
|
||||
),
|
||||
]
|
||||
@@ -5,11 +5,11 @@ Course Duration Limit Configuration Models
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from experiments.models import ExperimentData
|
||||
from student.models import CourseEnrollment
|
||||
@@ -29,14 +29,14 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
|
||||
|
||||
STACKABLE_FIELDS = ('enabled', 'enabled_as_of')
|
||||
|
||||
enabled_as_of = models.DateField(
|
||||
enabled_as_of = models.DateTimeField(
|
||||
default=None,
|
||||
null=True,
|
||||
verbose_name=_('Enabled As Of'),
|
||||
blank=True,
|
||||
help_text=_(
|
||||
'If the configuration is Enabled, then all enrollments '
|
||||
'created after this date (UTC) will be affected.'
|
||||
'created after this date and time (UTC) will be affected.'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -78,7 +78,7 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
|
||||
# enrollment might be None if the user isn't enrolled. In that case,
|
||||
# return enablement as if the user enrolled today
|
||||
if enrollment is None:
|
||||
return cls.enabled_for_course(course_key=course_key, target_date=datetime.utcnow().date())
|
||||
return cls.enabled_for_course(course_key=course_key, target_datetime=timezone.now())
|
||||
else:
|
||||
# TODO: clean up as part of REV-100
|
||||
experiment_data_holdback_key = EXPERIMENT_DATA_HOLDBACK_KEY.format(user)
|
||||
@@ -95,48 +95,48 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
|
||||
if is_in_holdback:
|
||||
return False
|
||||
current_config = cls.current(course_key=enrollment.course_id)
|
||||
return current_config.enabled_as_of_date(target_date=enrollment.created.date())
|
||||
return current_config.enabled_as_of_datetime(target_datetime=enrollment.created)
|
||||
|
||||
@classmethod
|
||||
def enabled_for_course(cls, course_key, target_date=None):
|
||||
def enabled_for_course(cls, course_key, target_datetime=None):
|
||||
"""
|
||||
Return whether Course Duration Limits are enabled for this course as of a particular date.
|
||||
|
||||
Course Duration Limits are enabled for a course on a date if they are enabled either specifically,
|
||||
or via a containing context, such as the org, site, or globally, and if the configuration
|
||||
is specified to be ``enabled_as_of`` before ``target_date``.
|
||||
is specified to be ``enabled_as_of`` before ``target_datetime``.
|
||||
|
||||
Only one of enrollment and (user, course_key) may be specified at a time.
|
||||
|
||||
Arguments:
|
||||
course_key: The CourseKey of the course being queried.
|
||||
target_date: The date to checked enablement as of. Defaults to the current date.
|
||||
target_datetime: The datetime to checked enablement as of. Defaults to the current date and time.
|
||||
"""
|
||||
if CONTENT_TYPE_GATING_FLAG.is_enabled():
|
||||
return True
|
||||
|
||||
if target_date is None:
|
||||
target_date = datetime.utcnow().date()
|
||||
if target_datetime is None:
|
||||
target_datetime = timezone.now()
|
||||
|
||||
current_config = cls.current(course_key=course_key)
|
||||
return current_config.enabled_as_of_date(target_date=target_date)
|
||||
return current_config.enabled_as_of_datetime(target_datetime=target_datetime)
|
||||
|
||||
def clean(self):
|
||||
if self.enabled and self.enabled_as_of is None:
|
||||
raise ValidationError({'enabled_as_of': _('enabled_as_of must be set when enabled is True')})
|
||||
|
||||
def enabled_as_of_date(self, target_date):
|
||||
def enabled_as_of_datetime(self, target_datetime):
|
||||
"""
|
||||
Return whether this Course Duration Limit configuration context is enabled as of a date.
|
||||
Return whether this Course Duration Limit configuration context is enabled as of a date and time.
|
||||
|
||||
Arguments:
|
||||
target_date (:class:`datetime.date`): The date that ``enabled_as_of`` must be equal to or before
|
||||
target_datetime (:class:`datetime.datetime`): The datetime that ``enabled_as_of`` must be equal to or before
|
||||
"""
|
||||
if CONTENT_TYPE_GATING_FLAG.is_enabled():
|
||||
return True
|
||||
|
||||
# Explicitly cast this to bool, so that when self.enabled is None the method doesn't return None
|
||||
return bool(self.enabled and self.enabled_as_of <= target_date)
|
||||
return bool(self.enabled and self.enabled_as_of <= target_datetime)
|
||||
|
||||
def __str__(self):
|
||||
return "CourseDurationLimits(enabled={!r}, enabled_as_of={!r})".format(
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
Tests of CourseDurationLimitConfig.
|
||||
"""
|
||||
|
||||
from datetime import timedelta, date
|
||||
from datetime import timedelta, datetime
|
||||
import itertools
|
||||
|
||||
import ddt
|
||||
from django.utils import timezone
|
||||
from mock import Mock
|
||||
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
|
||||
@@ -46,12 +46,12 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
enrolled_before_enabled,
|
||||
):
|
||||
|
||||
# Tweak the day to enable the config so that it is either before
|
||||
# or after today (which is when the enrollment will be created)
|
||||
# Tweak the datetime to enable the config so that it is either before
|
||||
# or after now (which is when the enrollment will be created)
|
||||
if enrolled_before_enabled:
|
||||
enabled_as_of = date.today() + timedelta(days=1)
|
||||
enabled_as_of = timezone.now() + timedelta(days=1)
|
||||
else:
|
||||
enabled_as_of = date.today() - timedelta(days=1)
|
||||
enabled_as_of = timezone.now() - timedelta(days=1)
|
||||
|
||||
CourseDurationLimitConfig.objects.create(
|
||||
enabled=True,
|
||||
@@ -130,15 +130,15 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
config = CourseDurationLimitConfig.objects.create(
|
||||
enabled=True,
|
||||
course=self.course_overview,
|
||||
enabled_as_of=date.today(),
|
||||
enabled_as_of=timezone.now(),
|
||||
)
|
||||
|
||||
# Tweak the day to check for course enablement so it is either
|
||||
# Tweak the datetime to check for course enablement so it is either
|
||||
# before or after when the configuration was enabled
|
||||
if before_enabled:
|
||||
target_date = config.enabled_as_of - timedelta(days=1)
|
||||
target_datetime = config.enabled_as_of - timedelta(days=1)
|
||||
else:
|
||||
target_date = config.enabled_as_of + timedelta(days=1)
|
||||
target_datetime = config.enabled_as_of + timedelta(days=1)
|
||||
|
||||
course_key = self.course_overview.id
|
||||
|
||||
@@ -146,7 +146,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
not before_enabled,
|
||||
CourseDurationLimitConfig.enabled_for_course(
|
||||
course_key=course_key,
|
||||
target_date=target_date,
|
||||
target_datetime=target_datetime,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -200,7 +200,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
self.assertEqual(expected_course_setting, CourseDurationLimitConfig.current(course_key=test_course.id).enabled)
|
||||
|
||||
def test_caching_global(self):
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the global value is not retrieved from cache after save
|
||||
@@ -220,7 +220,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
|
||||
def test_caching_site(self):
|
||||
site_cfg = SiteConfigurationFactory()
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the site value is not retrieved from cache after save
|
||||
@@ -238,7 +238,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(1):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(site=site_cfg.site).enabled)
|
||||
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the site value is not updated in cache by changing the global value
|
||||
@@ -248,7 +248,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
def test_caching_org(self):
|
||||
course = CourseOverviewFactory.create(org='test-org')
|
||||
site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org})
|
||||
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
org_config.save()
|
||||
|
||||
# Check that the org value is not retrieved from cache after save
|
||||
@@ -266,14 +266,14 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(2):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(org=course.org).enabled)
|
||||
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the global value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(org=course.org).enabled)
|
||||
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
@@ -283,7 +283,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
def test_caching_course(self):
|
||||
course = CourseOverviewFactory.create(org='test-org')
|
||||
site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org})
|
||||
course_config = CourseDurationLimitConfig(course=course, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
course_config = CourseDurationLimitConfig(course=course, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
course_config.save()
|
||||
|
||||
# Check that the org value is not retrieved from cache after save
|
||||
@@ -301,21 +301,21 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
with self.assertNumQueries(2):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(course_key=course.id).enabled)
|
||||
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
global_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the global value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(course_key=course.id).enabled)
|
||||
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
site_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
with self.assertNumQueries(0):
|
||||
self.assertFalse(CourseDurationLimitConfig.current(course_key=course.id).enabled)
|
||||
|
||||
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
org_config.save()
|
||||
|
||||
# Check that the org value is not updated in cache by changing the site value
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
Tests for the course home page.
|
||||
"""
|
||||
from datetime import datetime, timedelta, date
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
@@ -189,7 +189,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
|
||||
"""
|
||||
Verify that the view's query count doesn't regress.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
# Pre-fetch the view to populate any caches
|
||||
course_home_url(self.course)
|
||||
|
||||
@@ -435,7 +435,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
Ensure that a user accessing an expired course sees a redirect to
|
||||
the student dashboard, not a 404.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2010, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2010, 1, 1))
|
||||
course = CourseFactory.create(start=THREE_YEARS_AGO)
|
||||
url = course_home_url(course)
|
||||
|
||||
@@ -468,7 +468,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
Verify that enrolled users are NOT shown the course expiration banner and can
|
||||
access the course home page if course audit only
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2010, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2010, 1, 1))
|
||||
audit_only_course = CourseFactory.create()
|
||||
self.create_user_for_course(audit_only_course, CourseUserType.ENROLLED)
|
||||
response = self.client.get(course_home_url(audit_only_course))
|
||||
@@ -483,7 +483,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
Ensure that a user accessing an expired course that is in the holdback
|
||||
does not get redirected to the student dashboard, not a 404.
|
||||
"""
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2010, 1, 1))
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2010, 1, 1))
|
||||
|
||||
course = CourseFactory.create(start=THREE_YEARS_AGO)
|
||||
url = course_home_url(course)
|
||||
@@ -584,7 +584,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
config = CourseDurationLimitConfig(
|
||||
course=CourseOverview.get_from_id(self.course.id),
|
||||
enabled=True,
|
||||
enabled_as_of=date(2018, 1, 1)
|
||||
enabled_as_of=datetime(2018, 1, 1)
|
||||
)
|
||||
config.save()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Tests for the course updates page.
|
||||
"""
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
|
||||
from courseware.courses import get_course_info_usage_key
|
||||
from django.urls import reverse
|
||||
@@ -122,7 +122,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
self.assertContains(response, 'Second Message')
|
||||
|
||||
def test_queries(self):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
create_course_update(self.course, self.user, 'First Message')
|
||||
|
||||
# Pre-fetch the view to populate any caches
|
||||
|
||||
Reference in New Issue
Block a user