Ejemplo n.º 1
0
    def test_dashboard_metadata_caching(self, modulestore_type):
        """
        Check that the student dashboard makes use of course metadata caching.

        After creating a course, that course's metadata should be cached as a
        CourseOverview. The student dashboard should never have to make calls to
        the modulestore.

        Arguments:
            modulestore_type (ModuleStoreEnum.Type): Type of modulestore to create
                test course in.

        Note to future developers:
            If you break this test so that the "check_mongo_calls(0)" fails,
            please do NOT change it to "check_mongo_calls(n>1)". Instead, change
            your code to not load courses from the module store. This may
            involve adding fields to CourseOverview so that loading a full
            CourseDescriptor isn't necessary.
        """
        # Create a course and log in the user.
        # Creating a new course will trigger a publish event and the course will be cached
        test_course = CourseFactory.create(default_store=modulestore_type, emit_signals=True)
        self.client.login(username="******", password="******")

        with check_mongo_calls(0):
            CourseEnrollment.enroll(self.user, test_course.id)

        # Subsequent requests will only result in SQL queries to load the
        # CourseOverview object that has been created.
        with check_mongo_calls(0):
            response_1 = self.client.get(reverse('dashboard'))
            self.assertEquals(response_1.status_code, 200)
            response_2 = self.client.get(reverse('dashboard'))
            self.assertEquals(response_2.status_code, 200)
    def test_course_listing_performance(self, store, courses_list_from_group_calls, courses_list_calls):
        """
        Create large number of courses and give access of some of these courses to the user and
        compare the time to fetch accessible courses for the user through traversing all courses and
        reversing django groups
        """
        # create list of random course numbers which will be accessible to the user
        user_course_ids = random.sample(range(TOTAL_COURSES_COUNT), USER_COURSES_COUNT)

        # create courses and assign those to the user which have their number in user_course_ids
        with self.store.default_store(store):
            for number in range(TOTAL_COURSES_COUNT):
                org = 'Org{0}'.format(number)
                course = 'Course{0}'.format(number)
                run = 'Run{0}'.format(number)
                course_location = self.store.make_course_key(org, course, run)
                if number in user_course_ids:
                    self._create_course_with_access_groups(course_location, self.user, store=store)
                else:
                    self._create_course_with_access_groups(course_location, store=store)

        # time the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_1:
            courses_iter, __ = _accessible_courses_iter(self.request)
        self.assertEqual(len(list(courses_iter)), USER_COURSES_COUNT)

        # time again the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_2:
            courses_iter, __ = _accessible_courses_iter(self.request)
        self.assertEqual(len(list(courses_iter)), USER_COURSES_COUNT)

        # time the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_1:
            courses_list, __ = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_2:
            courses_list, __ = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # TODO (cdyer) : iteration over courses was optimized, and is now
        # sometimes faster than iteration over groups. One of the following
        # should be done to resolve this:
        # * Iteration over groups should be sped up.
        # * Iteration over groups should be removed, as it no longer saves time.
        # * Or this part of the test should be removed.

        # Test that the time taken by getting courses through reversing django
        # groups is lower then the time taken by traversing through all courses
        # (if accessible courses are relatively small).
        #self.assertGreaterEqual(iteration_over_courses_time_1.elapsed, iteration_over_groups_time_1.elapsed)
        #self.assertGreaterEqual(iteration_over_courses_time_2.elapsed, iteration_over_groups_time_2.elapsed)

        # Now count the db queries
        with check_mongo_calls(courses_list_from_group_calls):
            _accessible_courses_list_from_groups(self.request)

        with check_mongo_calls(courses_list_calls):
            list(_accessible_courses_iter(self.request))
Ejemplo n.º 3
0
 def test_ccx_course_caching(self):
     """verify that caching the propery works to limit queries"""
     with check_mongo_calls(1):
         # these statements are used entirely to demonstrate the
         # instance-level caching of these values on CCX objects. The
         # check_mongo_calls context is the point here.
         self.ccx.course  # pylint: disable=pointless-statement
     with check_mongo_calls(0):
         self.ccx.course  # pylint: disable=pointless-statement
Ejemplo n.º 4
0
    def test_course_listing_performance(self):
        """
        Create large number of courses and give access of some of these courses to the user and
        compare the time to fetch accessible courses for the user through traversing all courses and
        reversing django groups
        """
        # create list of random course numbers which will be accessible to the user
        user_course_ids = random.sample(range(TOTAL_COURSES_COUNT), USER_COURSES_COUNT)

        # create courses and assign those to the user which have their number in user_course_ids
        for number in range(TOTAL_COURSES_COUNT):
            org = 'Org{0}'.format(number)
            course = 'Course{0}'.format(number)
            run = 'Run{0}'.format(number)
            course_location = SlashSeparatedCourseKey(org, course, run)
            if number in user_course_ids:
                self._create_course_with_access_groups(course_location, self.user)
            else:
                self._create_course_with_access_groups(course_location)

        # time the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_1:
            courses_list, __ = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_2:
            courses_list, __ = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_1:
            courses_list, __ = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_2:
            courses_list, __ = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # test that the time taken by getting courses through reversing django groups is lower then the time
        # taken by traversing through all courses (if accessible courses are relatively small)
        self.assertGreaterEqual(iteration_over_courses_time_1.elapsed, iteration_over_groups_time_1.elapsed)
        self.assertGreaterEqual(iteration_over_courses_time_2.elapsed, iteration_over_groups_time_2.elapsed)

        # Now count the db queries
        with check_mongo_calls(USER_COURSES_COUNT):
            _accessible_courses_list_from_groups(self.request)

        # Calls:
        #    1) query old mongo
        #    2) get_more on old mongo
        #    3) query split (but no courses so no fetching of data)
        with check_mongo_calls(3):
            _accessible_courses_list(self.request)
Ejemplo n.º 5
0
 def test_ccx_start_caching(self):
     """verify that caching the start property works to limit queries"""
     now = datetime.now(utc)
     self.set_ccx_override("start", now)
     with check_mongo_calls(1):
         # these statements are used entirely to demonstrate the
         # instance-level caching of these values on CCX objects. The
         # check_mongo_calls context is the point here.
         self.ccx.start  # pylint: disable=pointless-statement, no-member
     with check_mongo_calls(0):
         self.ccx.start  # pylint: disable=pointless-statement, no-member
Ejemplo n.º 6
0
 def test_ccx_due_caching(self):
     """verify that caching the due property works to limit queries"""
     expected = datetime.now(UTC())
     self.set_ccx_override('due', expected)
     with check_mongo_calls(1):
         # these statements are used entirely to demonstrate the
         # instance-level caching of these values on CCX objects. The
         # check_mongo_calls context is the point here.
         self.ccx.due  # pylint: disable=pointless-statement, no-member
     with check_mongo_calls(0):
         self.ccx.due  # pylint: disable=pointless-statement, no-member
Ejemplo n.º 7
0
    def test_self_get_grade(self):
        """
        Test that a user can successfully request her own grade.
        """
        with check_mongo_calls(6):
            resp = self.client.get(self.get_url(self.student.username))
            self.assertEqual(resp.status_code, status.HTTP_200_OK)

        # redo with block structure now in the cache
        with check_mongo_calls(3):
            resp = self.client.get(self.get_url(self.student.username))
            self.assertEqual(resp.status_code, status.HTTP_200_OK)
Ejemplo n.º 8
0
    def test_course_listing_performance(self):
        """
        Create large number of courses and give access of some of these courses to the user and
        compare the time to fetch accessible courses for the user through traversing all courses and
        reversing django groups
        """
        # create list of random course numbers which will be accessible to the user
        user_course_ids = random.sample(range(TOTAL_COURSES_COUNT), USER_COURSES_COUNT)

        # create courses and assign those to the user which have their number in user_course_ids
        for number in range(TOTAL_COURSES_COUNT):
            org = "Org{0}".format(number)
            course = "Course{0}".format(number)
            run = "Run{0}".format(number)
            course_location = SlashSeparatedCourseKey(org, course, run)
            if number in user_course_ids:
                self._create_course_with_access_groups(course_location, self.user)
            else:
                self._create_course_with_access_groups(course_location)

        # time the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_1:
            courses_list = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_2:
            courses_list = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_1:
            courses_list = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_2:
            courses_list = _accessible_courses_list_from_groups(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # test that the time taken by getting courses through reversing django groups is lower then the time
        # taken by traversing through all courses (if accessible courses are relatively small)
        self.assertGreaterEqual(iteration_over_courses_time_1.elapsed, iteration_over_groups_time_1.elapsed)
        self.assertGreaterEqual(iteration_over_courses_time_2.elapsed, iteration_over_groups_time_2.elapsed)

        # Now count the db queries
        store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
        with check_mongo_calls(store.collection, USER_COURSES_COUNT):
            courses_list = _accessible_courses_list_from_groups(self.request)

        with check_mongo_calls(store.collection, 1):
            courses_list = _accessible_courses_list(self.request)
Ejemplo n.º 9
0
 def test_query_counts_with_feature_flag(self, default_store, feature_flag):
     PersistentGradesEnabledFlag.objects.create(enabled=feature_flag)
     with self.store.default_store(default_store):
         self.set_up_course()
         with check_mongo_calls(0):
             with self.assertNumQueries(3 if feature_flag else 2):
                 self._apply_recalculate_subsection_grade()
Ejemplo n.º 10
0
 def test_missing_kwargs(self, kwarg, expected_mongo_calls, expected_sql_calls):
     self.set_up_course()
     del self.score_changed_kwargs[kwarg]
     with patch('lms.djangoapps.grades.signals.log') as log_mock:
         with check_mongo_calls(expected_mongo_calls) and self.assertNumQueries(expected_sql_calls):
             recalculate_subsection_grade_handler(None, **self.score_changed_kwargs)
         self.assertEqual(log_mock.exception.called, kwarg not in ['points_possible', 'points_earned'])
Ejemplo n.º 11
0
 def check_index_page_with_query_count(self, separate_archived_courses, org, mongo_queries, sql_queries):
     """
     Checks the index page, and ensures the number of database queries is as expected.
     """
     with self.assertNumQueries(sql_queries):
         with check_mongo_calls(mongo_queries):
             self.check_index_page(separate_archived_courses=separate_archived_courses, org=org)
Ejemplo n.º 12
0
 def inner(self, default_store, module_count, mongo_calls, sql_queries, *args, **kwargs):
     with modulestore().default_store(default_store):
         self.set_up_course(module_count=module_count)
         self.clear_caches()
         with self.assertNumQueries(sql_queries):
             with check_mongo_calls(mongo_calls):
                 func(self, *args, **kwargs)
Ejemplo n.º 13
0
    def test_staff_course_listing(self, default_store, mongo_calls):
        """
        Create courses and verify they take certain amount of mongo calls to call get_courses_accessible_to_user.
        Also verify that fetch accessible courses list for staff user returns CourseSummary instances.
        """

        # Assign & verify staff role to the user
        GlobalStaff().add_users(self.user)
        self.assertTrue(GlobalStaff().has_user(self.user))

        with self.store.default_store(default_store):
            # Create few courses
            for num in xrange(TOTAL_COURSES_COUNT):
                course_location = self.store.make_course_key('Org', 'CreatedCourse' + str(num), 'Run')
                self._create_course_with_access_groups(course_location, self.user, default_store)

        # Fetch accessible courses list & verify their count
        courses_list_by_staff, __ = get_courses_accessible_to_user(self.request)
        self.assertEqual(len(courses_list_by_staff), TOTAL_COURSES_COUNT)

        # Verify fetched accessible courses list is a list of CourseSummery instances
        self.assertTrue(all(isinstance(course, CourseSummary) for course in courses_list_by_staff))

        # Now count the db queries for staff
        with check_mongo_calls(mongo_calls):
            _accessible_courses_summary_list(self.request)
Ejemplo n.º 14
0
 def test_persistent_grades_not_enabled_on_course(self, default_store):
     with self.store.default_store(default_store):
         self.set_up_course(enable_persistent_grades=False)
         self.assertFalse(PersistentGradesEnabledFlag.feature_enabled(self.course.id))
         with check_mongo_calls(0):
             with self.assertNumQueries(0):
                 self._apply_recalculate_subsection_grade()
Ejemplo n.º 15
0
 def test_request_instructor_courses_using_scope(self):
     CourseInstructorRole(self.course_key).add_users(self.user)
     with check_mongo_calls(0):
         claims = self.get_with_scope('course_instructor')
     courses = claims['instructor_courses']
     self.assertIn(self.course_id, courses)
     self.assertEqual(len(courses), 1)
Ejemplo n.º 16
0
    def test_number_of_mongo_queries(self, default_store, num_thread_responses, num_mongo_calls, mock_request):

        with modulestore().default_store(default_store):
            course = CourseFactory.create()

        student = UserFactory.create()
        CourseEnrollmentFactory.create(user=student, course_id=course.id)

        test_thread_id = "test_thread_id"
        mock_request.side_effect = make_mock_request_impl(
            "dummy content",
            test_thread_id,
            num_thread_responses=num_thread_responses,
        )
        request = RequestFactory().get(
            "dummy_url",
            HTTP_X_REQUESTED_WITH="XMLHttpRequest"
        )
        request.user = student
        with check_mongo_calls(num_mongo_calls):
            response = views.single_thread(
                request,
                course.id.to_deprecated_string(),
                "dummy_discussion_id",
                test_thread_id
            )
            self.assertEquals(response.status_code, 200)
            self.assertEquals(len(json.loads(response.content)["content"]["children"]), num_thread_responses)
Ejemplo n.º 17
0
 def test_query_counts(self, default_store, num_mongo_calls, num_sql_calls, create_multiple_subsections):
     with self.store.default_store(default_store):
         self.set_up_course(create_multiple_subsections=create_multiple_subsections)
         self.assertTrue(PersistentGradesEnabledFlag.feature_enabled(self.course.id))
         with check_mongo_calls(num_mongo_calls):
             with self.assertNumQueries(num_sql_calls):
                 self._apply_recalculate_subsection_grade()
    def instrument_course_progress_render(self, course_width, enable_ccx, queries, reads, xblocks):
        """
        Renders the progress page, instrumenting Mongo reads and SQL queries.
        """
        self.setup_course(course_width, enable_ccx)

        # Switch to published-only mode to simulate the LMS
        with self.settings(MODULESTORE_BRANCH='published-only'):
            # Clear all caches before measuring
            for cache in settings.CACHES:
                get_cache(cache).clear()

            # Refill the metadata inheritance cache
            modulestore().get_course(self.course.id, depth=None)

            # We clear the request cache to simulate a new request in the LMS.
            RequestCache.clear_request_cache()

            # Reset the list of provider classes, so that our django settings changes
            # can actually take affect.
            OverrideFieldData.provider_classes = None

            with self.assertNumQueries(queries):
                with check_mongo_calls(reads):
                    with check_sum_of_calls(XBlock, ['__init__'], xblocks, xblocks, include_arguments=False):
                        self.grade_course(self.course)
Ejemplo n.º 19
0
    def test_course_staff_courses(self):
        CourseStaffRole(self.course_key).add_users(self.user)
        with check_mongo_calls(0):
            scopes, claims = self.get_id_token_values('openid course_staff')

        self.assertIn('course_staff', scopes)
        self.assertNotIn('staff_courses', claims)  # should not return courses in id_token
Ejemplo n.º 20
0
    def test_canonical_asset_path_with_new_style_assets(self, base_url, start, expected, mongo_calls):
        exts = ['.html', '.tm']
        prefix = 'split'
        encoded_base_url = quote_plus('//' + base_url)
        c4x = 'c4x/a/b/asset'
        asset_key = 'asset-v1:a+b+{}+type@asset+block'.format(prefix)
        encoded_asset_key = quote_plus('/asset-v1:a+b+{}+type@asset+block@'.format(prefix))
        th_key = 'asset-v1:a+b+{}+type@thumbnail+block'.format(prefix)
        th_ext = 'png-16x16.jpg'

        start = start.format(
            prfx=prefix,
            c4x=c4x,
            asset=asset_key,
            encoded_base_url=encoded_base_url,
            encoded_asset=encoded_asset_key,
            th_key=th_key,
            th_ext=th_ext
        )
        expected = expected.format(
            prfx=prefix,
            c4x=c4x,
            asset=asset_key,
            encoded_base_url=encoded_base_url,
            encoded_asset=encoded_asset_key,
            th_key=th_key,
            th_ext=th_ext
        )

        with check_mongo_calls(mongo_calls):
            asset_path = StaticContent.get_canonicalized_asset_path(self.courses[prefix].id, start, base_url, exts)
            self.assertEqual(asset_path, expected)
Ejemplo n.º 21
0
    def test_toc_toy_from_section(self, default_ms, setup_finds, setup_sends, toc_finds):
        with self.store.default_store(default_ms):
            self.setup_modulestore(default_ms, setup_finds, setup_sends)
            section = 'Welcome'
            expected = ([{'active': True, 'sections':
                          [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
                            'format': u'Lecture Sequence', 'due': None, 'active': False},
                           {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
                            'format': '', 'due': None, 'active': True},
                           {'url_name': 'video_123456789012', 'display_name': 'Test Video', 'graded': True,
                            'format': '', 'due': None, 'active': False},
                           {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True,
                            'format': '', 'due': None, 'active': False}],
                          'url_name': 'Overview', 'display_name': u'Overview'},
                         {'active': False, 'sections':
                          [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
                            'format': '', 'due': None, 'active': False}],
                          'url_name': 'secret:magic', 'display_name': 'secret:magic'}])

            with check_mongo_calls(toc_finds):
                actual = render.toc_for_course(
                    self.request, self.toy_course, self.chapter, section, self.field_data_cache
                )
            for toc_section in expected:
                self.assertIn(toc_section, actual)
Ejemplo n.º 22
0
 def test_query_count_does_not_change_with_more_problems(self, default_store):
     with self.store.default_store(default_store):
         self.set_up_course()
         ItemFactory.create(parent=self.sequential, category='problem', display_name='problem2')
         ItemFactory.create(parent=self.sequential, category='problem', display_name='problem3')
         with check_mongo_calls(2) and self.assertNumQueries(13):
             recalculate_subsection_grade_handler(None, **self.score_changed_kwargs)
Ejemplo n.º 23
0
    def test_calculate_course_xblocks_data_queries(self, store_type, children_per_block, depth, expected_mongo_calls):

        course = self.create_course_with_blocks(children_per_block, depth, store_type)

        with check_mongo_calls(expected_mongo_calls):
            blocks_data = _calculate_course_xblocks_data(course.id)
            self.assertGreater(len(blocks_data), children_per_block ** depth)
Ejemplo n.º 24
0
    def test_canonical_asset_path_with_new_style_assets(self, base_url, start, expected, mongo_calls):
        exts = ['.html', '.tm']
        prefix = u'split'
        encoded_base_url = urlquote(u'//' + base_url)
        c4x = u'c4x/a/b/asset'
        base_asset_key = u'asset-v1:a+b+{}+type@asset+block'.format(prefix)
        adjusted_asset_key = base_asset_key
        encoded_asset_key = urlquote(u'/asset-v1:a+b+{}+type@asset+block@'.format(prefix))
        encoded_base_asset_key = encoded_asset_key
        base_th_key = u'asset-v1:a+b+{}+type@thumbnail+block'.format(prefix)
        adjusted_th_key = base_th_key
        th_ext = u'png-16x16.jpg'

        start = start.format(
            prfx=prefix,
            c4x=c4x,
            base_asset=base_asset_key,
            asset=adjusted_asset_key,
            encoded_base_url=encoded_base_url,
            encoded_asset=encoded_asset_key,
            base_th_key=base_th_key,
            th_key=adjusted_th_key,
            th_ext=th_ext
        )

        # Adjust for content digest.  This gets dicey quickly and we have to order our steps:
        # - replace format markets because they have curly braces
        # - encode Unicode characters to percent-encoded
        # - finally shove back in our regex patterns
        digest = CanonicalContentTest.get_content_digest_for_asset_path(prefix, start)
        if digest:
            adjusted_asset_key = u'assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@asset+block'.format(prefix)
            adjusted_th_key = u'assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@thumbnail+block'.format(prefix)
            encoded_asset_key = u'/assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@asset+block@'.format(prefix)
            encoded_asset_key = urlquote(encoded_asset_key)

        expected = expected.format(
            prfx=prefix,
            c4x=c4x,
            base_asset=base_asset_key,
            asset=adjusted_asset_key,
            encoded_base_url=encoded_base_url,
            encoded_asset=encoded_asset_key,
            base_th_key=base_th_key,
            th_key=adjusted_th_key,
            th_ext=th_ext,
            encoded_base_asset=encoded_base_asset_key,
        )

        expected = encode_unicode_characters_in_url(expected)
        expected = expected.replace('VMARK', r'v[\d]')
        expected = expected.replace('HMARK', '[a-f0-9]{32}')
        expected = expected.replace('+', r'\+').replace('?', r'\?')

        with check_mongo_calls(mongo_calls):
            asset_path = StaticContent.get_canonicalized_asset_path(self.courses[prefix].id, start, base_url, exts)
            print expected
            print asset_path
            self.assertIsNotNone(re.match(expected, asset_path))
Ejemplo n.º 25
0
 def _assert_role_using_scope(self, scope, claim, assert_one_course=True):
     with check_mongo_calls(0):
         claims = self.get_with_scope(scope)
     self.assertEqual(len(claims), 2)
     courses = claims[claim]
     self.assertIn(self.course_id, courses)
     if assert_one_course:
         self.assertEqual(len(courses), 1)
Ejemplo n.º 26
0
 def test_persistent_grades_enabled_on_course(self, default_store, num_mongo_queries, num_sql_queries):
     with self.store.default_store(default_store):
         self.set_up_course(enable_persistent_grades=True)
         with check_mongo_calls(num_mongo_queries):
             with self.assertNumQueries(num_sql_queries):
                 self._apply_recalculate_subsection_grade()
         self.assertIsNotNone(PersistentCourseGrade.read(self.user.id, self.course.id))
         self.assertGreater(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
Ejemplo n.º 27
0
 def test_query_count_does_not_change_with_more_problems(self, default_store, added_queries):
     with self.store.default_store(default_store):
         self.set_up_course()
         self.assertTrue(PersistentGradesEnabledFlag.feature_enabled(self.course.id))
         ItemFactory.create(parent=self.sequential, category="problem", display_name="problem2")
         ItemFactory.create(parent=self.sequential, category="problem", display_name="problem3")
         with check_mongo_calls(2) and self.assertNumQueries(20 + added_queries):
             self._apply_recalculate_subsection_grade()
Ejemplo n.º 28
0
    def test_course_instructor_courses(self):
        with check_mongo_calls(0):
            CourseInstructorRole(self.course_key).add_users(self.user)

        scopes, claims = self.get_id_token_values('openid course_instructor')

        self.assertIn('course_instructor', scopes)
        self.assertNotIn('instructor_courses', claims)  # should not return courses in id_token
Ejemplo n.º 29
0
    def test_no_special_course_access(self):
        with check_mongo_calls(0):
            scopes, claims = self.get_id_token_values('openid course_instructor course_staff')
        self.assertNotIn('course_staff', scopes)
        self.assertNotIn('staff_courses', claims)

        self.assertNotIn('course_instructor', scopes)
        self.assertNotIn('instructor_courses', claims)
Ejemplo n.º 30
0
 def test_with_unit_graded_for_different_user(self):
     self.create_graded_assignment(self.unit, 'graded_unit', self.outcome_service)
     other_user = UserFactory.create()
     with check_mongo_calls(3):
         assignments = outcomes.get_assignments_for_problem(
             self.unit, other_user.id, self.course.id
         )
     self.assertEqual(len(assignments), 0)
    def test_lazy_when_course_previously_cached(self, store_builder,
                                                num_mongo_calls):
        request_cache = MemoryCache()
        with store_builder.build(
                request_cache=request_cache) as (content_store, modulestore):
            course_key = self._import_course(content_store, modulestore)

            with check_mongo_calls(num_mongo_calls):
                with modulestore.bulk_operations(course_key):
                    # assume the course was retrieved earlier
                    course = modulestore.get_course(course_key,
                                                    depth=0,
                                                    lazy=True)

                    # and then subsequently retrieved with the lazy and depth=None values
                    course = modulestore.get_item(course.location,
                                                  depth=None,
                                                  lazy=False)
                    self._traverse_blocks_in_course(
                        course, access_all_block_fields=True)
Ejemplo n.º 32
0
 def _make_api_call(self,
                    requesting_user,
                    specified_user,
                    org=None,
                    filter_=None,
                    permissions=None):
     """
     Call the list_courses api endpoint to get information about
     `specified_user` on behalf of `requesting_user`.
     """
     request = Request(self.request_factory.get('/'))
     request.user = requesting_user
     with check_mongo_calls(0):
         return list_courses(
             request,
             specified_user.username,
             org=org,
             filter_=filter_,
             permissions=permissions,
         )
Ejemplo n.º 33
0
    def test_success_enrolled_staff(self, default_store, mongo_calls):
        with self.store.default_store(default_store):
            self.setup_course(default_store)
            self.setup_user(admin=True, enroll=True, login=True)

            # The 5 mongoDB calls include calls for
            # Old Mongo:
            #   (1) fill_in_run
            #   (2) get_course in get_course_with_access
            #   (3) get_item for HTML block in get_module_by_usage_id
            #   (4) get_parent when loading HTML block
            #   (5) edx_notes descriptor call to get_course
            # Split:
            #   (1) course_index - bulk_operation call
            #   (2) structure - get_course_with_access
            #   (3) definition - get_course_with_access
            #   (4) definition - HTML block
            #   (5) definition - edx_notes decorator (original_get_html)
            with check_mongo_calls(mongo_calls):
                self.verify_response()
Ejemplo n.º 34
0
    def test_courseware_html(self, block_name, default_store, mongo_calls):
        """
        To verify that the removal of courseware chrome elements is working,
        we include this test here to make sure the chrome elements that should
        be removed actually exist in the full courseware page.
        If this test fails, it's probably because the HTML template for courseware
        has changed and COURSEWARE_CHROME_HTML_ELEMENTS needs to be updated.
        """
        with self.store.default_store(default_store):
            self.block_name_to_be_tested = block_name
            self.setup_course(default_store)
            self.setup_user(admin=True, enroll=True, login=True)

            with check_mongo_calls(mongo_calls):
                url = get_legacy_courseware_url(
                    self.block_to_be_tested.location)
                response = self.client.get(url)
                expected_elements = self.block_specific_chrome_html_elements + self.COURSEWARE_CHROME_HTML_ELEMENTS
                for chrome_element in expected_elements:
                    self.assertContains(response, chrome_element)
Ejemplo n.º 35
0
    def test_path_and_queries_on_create(self, store_type, block_to_bookmark, ancestors_attrs, expected_mongo_calls):
        """
        In case of mongo, 1 query is used to fetch the block, and 2 by path_to_location(), and then
        1 query per parent in path is needed to fetch the parent blocks.
        """

        self.setup_test_data(store_type)
        user = UserFactory.create()

        expected_path = [PathItem(
            usage_key=getattr(self, ancestor_attr).location, display_name=getattr(self, ancestor_attr).display_name
        ) for ancestor_attr in ancestors_attrs]

        bookmark_data = self.get_bookmark_data(getattr(self, block_to_bookmark), user=user)

        with check_mongo_calls(expected_mongo_calls):
            bookmark, __ = Bookmark.create(bookmark_data)

        self.assertEqual(bookmark.path, expected_path)
        self.assertIsNotNone(bookmark.xblock_cache)
        self.assertEqual(bookmark.xblock_cache.paths, [])
Ejemplo n.º 36
0
    def test_course_overview_caching(self, modulestore_type, min_mongo_calls,
                                     max_mongo_calls):
        """
        Tests that CourseOverview structures are actually getting cached.
        """
        course = CourseFactory.create(course="TEST101",
                                      org="edX",
                                      run="Run1",
                                      mobile_available=True,
                                      default_store=modulestore_type)

        # The first time we load a CourseOverview, it will be a cache miss, so
        # we expect the modulestore to be queried.
        with check_mongo_calls_range(max_finds=max_mongo_calls,
                                     min_finds=min_mongo_calls):
            _course_overview_1 = CourseOverview.get_from_id(course.id)

        # The second time we load a CourseOverview, it will be a cache hit, so
        # we expect no modulestore queries to be made.
        with check_mongo_calls(0):
            _course_overview_2 = CourseOverview.get_from_id(course.id)
Ejemplo n.º 37
0
    def test_course_staff_courses_with_claims(self):
        CourseStaffRole(self.course_key).add_users(self.user)

        course_id = unicode(self.course_key)

        nonexistent_course_id = 'some/other/course'

        claims = {
            'staff_courses': {
                'values': [course_id, nonexistent_course_id],
                'essential': True,
            }
        }

        with check_mongo_calls(0):
            scopes, claims = self.get_id_token_values(scope='openid course_staff', claims=claims)

        self.assertIn('course_staff', scopes)
        self.assertIn('staff_courses', claims)
        self.assertEqual(len(claims['staff_courses']), 1)
        self.assertIn(course_id, claims['staff_courses'])
        self.assertNotIn(nonexistent_course_id, claims['staff_courses'])
Ejemplo n.º 38
0
 def test_modulestore_performance(self, store_type, expected_mongo_queries):
     """
     Test that a constant number of mongo calls are made regardless of how
     many grade-related blocks are in the course.
     """
     course = [
         {
             u'org': u'GradesTestOrg',
             u'course': u'GB101',
             u'run': u'cannonball',
             u'metadata': {u'format': u'homework'},
             u'#type': u'course',
             u'#ref': u'course',
             u'#children': [],
         },
     ]
     for problem_number in range(random.randrange(10, 20)):
         course[0][u'#children'].append(
             {
                 u'metadata': {
                     u'graded': True,
                     u'weight': 1,
                     u'due': datetime.datetime(2099, 3, 15, 12, 30, 0, tzinfo=pytz.utc),
                 },
                 u'#type': u'problem',
                 u'#ref': u'problem_{}'.format(problem_number),
                 u'data': u'''
                     <problem>
                         <numericalresponse answer="{number}">
                             <textline label="1*{number}" />
                         </numericalresponse>
                     </problem>'''.format(number=problem_number),
             }
         )
     with self.store.default_store(store_type):
         blocks = self.build_course(course)
     clear_course_from_cache(blocks[u'course'].id)
     with check_mongo_calls(expected_mongo_queries):
         get_course_blocks(self.student, blocks[u'course'].location, self.transformers)
Ejemplo n.º 39
0
    def test_number_mongo_calls(self, store, depth, lazy, access_all_block_fields, num_mongo_calls):
        with store.build() as (source_content, source_store):

            source_course_key = source_store.make_course_key('a', 'course', 'course')

            # First, import a course.
            import_from_xml(
                source_store,
                'test_user',
                TEST_DATA_DIR,
                course_dirs=['manual-testing-complete'],
                static_content_store=source_content,
                target_course_id=source_course_key,
                create_course_if_not_present=True,
                raise_on_failure=True,
            )

            # Course traversal modeled after the traversal done here:
            # lms/djangoapps/mobile_api/video_outlines/serializers.py:BlockOutline
            # Starting at the root course block, do a breadth-first traversal using
            # get_children() to retrieve each block's children.
            with check_mongo_calls(num_mongo_calls):
                with source_store.bulk_operations(source_course_key):
                    start_block = source_store.get_course(source_course_key, depth=depth, lazy=lazy)
                    all_blocks = []
                    stack = [start_block]
                    while stack:
                        curr_block = stack.pop()
                        all_blocks.append(curr_block)
                        if curr_block.has_children:
                            for block in reversed(curr_block.get_children()):
                                stack.append(block)

                    if access_all_block_fields:
                        # Read the fields on each block in order to ensure each block and its definition is loaded.
                        for xblock in all_blocks:
                            for __, field in xblock.fields.iteritems():
                                if field.is_set_on(xblock):
                                    __ = field.read_from(xblock)
Ejemplo n.º 40
0
    def test_user_randomly_assigned(self):
        # user was randomly assigned to one of the groups
        user_groups = get_user_partition_groups(
            self.course.id, [self.split_test_user_partition], self.user, 'id')
        self.assertEquals(len(user_groups), 1)

        # calling twice should result in the same block set
        block_structure1 = get_course_blocks(
            self.user,
            self.course.location,
            self.transformers,
        )
        with check_mongo_calls(0):
            block_structure2 = get_course_blocks(
                self.user,
                self.course.location,
                self.transformers,
            )
        self.assertEqual(
            set(block_structure1.get_block_keys()),
            set(block_structure2.get_block_keys()),
        )
Ejemplo n.º 41
0
    def test_user_randomly_assigned(self):
        # user was randomly assigned to one of the groups
        user_groups = _get_user_partition_groups(  # pylint: disable=protected-access
            self.course.id, [self.split_test_user_partition], self.user)
        self.assertEquals(len(user_groups), 1)

        # calling twice should result in the same block set
        with check_mongo_calls_range(min_finds=1):
            block_structure1 = get_course_blocks(
                self.user,
                self.course.location,
                transformers={self.transformer},
            )
        with check_mongo_calls(0):
            block_structure2 = get_course_blocks(
                self.user,
                self.course.location,
                transformers={self.transformer},
            )
        self.assertEqual(
            set(block_structure1.get_block_keys()),
            set(block_structure2.get_block_keys()),
        )
Ejemplo n.º 42
0
    def test_get(self):
        with check_mongo_calls(3):
            response = super(CourseBlocksOrNavigationTestMixin, self).test_get()

        # verify root element
        self.assertIn('root', response.data)
        root_string = unicode(self.course.location)
        self.assertEquals(response.data['root'], root_string)

        # verify ~blocks element
        self.assertTrue(self.block_navigation_view_type in response.data)
        blocks = response.data[self.block_navigation_view_type]

        # verify number of blocks
        self.assertEquals(len(blocks), 4)

        # verify fields in blocks
        for field, block in product(self.block_fields, blocks.values()):
            self.assertIn(field, block)

        # verify container fields in container blocks
        for field in self.container_fields:
            self.assertIn(field, blocks[root_string])
Ejemplo n.º 43
0
    def test_toc_toy_from_chapter(self, default_ms, num_finds, num_sends):
        with self.store.default_store(default_ms):
            self.setup_modulestore(default_ms, num_finds, num_sends)
            expected = ([{'active': True, 'sections':
                          [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
                            'format': u'Lecture Sequence', 'due': None, 'active': False},
                           {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
                            'format': '', 'due': None, 'active': False},
                           {'url_name': 'video_123456789012', 'display_name': 'Test Video', 'graded': True,
                            'format': '', 'due': None, 'active': False},
                           {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True,
                            'format': '', 'due': None, 'active': False}],
                          'url_name': 'Overview', 'display_name': u'Overview'},
                         {'active': False, 'sections':
                          [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
                            'format': '', 'due': None, 'active': False}],
                          'url_name': 'secret:magic', 'display_name': 'secret:magic'}])

            with check_mongo_calls(self.modulestore, 0, 0):
                actual = render.toc_for_course(
                    self.request.user, self.request, self.toy_course, self.chapter, None, self.field_data_cache
                )
        for toc_section in expected:
            self.assertIn(toc_section, actual)
Ejemplo n.º 44
0
 def test_subsection_grade_updated_on_signal(self, default_store):
     with self.store.default_store(default_store):
         self.set_up_course()
         with check_mongo_calls(2) and self.assertNumQueries(13):
             recalculate_subsection_grade_handler(None, **self.score_changed_kwargs)
Ejemplo n.º 45
0
    def _create_course(self):
        """
        Create the course, publish all verticals
        * some detached items
        """
        # There are 12 created items and 7 parent updates
        # create course: finds: 1 to verify uniqueness, 1 to find parents
        # sends: 1 to create course, 1 to create overview
        with check_mongo_calls(5, 2):
            super(TestPublish, self)._create_course(split=False)  # 2 inserts (course and overview)

        # with bulk will delay all inheritance computations which won't be added into the mongo_calls
        with self.draft_mongo.bulk_operations(self.old_course_key):
            # finds: 1 for parent to add child and 2 to get ancestors
            # sends: 1 for insert, 1 for parent (add child)
            with check_mongo_calls(3, 2):
                self._create_item('chapter', 'Chapter1', {}, {'display_name': 'Chapter 1'}, 'course', 'runid', split=False)

            with check_mongo_calls(4, 2):
                self._create_item('chapter', 'Chapter2', {}, {'display_name': 'Chapter 2'}, 'course', 'runid', split=False)
            # For each vertical (2) created:
            #   - load draft
            #   - load non-draft
            #   - get last error
            #   - load parent
            #   - get ancestors
            #   - load inheritable data
            with check_mongo_calls(15, 6):
                self._create_item('vertical', 'Vert1', {}, {'display_name': 'Vertical 1'}, 'chapter', 'Chapter1', split=False)
                self._create_item('vertical', 'Vert2', {}, {'display_name': 'Vertical 2'}, 'chapter', 'Chapter1', split=False)
            # For each (4) item created
            #   - try to find draft
            #   - try to find non-draft
            #   - retrieve draft of new parent
            #   - get last error
            #   - load parent
            #   - load inheritable data
            #   - load parent
            #   - load ancestors
            # count for updates increased to 16 b/c of edit_info updating
            with check_mongo_calls(40, 16):
                self._create_item('html', 'Html1', "<p>Goodbye</p>", {'display_name': 'Parented Html'}, 'vertical', 'Vert1', split=False)
                self._create_item(
                    'discussion', 'Discussion1',
                    "discussion discussion_category=\"Lecture 1\" discussion_id=\"a08bfd89b2aa40fa81f2c650a9332846\" discussion_target=\"Lecture 1\"/>\n",
                    {
                        "discussion_category": "Lecture 1",
                        "discussion_target": "Lecture 1",
                        "display_name": "Lecture 1 Discussion",
                        "discussion_id": "a08bfd89b2aa40fa81f2c650a9332846"
                    },
                    'vertical', 'Vert1',
                    split=False
                )
                self._create_item('html', 'Html2', "<p>Hello</p>", {'display_name': 'Hollow Html'}, 'vertical', 'Vert1', split=False)
                self._create_item(
                    'discussion', 'Discussion2',
                    "discussion discussion_category=\"Lecture 2\" discussion_id=\"b08bfd89b2aa40fa81f2c650a9332846\" discussion_target=\"Lecture 2\"/>\n",
                    {
                        "discussion_category": "Lecture 2",
                        "discussion_target": "Lecture 2",
                        "display_name": "Lecture 2 Discussion",
                        "discussion_id": "b08bfd89b2aa40fa81f2c650a9332846"
                    },
                    'vertical', 'Vert2',
                    split=False
                )

            with check_mongo_calls(2, 2):
                # 2 finds b/c looking for non-existent parents
                self._create_item('static_tab', 'staticuno', "<p>tab</p>", {'display_name': 'Tab uno'}, None, None, split=False)
                self._create_item('course_info', 'updates', "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {}, None, None, split=False)
Ejemplo n.º 46
0
    def test_course_listing_performance(self, store,
                                        courses_list_from_group_calls,
                                        courses_list_calls):
        """
        Create large number of courses and give access of some of these courses to the user and
        compare the time to fetch accessible courses for the user through traversing all courses and
        reversing django groups
        """
        # create list of random course numbers which will be accessible to the user
        user_course_ids = random.sample(range(TOTAL_COURSES_COUNT),
                                        USER_COURSES_COUNT)

        # create courses and assign those to the user which have their number in user_course_ids
        with self.store.default_store(store):
            for number in range(TOTAL_COURSES_COUNT):
                org = 'Org{0}'.format(number)
                course = 'Course{0}'.format(number)
                run = 'Run{0}'.format(number)
                course_location = self.store.make_course_key(org, course, run)
                if number in user_course_ids:
                    self._create_course_with_access_groups(course_location,
                                                           self.user,
                                                           store=store)
                else:
                    self._create_course_with_access_groups(course_location,
                                                           store=store)

        # time the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_1:
            courses_iter, __ = _accessible_courses_iter(self.request)
        self.assertEqual(len(list(courses_iter)), USER_COURSES_COUNT)

        # time again the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_2:
            courses_iter, __ = _accessible_courses_iter(self.request)
        self.assertEqual(len(list(courses_iter)), USER_COURSES_COUNT)

        # time the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_1:
            courses_list, __ = _accessible_courses_list_from_groups(
                self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_2:
            courses_list, __ = _accessible_courses_list_from_groups(
                self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # TODO (cdyer) : iteration over courses was optimized, and is now
        # sometimes faster than iteration over groups. One of the following
        # should be done to resolve this:
        # * Iteration over groups should be sped up.
        # * Iteration over groups should be removed, as it no longer saves time.
        # * Or this part of the test should be removed.

        # Test that the time taken by getting courses through reversing django
        # groups is lower then the time taken by traversing through all courses
        # (if accessible courses are relatively small).
        #self.assertGreaterEqual(iteration_over_courses_time_1.elapsed, iteration_over_groups_time_1.elapsed)
        #self.assertGreaterEqual(iteration_over_courses_time_2.elapsed, iteration_over_groups_time_2.elapsed)

        # Now count the db queries
        with check_mongo_calls(courses_list_from_group_calls):
            _accessible_courses_list_from_groups(self.request)

        with check_mongo_calls(courses_list_calls):
            list(_accessible_courses_iter(self.request))
Ejemplo n.º 47
0
    def test_query_counts(self):
        self.add_credit_course(self.course.id)
        self.add_icrv_xblock()

        with check_mongo_calls(3):
            listen_for_course_publish(self, self.course.id)
Ejemplo n.º 48
0
 def test_get_course_func_with_access(self, course_access_func_name, num_mongo_calls):
     course_access_func = self.COURSE_ACCESS_FUNCS[course_access_func_name]
     user = UserFactory.create()
     course = CourseFactory.create(emit_signals=True)
     with check_mongo_calls(num_mongo_calls):
         course_access_func(user, 'load', course.id)
Ejemplo n.º 49
0
 def test_get_course_func_with_access(self, course_access_func,
                                      num_mongo_calls):
     user = UserFactory.create()
     course = CourseFactory.create(emit_signals=True)
     with check_mongo_calls(num_mongo_calls):
         course_access_func(user, 'load', course.id)
Ejemplo n.º 50
0
 def test_queries(self):
     # Fetch the view and verify that the query counts haven't changed
     with self.assertNumQueries(34):
         with check_mongo_calls(4):
             url = course_updates_url(self.course)
             self.client.get(url)
Ejemplo n.º 51
0
 def test_path_to_location(self):
     '''Make sure that path_to_location works'''
     with check_mongo_calls(self.draft_store, 9):
         check_path_to_location(self.draft_store)
    def test_canonical_asset_path_with_new_style_assets(
            self, base_url, start, expected, mongo_calls):
        exts = ['.html', '.tm']
        prefix = u'split'
        encoded_base_url = urlquote(u'//' + base_url)
        c4x = u'c4x/a/b/asset'
        base_asset_key = u'asset-v1:a+b+{}+type@asset+block'.format(prefix)
        adjusted_asset_key = base_asset_key
        encoded_asset_key = urlquote(
            u'/asset-v1:a+b+{}+type@asset+block@'.format(prefix))
        encoded_base_asset_key = encoded_asset_key
        base_th_key = u'asset-v1:a+b+{}+type@thumbnail+block'.format(prefix)
        adjusted_th_key = base_th_key
        th_ext = u'png-16x16.jpg'

        start = start.format(prfx=prefix,
                             c4x=c4x,
                             base_asset=base_asset_key,
                             asset=adjusted_asset_key,
                             encoded_base_url=encoded_base_url,
                             encoded_asset=encoded_asset_key,
                             base_th_key=base_th_key,
                             th_key=adjusted_th_key,
                             th_ext=th_ext)

        # Adjust for content digest.  This gets dicey quickly and we have to order our steps:
        # - replace format markets because they have curly braces
        # - encode Unicode characters to percent-encoded
        # - finally shove back in our regex patterns
        digest = CanonicalContentTest.get_content_digest_for_asset_path(
            prefix, start)
        if digest:
            adjusted_asset_key = u'assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@asset+block'.format(
                prefix)
            adjusted_th_key = u'assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@thumbnail+block'.format(
                prefix)
            encoded_asset_key = u'/assets/courseware/VMARK/HMARK/asset-v1:a+b+{}+type@asset+block@'.format(
                prefix)
            encoded_asset_key = urlquote(encoded_asset_key)

        expected = expected.format(
            prfx=prefix,
            c4x=c4x,
            base_asset=base_asset_key,
            asset=adjusted_asset_key,
            encoded_base_url=encoded_base_url,
            encoded_asset=encoded_asset_key,
            base_th_key=base_th_key,
            th_key=adjusted_th_key,
            th_ext=th_ext,
            encoded_base_asset=encoded_base_asset_key,
        )

        expected = encode_unicode_characters_in_url(expected)
        expected = expected.replace('VMARK', r'v[\d]')
        expected = expected.replace('HMARK', '[a-f0-9]{32}')
        expected = expected.replace('+', r'\+').replace('?', r'\?')

        with check_mongo_calls(mongo_calls):
            asset_path = StaticContent.get_canonicalized_asset_path(
                self.courses[prefix].id, start, base_url, exts)
            print expected
            print asset_path
            self.assertIsNotNone(re.match(expected, asset_path))
Ejemplo n.º 53
0
 def test_basic(self):
     course = self.create_course()
     CourseDetails.update_about_video(course, 'test_youtube_id', self.staff_user.id)  # pylint: disable=no-member
     with check_mongo_calls(self.expected_mongo_calls):
         result = self._get_result(course)
     self.assertDictEqual(result, self.expected_data)
 def assertMongoCallCount(self, calls):
     """
     Assert that mongodb is queried ``calls`` times in the surrounded
     context.
     """
     return check_mongo_calls(calls)
Ejemplo n.º 55
0
 def test_with_no_graded_assignments(self):
     with check_mongo_calls(3):
         assignments = outcomes.get_assignments_for_problem(
             self.unit, self.user_id, self.course.id)
     self.assertEqual(len(assignments), 0)
Ejemplo n.º 56
0
    def test_course_listing_performance(self):
        """
        Create large number of courses and give access of some of these courses to the user and
        compare the time to fetch accessible courses for the user through traversing all courses and
        reversing django groups
        """
        # create list of random course numbers which will be accessible to the user
        user_course_ids = random.sample(range(TOTAL_COURSES_COUNT),
                                        USER_COURSES_COUNT)

        # create courses and assign those to the user which have their number in user_course_ids
        for number in range(TOTAL_COURSES_COUNT):
            org = 'Org{0}'.format(number)
            course = 'Course{0}'.format(number)
            run = 'Run{0}'.format(number)
            course_location = self.store.make_course_key(org, course, run)
            if number in user_course_ids:
                self._create_course_with_access_groups(course_location,
                                                       self.user)
            else:
                self._create_course_with_access_groups(course_location)

        # time the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_1:
            courses_list, __ = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by iterating through all courses
        with Timer() as iteration_over_courses_time_2:
            courses_list, __ = _accessible_courses_list(self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_1:
            courses_list, __ = _accessible_courses_list_from_groups(
                self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # time again the get courses by reversing django groups
        with Timer() as iteration_over_groups_time_2:
            courses_list, __ = _accessible_courses_list_from_groups(
                self.request)
        self.assertEqual(len(courses_list), USER_COURSES_COUNT)

        # test that the time taken by getting courses through reversing django groups is lower then the time
        # taken by traversing through all courses (if accessible courses are relatively small)
        self.assertGreaterEqual(iteration_over_courses_time_1.elapsed,
                                iteration_over_groups_time_1.elapsed)
        self.assertGreaterEqual(iteration_over_courses_time_2.elapsed,
                                iteration_over_groups_time_2.elapsed)

        # Now count the db queries
        with check_mongo_calls(USER_COURSES_COUNT):
            _accessible_courses_list_from_groups(self.request)

        # Calls:
        #    1) query old mongo
        #    2) get_more on old mongo
        #    3) query split (but no courses so no fetching of data)
        with check_mongo_calls(3):
            _accessible_courses_list(self.request)
Ejemplo n.º 57
0
 def test_bulk_response(self, modules_count, module_store, mongo_calls,
                        topics):
     course_url = self.create_course(modules_count, module_store, topics)
     with check_mongo_calls(mongo_calls):
         with modulestore().default_store(module_store):
             self.client.get(course_url)
Ejemplo n.º 58
0
 def test_subsection_grades_not_enabled_on_course(self, default_store):
     with self.store.default_store(default_store):
         self.set_up_course(enable_subsection_grades=False)
         with check_mongo_calls(2) and self.assertNumQueries(0):
             recalculate_subsection_grade_handler(None, **self.score_changed_kwargs)
Ejemplo n.º 59
0
    def test_spoc_gradebook_mongo_calls(self):
        """
        Test that the MongoDB cache is used in API to return grades
        """
        # prepare course structure
        course = ItemFactory.create(
            parent_location=self.course.location,
            category="course",
            display_name="Test course",
        )

        students = []
        for i in range(20):
            username = "******" % i
            student = UserFactory.create(username=username)
            CourseEnrollmentFactory.create(user=student,
                                           course_id=self.course.id)
            students.append(student)

        chapter = ItemFactory.create(
            parent=course,
            category='chapter',
            display_name="Chapter",
            publish_item=True,
            start=datetime.datetime(2015, 3, 1, tzinfo=UTC),
        )
        sequential = ItemFactory.create(
            parent=chapter,
            category='sequential',
            display_name="Lesson",
            publish_item=True,
            start=datetime.datetime(2015, 3, 1, tzinfo=UTC),
            metadata={
                'graded': True,
                'format': 'Homework'
            },
        )
        vertical = ItemFactory.create(
            parent=sequential,
            category='vertical',
            display_name='Subsection',
            publish_item=True,
            start=datetime.datetime(2015, 4, 1, tzinfo=UTC),
        )
        for i in range(10):
            problem = ItemFactory.create(
                category="problem",
                parent=vertical,
                display_name="A Problem Block %d" % i,
                weight=1,
                publish_item=False,
                metadata={'rerandomize': 'always'},
            )
            for j in students:
                grade = i % 2
                StudentModuleFactory.create(grade=grade,
                                            max_grade=1,
                                            student=j,
                                            course_id=self.course.id,
                                            module_state_key=problem.location)

        # check MongoDB calls count
        url = reverse('spoc_gradebook', kwargs={'course_id': self.course.id})
        with check_mongo_calls(7):
            response = self.client.get(url)
            assert response.status_code == 200
Ejemplo n.º 60
0
 def test_score_changed_sent_with_feature_flag(self, default_store, feature_flag):
     with patch.dict('django.conf.settings.FEATURES', {'ENABLE_SUBSECTION_GRADES_SAVED': feature_flag}):
         with self.store.default_store(default_store):
             self.set_up_course()
             with check_mongo_calls(0) and self.assertNumQueries(19 if feature_flag else 1):
                 SCORE_CHANGED.send(sender=None, **self.score_changed_kwargs)