create table that excludes enrollments from FBE
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2019-10-31 14:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('student', '0023_bulkunenrollconfiguration'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FBEEnrollmentExclusion',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('enrollment', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='student.CourseEnrollment')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -2045,6 +2045,22 @@ class CourseEnrollment(models.Model):
|
||||
cache[(user_id, course_key)] = enrollment_state
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class FBEEnrollmentExclusion(models.Model):
|
||||
"""
|
||||
Disable FBE for enrollments in this table.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
enrollment = models.ForeignKey(
|
||||
CourseEnrollment,
|
||||
on_delete=models.DO_NOTHING,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "[FBEEnrollmentExclusion] %s" % (self.enrollment,)
|
||||
|
||||
|
||||
@receiver(models.signals.post_save, sender=CourseEnrollment)
|
||||
@receiver(models.signals.post_delete, sender=CourseEnrollment)
|
||||
def invalidate_enrollment_mode_cache(sender, instance, **kwargs): # pylint: disable=unused-argument, invalid-name
|
||||
|
||||
@@ -244,7 +244,7 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
|
||||
__test__ = True
|
||||
|
||||
# TODO: decrease query count as part of REVO-28
|
||||
QUERY_COUNT = 36
|
||||
QUERY_COUNT = 40
|
||||
TEST_DATA = {
|
||||
# (providers, course_width, enable_ccx, view_as_ccx): (
|
||||
# # of sql queries to default,
|
||||
@@ -273,7 +273,7 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
|
||||
__test__ = True
|
||||
|
||||
# TODO: decrease query count as part of REVO-28
|
||||
QUERY_COUNT = 36
|
||||
QUERY_COUNT = 40
|
||||
|
||||
TEST_DATA = {
|
||||
('no_overrides', 1, True, False): (QUERY_COUNT, 3),
|
||||
|
||||
@@ -225,8 +225,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
|
||||
NUM_PROBLEMS = 20
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.mongo, 10, 186),
|
||||
(ModuleStoreEnum.Type.split, 4, 180),
|
||||
(ModuleStoreEnum.Type.mongo, 10, 193),
|
||||
(ModuleStoreEnum.Type.split, 4, 185),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
|
||||
@@ -1459,8 +1459,8 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
self.assertContains(resp, u"Download Your Certificate")
|
||||
|
||||
@ddt.data(
|
||||
(True, 56),
|
||||
(False, 55)
|
||||
(True, 60),
|
||||
(False, 59)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_progress_queries_paced_courses(self, self_paced, query_count):
|
||||
@@ -1473,8 +1473,8 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS': False})
|
||||
@ddt.data(
|
||||
(False, 64, 44),
|
||||
(True, 55, 39)
|
||||
(False, 68, 48),
|
||||
(True, 59, 43)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_progress_queries(self, enable_waffle, initial, subsequent):
|
||||
|
||||
@@ -404,8 +404,8 @@ class ViewsQueryCountTestCase(
|
||||
return inner
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.mongo, 3, 4, 41),
|
||||
(ModuleStoreEnum.Type.split, 3, 13, 41),
|
||||
(ModuleStoreEnum.Type.mongo, 3, 4, 42),
|
||||
(ModuleStoreEnum.Type.split, 3, 13, 42),
|
||||
)
|
||||
@ddt.unpack
|
||||
@count_queries
|
||||
@@ -413,8 +413,8 @@ class ViewsQueryCountTestCase(
|
||||
self.create_thread_helper(mock_request)
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.mongo, 3, 3, 37),
|
||||
(ModuleStoreEnum.Type.split, 3, 10, 37),
|
||||
(ModuleStoreEnum.Type.mongo, 3, 3, 38),
|
||||
(ModuleStoreEnum.Type.split, 3, 10, 38),
|
||||
)
|
||||
@ddt.unpack
|
||||
@count_queries
|
||||
|
||||
@@ -464,18 +464,18 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
# course is outside the context manager that is verifying the number of queries,
|
||||
# and with split mongo, that method ends up querying disabled_xblocks (which is then
|
||||
# cached and hence not queried as part of call_single_thread).
|
||||
(ModuleStoreEnum.Type.mongo, False, 1, 5, 2, 24, 9),
|
||||
(ModuleStoreEnum.Type.mongo, False, 50, 5, 2, 24, 9),
|
||||
(ModuleStoreEnum.Type.mongo, False, 1, 5, 2, 25, 10),
|
||||
(ModuleStoreEnum.Type.mongo, False, 50, 5, 2, 25, 10),
|
||||
# split mongo: 3 queries, regardless of thread response size.
|
||||
(ModuleStoreEnum.Type.split, False, 1, 3, 3, 24, 9),
|
||||
(ModuleStoreEnum.Type.split, False, 50, 3, 3, 24, 9),
|
||||
(ModuleStoreEnum.Type.split, False, 1, 3, 3, 25, 10),
|
||||
(ModuleStoreEnum.Type.split, False, 50, 3, 3, 25, 10),
|
||||
|
||||
# Enabling Enterprise integration should have no effect on the number of mongo queries made.
|
||||
(ModuleStoreEnum.Type.mongo, True, 1, 5, 2, 24, 9),
|
||||
(ModuleStoreEnum.Type.mongo, True, 50, 5, 2, 24, 9),
|
||||
(ModuleStoreEnum.Type.mongo, True, 1, 5, 2, 25, 10),
|
||||
(ModuleStoreEnum.Type.mongo, True, 50, 5, 2, 25, 10),
|
||||
# split mongo: 3 queries, regardless of thread response size.
|
||||
(ModuleStoreEnum.Type.split, True, 1, 3, 3, 24, 9),
|
||||
(ModuleStoreEnum.Type.split, True, 50, 3, 3, 24, 9),
|
||||
(ModuleStoreEnum.Type.split, True, 1, 3, 3, 25, 10),
|
||||
(ModuleStoreEnum.Type.split, True, 50, 3, 3, 25, 10),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_number_of_mongo_queries(
|
||||
|
||||
@@ -97,35 +97,35 @@ class TestCourseGradeFactory(GradeTestBase):
|
||||
[self.sequence.display_name, self.sequence2.display_name]
|
||||
)
|
||||
|
||||
with self.assertNumQueries(4), mock_get_score(1, 2):
|
||||
with self.assertNumQueries(5), mock_get_score(1, 2):
|
||||
_assert_read(expected_pass=False, expected_percent=0) # start off with grade of 0
|
||||
|
||||
num_queries = 47
|
||||
num_queries = 48
|
||||
with self.assertNumQueries(num_queries), mock_get_score(1, 2):
|
||||
grade_factory.update(self.request.user, self.course, force_update_subsections=True)
|
||||
|
||||
with self.assertNumQueries(5):
|
||||
with self.assertNumQueries(6):
|
||||
_assert_read(expected_pass=True, expected_percent=0.5) # updated to grade of .5
|
||||
|
||||
num_queries = 9
|
||||
num_queries = 10
|
||||
with self.assertNumQueries(num_queries), mock_get_score(1, 4):
|
||||
grade_factory.update(self.request.user, self.course, force_update_subsections=False)
|
||||
|
||||
with self.assertNumQueries(5):
|
||||
with self.assertNumQueries(6):
|
||||
_assert_read(expected_pass=True, expected_percent=0.5) # NOT updated to grade of .25
|
||||
|
||||
num_queries = 26
|
||||
num_queries = 27
|
||||
with self.assertNumQueries(num_queries), mock_get_score(2, 2):
|
||||
grade_factory.update(self.request.user, self.course, force_update_subsections=True)
|
||||
|
||||
with self.assertNumQueries(5):
|
||||
with self.assertNumQueries(6):
|
||||
_assert_read(expected_pass=True, expected_percent=1.0) # updated to grade of 1.0
|
||||
|
||||
num_queries = 30
|
||||
num_queries = 31
|
||||
with self.assertNumQueries(num_queries), mock_get_score(0, 0): # the subsection now is worth zero
|
||||
grade_factory.update(self.request.user, self.course, force_update_subsections=True)
|
||||
|
||||
with self.assertNumQueries(5):
|
||||
with self.assertNumQueries(6):
|
||||
_assert_read(expected_pass=False, expected_percent=0.0) # updated to grade of 0.0
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS': False})
|
||||
|
||||
@@ -3,9 +3,10 @@ from __future__ import absolute_import
|
||||
|
||||
from experiments.models import ExperimentData
|
||||
from openedx.features.course_duration_limits.config import EXPERIMENT_DATA_HOLDBACK_KEY, EXPERIMENT_ID
|
||||
from student.models import FBEEnrollmentExclusion
|
||||
|
||||
|
||||
def is_in_holdback(user):
|
||||
def is_in_holdback(user, enrollment):
|
||||
"""
|
||||
Return true if given user is in holdback expermiment
|
||||
"""
|
||||
@@ -21,4 +22,8 @@ def is_in_holdback(user):
|
||||
except ExperimentData.DoesNotExist:
|
||||
pass
|
||||
|
||||
if enrollment is not None:
|
||||
if FBEEnrollmentExclusion.objects.filter(enrollment=enrollment).exists():
|
||||
return True
|
||||
|
||||
return in_holdback
|
||||
|
||||
@@ -134,7 +134,7 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
|
||||
return False
|
||||
|
||||
# check if user is in holdback
|
||||
if user_variable_represents_correct_user and is_in_holdback(user):
|
||||
if user_variable_represents_correct_user and is_in_holdback(user, enrollment):
|
||||
return False
|
||||
|
||||
if not correct_modes_for_fbe(course_key, enrollment, user):
|
||||
|
||||
@@ -78,9 +78,9 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
|
||||
user = self.user
|
||||
course_key = self.course_overview.id
|
||||
|
||||
query_count = 7
|
||||
if not already_enrolled or not pass_enrollment and already_enrolled:
|
||||
query_count = 8
|
||||
query_count = 8
|
||||
if not already_enrolled and pass_enrollment or not pass_enrollment and already_enrolled:
|
||||
query_count = 9
|
||||
|
||||
with self.assertNumQueries(query_count):
|
||||
enabled = ContentTypeGatingConfig.enabled_for_enrollment(
|
||||
|
||||
@@ -128,7 +128,7 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
|
||||
student_masquerade = is_masquerading_as_specific_student(user, course_key)
|
||||
|
||||
# check if user is in holdback
|
||||
if (no_masquerade or student_masquerade) and is_in_holdback(user):
|
||||
if (no_masquerade or student_masquerade) and is_in_holdback(user, enrollment):
|
||||
return False
|
||||
|
||||
not_student_masquerade = is_masquerading and not student_masquerade
|
||||
|
||||
@@ -84,9 +84,9 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
user = self.user
|
||||
course_key = self.course_overview.id
|
||||
|
||||
query_count = 7
|
||||
if pass_enrollment and already_enrolled:
|
||||
query_count = 6
|
||||
query_count = 8
|
||||
if pass_enrollment and already_enrolled or not pass_enrollment and not already_enrolled:
|
||||
query_count = 7
|
||||
|
||||
with self.assertNumQueries(query_count):
|
||||
enabled = CourseDurationLimitConfig.enabled_for_enrollment(
|
||||
|
||||
@@ -220,7 +220,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
|
||||
|
||||
# Fetch the view and verify the query counts
|
||||
# TODO: decrease query count as part of REVO-28
|
||||
with self.assertNumQueries(97, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
|
||||
with self.assertNumQueries(106, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
|
||||
with check_mongo_calls(4):
|
||||
url = course_home_url(self.course)
|
||||
self.client.get(url)
|
||||
|
||||
@@ -134,7 +134,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
|
||||
# Fetch the view and verify that the query counts haven't changed
|
||||
# TODO: decrease query count as part of REVO-28
|
||||
with self.assertNumQueries(56, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
|
||||
with self.assertNumQueries(59, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
|
||||
with check_mongo_calls(4):
|
||||
url = course_updates_url(self.course)
|
||||
self.client.get(url)
|
||||
|
||||
Reference in New Issue
Block a user