Example #1
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse(
            'ccx_grades_csv',
            kwargs={'course_id': self.ccx_key}
        )
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Are the grades downloaded as an attachment?
        self.assertEqual(
            response['content-disposition'],
            'attachment'
        )
        rows = response.content.strip().split('\r')
        headers = rows[0]

        # picking first student records
        data = dict(zip(headers.strip().split(','), rows[1].strip().split(',')))
        self.assertNotIn('HW 04', data)
        self.assertEqual(data['HW 01'], '0.75')
        self.assertEqual(data['HW 02'], '0.5')
        self.assertEqual(data['HW 03'], '0.25')
        self.assertEqual(data['HW Avg'], '0.5')
    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)
Example #3
0
    def test_request_context_caching(self):
        """
        Test that the RequestContext is cached in the RequestCache.
        """
        with patch('edxmako.request_context.get_current_request',
                   return_value=None):
            # requestcontext should be None, because the cache isn't filled
            self.assertIsNone(get_template_request_context())

        with patch('edxmako.request_context.get_current_request',
                   return_value=self.request):
            # requestcontext should not be None, and should fill the cache
            self.assertIsNotNone(get_template_request_context())

        mock_get_current_request = Mock()
        with patch('edxmako.request_context.get_current_request',
                   mock_get_current_request):
            # requestcontext should not be None, because the cache is filled
            self.assertIsNotNone(get_template_request_context())
        mock_get_current_request.assert_not_called()

        RequestCache.clear_request_cache()

        with patch('edxmako.request_context.get_current_request',
                   return_value=None):
            # requestcontext should be None, because the cache isn't filled
            self.assertIsNone(get_template_request_context())
Example #4
0
    def test_course_waffle_flag(self, data):
        """
        Tests various combinations of a flag being set in waffle and overridden
        for a course.
        """
        RequestCache.clear_request_cache()

        with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
            with override_flag(self.NAMESPACED_FLAG_NAME, active=data['waffle_enabled']):
                # check twice to test that the result is properly cached
                self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY), data['result'])
                self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY), data['result'])
                # result is cached, so override check should happen once
                WaffleFlagCourseOverrideModel.override_value.assert_called_once_with(
                    self.NAMESPACED_FLAG_NAME,
                    self.TEST_COURSE_KEY
                )

        # check flag for a second course
        if data['course_override'] == WaffleFlagCourseOverrideModel.ALL_CHOICES.unset:
            # When course override wasn't set for the first course, the second course will get the same
            # cached value from waffle.
            self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_2_KEY), data['waffle_enabled'])
        else:
            # When course override was set for the first course, it should not apply to the second
            # course which should get the default value of False.
            self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_2_KEY), False)
Example #5
0
    def test_get_template_path(self):
        """
        Tests to make sure the get_template_path function works as expected.
        """
        # if the current site has associated SiteTheme then get_template_path should return the argument as is.
        with patch(
            "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme",
            Mock(return_value=True),
        ):
            with patch(
                "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite",
                Mock(return_value=True),
            ):
                with patch("microsite_configuration.microsite.TEMPLATES_BACKEND") as mock_microsite_backend:
                    mock_microsite_backend.get_template = Mock(return_value="/microsite/about.html")
                    self.assertEqual(theming_helpers.get_template_path("about.html"), "about.html")

        RequestCache.clear_request_cache()

        # if the current site does not have associated SiteTheme then get_template_path should return microsite override
        with patch(
            "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme",
            Mock(return_value=False),
        ):
            with patch(
                "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite",
                Mock(return_value=True),
            ):
                with patch("microsite_configuration.microsite.TEMPLATES_BACKEND") as mock_microsite_backend:
                    mock_microsite_backend.get_template_path = Mock(return_value="/microsite/about.html")
                    self.assertEqual(theming_helpers.get_template_path("about.html"), "/microsite/about.html")
    def instrument_course_progress_render(
            self, course_width, enable_ccx, view_as_ccx,
            sql_queries, mongo_reads,
    ):
        """
        Renders the progress page, instrumenting Mongo reads and SQL queries.
        """
        course_key = self.setup_course(course_width, enable_ccx, view_as_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:
                caches[cache].clear()

            # Refill the metadata inheritance cache
            get_course_in_cache(course_key)

            # 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(sql_queries, using='default'):
                with self.assertNumQueries(0, using='student_module_history'):
                    with self.assertMongoCallCount(mongo_reads):
                        with self.assertXBlockInstantiations(1):
                            self.grade_course(course_key)
Example #7
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse(
            'ccx_grades_csv',
            kwargs={'course_id': self.ccx_key}
        )
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Are the grades downloaded as an attachment?
        self.assertEqual(
            response['content-disposition'],
            'attachment'
        )
        rows = response.content.strip().split('\r')
        headers = rows[0]

        # picking first student records
        data = dict(zip(headers.strip().split(','), rows[1].strip().split(',')))
        self.assertNotIn('HW 04', data)
        self.assertEqual(data['HW 01'], '0.75')
        self.assertEqual(data['HW 02'], '0.5')
        self.assertEqual(data['HW 03'], '0.25')
        self.assertEqual(data['HW Avg'], '0.5')
Example #8
0
    def test_request_cached_with_caches_despite_changing_wrapped_result(self):
        """
        Ensure that after caching a result, we always send it back, even if the underlying result changes.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [1, 2, 3]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)
        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        direct_result = mock_wrapper()
        self.assertEqual(direct_result, 2)
        self.assertEqual(to_be_wrapped.call_count, 2)

        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 2)

        direct_result = mock_wrapper()
        self.assertEqual(direct_result, 3)
        self.assertEqual(to_be_wrapped.call_count, 3)
Example #9
0
    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)
Example #10
0
    def test_request_cached_mixed_unicode_str_args(self):
        """
        Ensure that request_cached can work with mixed str and Unicode parameters.
        """
        RequestCache.clear_request_cache()

        def dummy_function(arg1, arg2):
            """
            A dummy function that expects an str and unicode arguments.
            """
            assert isinstance(arg1,
                              str), 'First parameter has to be of type `str`'
            assert isinstance(
                arg2, unicode), 'Second parameter has to be of type `unicode`'
            return True

        self.assertTrue(dummy_function('Hello', u'World'),
                        'Should be callable with ASCII chars')
        self.assertTrue(dummy_function('H∂llå', u'Wørld'),
                        'Should be callable with non-ASCII chars')

        wrapped = request_cached(dummy_function)

        self.assertTrue(wrapped('Hello', u'World'),
                        'Wrapper should handle ASCII only chars')
        self.assertTrue(wrapped('H∂llå', u'Wørld'),
                        'Wrapper should handle non-ASCII chars')
Example #11
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse('ccx_grades_csv', kwargs={'course_id': self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Are the grades downloaded as an attachment?
        self.assertEqual(response['content-disposition'], 'attachment')
        rows = response.content.strip().split('\r')
        headers = rows[0]

        records = dict()
        for i in range(1, len(rows)):
            data = dict(
                zip(headers.strip().split(','), rows[i].strip().split(',')))
            records[data['username']] = data

        student_data = records[self.student.username]  # pylint: disable=no-member

        self.assertNotIn('HW 04', student_data)
        self.assertEqual(student_data['HW 01'], '0.75')
        self.assertEqual(student_data['HW 02'], '0.5')
        self.assertEqual(student_data['HW 03'], '0.25')
        self.assertEqual(student_data['HW Avg'], '0.5')
Example #12
0
 def test_setting_override(self, is_enabled, override_choice,
                           expected_result):
     RequestCache.clear_request_cache()
     self.set_waffle_course_override(override_choice, is_enabled)
     override_value = WaffleFlagCourseOverrideModel.override_value(
         self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY)
     self.assertEqual(override_value, expected_result)
Example #13
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse(
            'ccx_grades_csv',
            kwargs={'course_id': self.ccx_key}
        )
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Are the grades downloaded as an attachment?
        self.assertEqual(
            response['content-disposition'],
            'attachment'
        )
        rows = response.content.strip().split('\r')
        headers = rows[0]

        records = dict()
        for i in range(1, len(rows)):
            data = dict(zip(headers.strip().split(','), rows[i].strip().split(',')))
            records[data['username']] = data

        student_data = records[self.student.username]  # pylint: disable=no-member

        self.assertNotIn('HW 04', student_data)
        self.assertEqual(student_data['HW 01'], '0.75')
        self.assertEqual(student_data['HW 02'], '0.5')
        self.assertEqual(student_data['HW 03'], '0.25')
        self.assertEqual(student_data['HW Avg'], '0.5')
Example #14
0
    def test_undefined_waffle_flag(self, data):
        """
        Test flag with various defaults provided for undefined waffle flags.
        """
        RequestCache.clear_request_cache()

        test_course_flag = CourseWaffleFlag(
            self.TEST_NAMESPACE,
            self.FLAG_NAME,
            flag_undefined_default=data['flag_undefined_default']
        )

        with patch.object(
            WaffleFlagCourseOverrideModel,
            'override_value',
            return_value=WaffleFlagCourseOverrideModel.ALL_CHOICES.unset
        ):
            # check twice to test that the result is properly cached
            self.assertEqual(test_course_flag.is_enabled(self.TEST_COURSE_KEY), data['result'])
            self.assertEqual(test_course_flag.is_enabled(self.TEST_COURSE_KEY), data['result'])
            # result is cached, so override check should happen once
            WaffleFlagCourseOverrideModel.override_value.assert_called_once_with(
                self.NAMESPACED_FLAG_NAME,
                self.TEST_COURSE_KEY
            )
Example #15
0
 def test_setting_override(self, is_enabled, override_choice, expected_result):
     RequestCache.clear_request_cache()
     self.set_waffle_course_override(override_choice, is_enabled)
     override_value = WaffleFlagCourseOverrideModel.override_value(
         self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY
     )
     self.assertEqual(override_value, expected_result)
Example #16
0
 def test_setting_override_multiple_times(self):
     RequestCache.clear_request_cache()
     self.set_waffle_course_override(self.OVERRIDE_CHOICES.on)
     self.set_waffle_course_override(self.OVERRIDE_CHOICES.off)
     override_value = WaffleFlagCourseOverrideModel.override_value(
         self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY)
     self.assertEqual(override_value, self.OVERRIDE_CHOICES.off)
Example #17
0
    def test_request_cached_with_caches_despite_changing_wrapped_result(self):
        """
        Ensure that after caching a result, we always send it back, even if the underlying result changes.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [1, 2, 3]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)
        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        direct_result = mock_wrapper()
        self.assertEqual(direct_result, 2)
        self.assertEqual(to_be_wrapped.call_count, 2)

        result = wrapped()
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 2)

        direct_result = mock_wrapper()
        self.assertEqual(direct_result, 3)
        self.assertEqual(to_be_wrapped.call_count, 3)
    def instrument_course_progress_render(
        self,
        course_width,
        enable_ccx,
        view_as_ccx,
        sql_queries,
        mongo_reads,
    ):
        """
        Renders the progress page, instrumenting Mongo reads and SQL queries.
        """
        course_key = self.setup_course(course_width, enable_ccx, view_as_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:
                caches[cache].clear()

            # Refill the metadata inheritance cache
            get_course_in_cache(course_key)

            # 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(sql_queries, using='default'):
                with self.assertNumQueries(0, using='student_module_history'):
                    with self.assertMongoCallCount(mongo_reads):
                        with self.assertXBlockInstantiations(1):
                            self.grade_course(course_key)
Example #19
0
 def test_setting_override_multiple_times(self):
     RequestCache.clear_request_cache()
     self.set_waffle_course_override(self.OVERRIDE_CHOICES.on)
     self.set_waffle_course_override(self.OVERRIDE_CHOICES.off)
     override_value = WaffleFlagCourseOverrideModel.override_value(
         self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY
     )
     self.assertEqual(override_value, self.OVERRIDE_CHOICES.off)
Example #20
0
    def handle(self, *args, **options):  # pylint: disable=unused-argument
        """
        Iterates through each course, serializes them into graphs, and saves
        those graphs to neo4j.
        """
        # first, make sure that there's a valid neo4j configuration
        if settings.NEO4J_CONFIG is None:
            raise CommandError(
                "No neo4j configuration (NEO4J_CONFIG) defined in lms.auth.json."
            )

        auth_params = ["{host}:{https_port}", "{user}", "{password}"]
        authenticate(*[param.format(**settings.NEO4J_CONFIG) for param in auth_params])

        graph = Graph(**settings.NEO4J_CONFIG)

        mss = ModuleStoreSerializer()

        total_number_of_courses = len(mss.all_courses)

        for index, course in enumerate(mss.all_courses):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now exporting %s to neo4j: course %d of %d total courses",
                course.id,
                index + 1,
                total_number_of_courses
            )
            nodes, relationships = mss.serialize_course(course.id)
            log.info(
                "%d nodes and %d relationships in %s",
                len(nodes),
                len(relationships),
                course.id
            )

            transaction = graph.begin()
            try:
                # first, delete existing course
                transaction.run(
                    "MATCH (n:item) WHERE n.course_key='{}' DETACH DELETE n".format(
                        six.text_type(course.id)
                    )
                )

                # now, re-add it
                self.add_to_transaction(nodes, transaction)
                self.add_to_transaction(relationships, transaction)
                transaction.commit()

            except Exception:  # pylint: disable=broad-except
                log.exception(
                    "Error trying to dump course %s to neo4j, rolling back",
                    six.text_type(course.id)
                )
                transaction.rollback()
Example #21
0
    def handle(self, *args, **options):  # pylint: disable=unused-argument
        """
        Iterates through each course, serializes them into graphs, and saves
        those graphs to neo4j.
        """
        host = options['host']
        port = options['port']
        neo4j_user = options['user']
        neo4j_password = options['password']

        authenticate(
            "{host}:{port}".format(host=host, port=port),
            neo4j_user,
            neo4j_password,
        )

        graph = Graph(bolt=True,
                      password=neo4j_password,
                      user=neo4j_user,
                      https_port=port,
                      host=host,
                      secure=True)

        mss = ModuleStoreSerializer()

        total_number_of_courses = len(mss.all_courses)

        for index, course in enumerate(mss.all_courses):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now exporting %s to neo4j: course %d of %d total courses",
                course.id, index + 1, total_number_of_courses)
            nodes, relationships = mss.serialize_course(course.id)
            log.info("%d nodes and %d relationships in %s", len(nodes),
                     len(relationships), course.id)

            transaction = graph.begin()
            try:
                # first, delete existing course
                transaction.run(
                    "MATCH (n:item) WHERE n.course_key='{}' DETACH DELETE n".
                    format(six.text_type(course.id)))

                # now, re-add it
                self.add_to_transaction(nodes, transaction)
                self.add_to_transaction(relationships, transaction)
                transaction.commit()

            except Exception:  # pylint: disable=broad-except
                log.exception(
                    "Error trying to dump course %s to neo4j, rolling back",
                    six.text_type(course.id))
                transaction.rollback()
Example #22
0
def lti_consumer_fields_editing_flag(course_id, enabled_for_course=False):
    """
    Yields CourseEditLTIFieldsEnabledFlag record for unit tests

    Arguments:
        course_id (CourseLocator): course locator to control this feature for.
        enabled_for_course (bool): whether feature is enabled for 'course_id'
    """
    RequestCache.clear_request_cache()
    CourseEditLTIFieldsEnabledFlag.objects.create(course_id=course_id, enabled=enabled_for_course)
    yield
Example #23
0
    def assert_access_to_gated_content(self, user, expected_access):
        """
        Verifies access to gated content for the given user is as expected.
        """
        # clear the request cache to flush any cached access results
        RequestCache.clear_request_cache()

        # access to gating content (seq1) remains constant
        self.assertTrue(bool(has_access(user, 'load', self.seq1, self.course.id)))

        # access to gated content (seq2) is as expected
        self.assertEquals(bool(has_access(user, 'load', self.seq2, self.course.id)), expected_access)
Example #24
0
    def test_gradebook(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse("ccx_gradebook", kwargs={"course_id": self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Max number of student per page is one.  Patched setting MAX_STUDENTS_PER_PAGE_GRADE_BOOK = 1
        self.assertEqual(len(response.mako_context["students"]), 1)  # pylint: disable=no-member
        student_info = response.mako_context["students"][0]  # pylint: disable=no-member
        self.assertEqual(student_info["grade_summary"]["percent"], 0.5)
        self.assertEqual(student_info["grade_summary"]["grade_breakdown"][0]["percent"], 0.5)
        self.assertEqual(len(student_info["grade_summary"]["section_breakdown"]), 4)
Example #25
0
    def test_gradebook(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse('ccx_gradebook', kwargs={'course_id': self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        student_info = response.mako_context['students'][0]  # pylint: disable=no-member
        self.assertEqual(student_info['grade_summary']['percent'], 0.5)
        self.assertEqual(
            student_info['grade_summary']['grade_breakdown'][0]['percent'],
            0.5)
        self.assertEqual(
            len(student_info['grade_summary']['section_breakdown']), 4)
    def handle(self, *args, **options):  # pylint: disable=unused-argument
        """
        Iterates through each course, serializes them into graphs, and saves
        those graphs to neo4j.
        """
        # first, make sure that there's a valid neo4j configuration
        if settings.NEO4J_CONFIG is None:
            raise CommandError(
                "No neo4j configuration (NEO4J_CONFIG) defined in lms.auth.json."
            )

        auth_params = ["{host}:{https_port}", "{user}", "{password}"]
        authenticate(
            *[param.format(**settings.NEO4J_CONFIG) for param in auth_params])

        graph = Graph(**settings.NEO4J_CONFIG)

        mss = ModuleStoreSerializer()

        total_number_of_courses = len(mss.all_courses)

        for index, course in enumerate(mss.all_courses):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now exporting %s to neo4j: course %d of %d total courses",
                course.id, index + 1, total_number_of_courses)
            nodes, relationships = mss.serialize_course(course.id)
            log.info("%d nodes and %d relationships in %s", len(nodes),
                     len(relationships), course.id)

            transaction = graph.begin()
            try:
                # first, delete existing course
                transaction.run(
                    "MATCH (n:item) WHERE n.course_key='{}' DETACH DELETE n".
                    format(six.text_type(course.id)))

                # now, re-add it
                self.add_to_transaction(nodes, transaction)
                self.add_to_transaction(relationships, transaction)
                transaction.commit()

            except Exception:  # pylint: disable=broad-except
                log.exception(
                    "Error trying to dump course %s to neo4j, rolling back",
                    six.text_type(course.id))
                transaction.rollback()
Example #27
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse('ccx_grades_csv', kwargs={'course_id': self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        headers, row = (row.strip().split(',')
                        for row in response.content.strip().split('\n'))
        data = dict(zip(headers, row))
        self.assertTrue('HW 04' not in data)
        self.assertEqual(data['HW 01'], '0.75')
        self.assertEqual(data['HW 02'], '0.5')
        self.assertEqual(data['HW 03'], '0.25')
        self.assertEqual(data['HW Avg'], '0.5')
Example #28
0
def new_assets_page_feature_flags(global_flag,
                                  enabled_for_all_courses=False,
                                  course_id=None,
                                  enabled_for_course=False):
    """
    Most test cases will use a single call to this manager,
    as they need to set the global setting and the course-specific
    setting for a single course.
    """
    RequestCache.clear_request_cache()
    NewAssetsPageFlag.objects.create(
        enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses)
    if course_id:
        CourseNewAssetsPageFlag.objects.create(course_id=course_id,
                                               enabled=enabled_for_course)
    yield
Example #29
0
def persistent_grades_feature_flags(
        global_flag,
        enabled_for_all_courses=False,
        course_id=None,
        enabled_for_course=False
):
    """
    Most test cases will use a single call to this manager,
    as they need to set the global setting and the course-specific
    setting for a single course.
    """
    RequestCache.clear_request_cache()
    PersistentGradesEnabledFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses)
    if course_id:
        CoursePersistentGradesFlag.objects.create(course_id=course_id, enabled=enabled_for_course)
    yield
Example #30
0
def new_assets_page_feature_flags(
        global_flag,
        enabled_for_all_courses=False,
        course_id=None,
        enabled_for_course=False
):
    """
    Most test cases will use a single call to this manager,
    as they need to set the global setting and the course-specific
    setting for a single course.
    """
    RequestCache.clear_request_cache()
    NewAssetsPageFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses)
    if course_id:
        CourseNewAssetsPageFlag.objects.create(course_id=course_id, enabled=enabled_for_course)
    yield
Example #31
0
    def clear_caches(cls):
        """
        Clear all of the caches defined in settings.CACHES.
        """
        # N.B. As of 2016-04-20, Django won't return any caches
        # from django.core.cache.caches.all() that haven't been
        # accessed using caches[name] previously, so we loop
        # over our list of overridden caches, instead.
        for cache in settings.CACHES:
            caches[cache].clear()

        # The sites framework caches in a module-level dictionary.
        # Clear that.
        sites.models.SITE_CACHE.clear()

        RequestCache.clear_request_cache()
Example #32
0
    def test_gradebook(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse('ccx_gradebook', kwargs={'course_id': self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Max number of student per page is one.  Patched setting MAX_STUDENTS_PER_PAGE_GRADE_BOOK = 1
        self.assertEqual(len(response.mako_context['students']), 1)  # pylint: disable=no-member
        student_info = response.mako_context['students'][0]  # pylint: disable=no-member
        self.assertEqual(student_info['grade_summary']['percent'], 0.5)
        self.assertEqual(
            student_info['grade_summary']['grade_breakdown'][0]['percent'],
            0.5)
        self.assertEqual(
            len(student_info['grade_summary']['section_breakdown']), 4)
Example #33
0
    def clear_caches(cls):
        """
        Clear all of the caches defined in settings.CACHES.
        """
        # N.B. As of 2016-04-20, Django won't return any caches
        # from django.core.cache.caches.all() that haven't been
        # accessed using caches[name] previously, so we loop
        # over our list of overridden caches, instead.
        for cache in settings.CACHES:
            caches[cache].clear()

        # The sites framework caches in a module-level dictionary.
        # Clear that.
        sites.models.SITE_CACHE.clear()

        RequestCache.clear_request_cache()
Example #34
0
    def test_course_waffle_flag(self, data):
        """
        Tests various combinations of a flag being set in waffle and overridden
        for a course.
        """
        RequestCache.clear_request_cache()

        with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
            with override_flag(self.NAMESPACED_FLAG_NAME, active=data['waffle_enabled']):
                # check twice to test that the result is properly cached
                self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY), data['result'])
                self.assertEqual(self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY), data['result'])
                # result is cached, so override check should happen once
                WaffleFlagCourseOverrideModel.override_value.assert_called_once_with(
                    self.NAMESPACED_FLAG_NAME,
                    self.TEST_COURSE_KEY
                )
Example #35
0
    def test_gradebook(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse(
            'ccx_gradebook',
            kwargs={'course_id': self.ccx_key}
        )
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        student_info = response.mako_context['students'][0]  # pylint: disable=no-member
        self.assertEqual(student_info['grade_summary']['percent'], 0.5)
        self.assertEqual(
            student_info['grade_summary']['grade_breakdown'][0]['percent'],
            0.5)
        self.assertEqual(
            len(student_info['grade_summary']['section_breakdown']), 4)
Example #36
0
    def test_request_cached_with_changing_kwargs(self):
        """
        Ensure that calling a decorated function with different keyword arguments
        will not use a cached value invoked by a previous call with different arguments.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)

        # This will be a miss, and make an underlying call.
        result = wrapped(1, foo=1)
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        # This will be a miss, and make an underlying call.
        result = wrapped(2, foo=2)
        self.assertEqual(result, 2)
        self.assertEqual(to_be_wrapped.call_count, 2)

        # This is bypass of the decorator.
        direct_result = mock_wrapper(3, foo=3)
        self.assertEqual(direct_result, 3)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # These will be hits, and not make an underlying call.
        result = wrapped(1, foo=1)
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 3)

        result = wrapped(2, foo=2)
        self.assertEqual(result, 2)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # Since we're changing foo, this will be a miss.
        result = wrapped(2, foo=5)
        self.assertEqual(result, 4)
        self.assertEqual(to_be_wrapped.call_count, 4)
Example #37
0
    def test_request_cached_with_changing_kwargs(self):
        """
        Ensure that calling a decorated function with different keyword arguments
        will not use a cached value invoked by a previous call with different arguments.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)

        # This will be a miss, and make an underlying call.
        result = wrapped(1, foo=1)
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 1)

        # This will be a miss, and make an underlying call.
        result = wrapped(2, foo=2)
        self.assertEqual(result, 2)
        self.assertEqual(to_be_wrapped.call_count, 2)

        # This is bypass of the decorator.
        direct_result = mock_wrapper(3, foo=3)
        self.assertEqual(direct_result, 3)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # These will be hits, and not make an underlying call.
        result = wrapped(1, foo=1)
        self.assertEqual(result, 1)
        self.assertEqual(to_be_wrapped.call_count, 3)

        result = wrapped(2, foo=2)
        self.assertEqual(result, 2)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # Since we're changing foo, this will be a miss.
        result = wrapped(2, foo=5)
        self.assertEqual(result, 4)
        self.assertEqual(to_be_wrapped.call_count, 4)
Example #38
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse("ccx_grades_csv", kwargs={"course_id": self.ccx_key})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        # Are the grades downloaded as an attachment?
        self.assertEqual(response["content-disposition"], "attachment")
        rows = response.content.strip().split("\r")
        headers = rows[0]

        # picking first student records
        data = dict(zip(headers.strip().split(","), rows[1].strip().split(",")))
        self.assertNotIn("HW 04", data)
        self.assertEqual(data["HW 01"], "0.75")
        self.assertEqual(data["HW 02"], "0.5")
        self.assertEqual(data["HW 03"], "0.25")
        self.assertEqual(data["HW Avg"], "0.5")
Example #39
0
    def dump_courses_to_neo4j(self, credentials, override_cache=False):
        """
        Method that iterates through a list of courses in a modulestore,
        serializes them, then submits tasks to write them to neo4j.
        Arguments:
            credentials (dict): the necessary credentials to connect
              to neo4j and create a py2neo `Graph` object
            override_cache: serialize the courses even if they'be been recently
                serialized

        Returns: two lists--one of the courses that were successfully written
            to neo4j and one of courses that were not.
        """

        total_number_of_courses = len(self.course_keys)

        submitted_courses = []
        skipped_courses = []

        graph = authenticate_and_create_graph(credentials)

        for index, course_key in enumerate(self.course_keys):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now submitting %s for export to neo4j: course %d of %d total courses",
                course_key,
                index + 1,
                total_number_of_courses,
            )

            if not (override_cache or should_dump_course(course_key, graph)):
                log.info("skipping submitting %s, since it hasn't changed", course_key)
                skipped_courses.append(six.text_type(course_key))
                continue

            dump_course_to_neo4j.apply_async(
                args=[six.text_type(course_key), credentials],
            )
            submitted_courses.append(six.text_type(course_key))

        return submitted_courses, skipped_courses
Example #40
0
    def dump_courses_to_neo4j(self, credentials, override_cache=False):
        """
        Method that iterates through a list of courses in a modulestore,
        serializes them, then submits tasks to write them to neo4j.
        Arguments:
            credentials (dict): the necessary credentials to connect
              to neo4j and create a py2neo `Graph` object
            override_cache: serialize the courses even if they'be been recently
                serialized

        Returns: two lists--one of the courses that were successfully written
            to neo4j and one of courses that were not.
        """

        total_number_of_courses = len(self.course_keys)

        submitted_courses = []
        skipped_courses = []

        graph = authenticate_and_create_graph(credentials)

        for index, course_key in enumerate(self.course_keys):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now submitting %s for export to neo4j: course %d of %d total courses",
                course_key,
                index + 1,
                total_number_of_courses,
            )

            if not (override_cache or should_dump_course(course_key, graph)):
                log.info("skipping submitting %s, since it hasn't changed", course_key)
                skipped_courses.append(six.text_type(course_key))
                continue

            dump_course_to_neo4j.apply_async(
                args=[six.text_type(course_key), credentials],
            )
            submitted_courses.append(six.text_type(course_key))

        return submitted_courses, skipped_courses
Example #41
0
    def test_grades_csv(self):
        self.course.enable_ccx = True
        RequestCache.clear_request_cache()

        url = reverse(
            'ccx_grades_csv',
            kwargs={'course_id': self.ccx_key}
        )
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        headers, row = (
            row.strip().split(',') for row in
            response.content.strip().split('\n')
        )
        data = dict(zip(headers, row))
        self.assertTrue('HW 04' not in data)
        self.assertEqual(data['HW 01'], '0.75')
        self.assertEqual(data['HW 02'], '0.5')
        self.assertEqual(data['HW 03'], '0.25')
        self.assertEqual(data['HW Avg'], '0.5')
Example #42
0
    def test_request_cached_with_none_result(self):
        """
        Ensure that calling a decorated function that returns None
        properly caches the result and doesn't recall the underlying
        function.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [None, None, None, 1, 1]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)

        # This will be a miss, and make an underlying call.
        result = wrapped(1)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 1)

        # This will be a miss, and make an underlying call.
        result = wrapped(2)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 2)

        # This is bypass of the decorator.
        direct_result = mock_wrapper(3)
        self.assertEqual(direct_result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # These will be hits, and not make an underlying call.
        result = wrapped(1)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)

        result = wrapped(2)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)
Example #43
0
    def test_get_template_path(self):
        """
        Tests to make sure the get_template_path function works as expected.
        """
        # if the current site has associated SiteTheme then get_template_path should return the argument as is.
        with patch(
                "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme",
                Mock(return_value=True),
        ):
            with patch(
                    "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite",
                    Mock(return_value=True),
            ):
                with patch(
                        "microsite_configuration.microsite.TEMPLATES_BACKEND"
                ) as mock_microsite_backend:
                    mock_microsite_backend.get_template = Mock(
                        return_value="/microsite/about.html")
                    self.assertEqual(
                        theming_helpers.get_template_path("about.html"),
                        "about.html")

        RequestCache.clear_request_cache()

        # if the current site does not have associated SiteTheme then get_template_path should return microsite override
        with patch(
                "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme",
                Mock(return_value=False),
        ):
            with patch(
                    "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite",
                    Mock(return_value=True),
            ):
                with patch(
                        "microsite_configuration.microsite.TEMPLATES_BACKEND"
                ) as mock_microsite_backend:
                    mock_microsite_backend.get_template_path = Mock(
                        return_value="/microsite/about.html")
                    self.assertEqual(
                        theming_helpers.get_template_path("about.html"),
                        "/microsite/about.html")
Example #44
0
    def test_request_cached_with_none_result(self):
        """
        Ensure that calling a decorated function that returns None
        properly caches the result and doesn't recall the underlying
        function.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.side_effect = [None, None, None, 1, 1]
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)

        # This will be a miss, and make an underlying call.
        result = wrapped(1)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 1)

        # This will be a miss, and make an underlying call.
        result = wrapped(2)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 2)

        # This is bypass of the decorator.
        direct_result = mock_wrapper(3)
        self.assertEqual(direct_result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)

        # These will be hits, and not make an underlying call.
        result = wrapped(1)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)

        result = wrapped(2)
        self.assertEqual(result, None)
        self.assertEqual(to_be_wrapped.call_count, 3)
Example #45
0
    def test_request_cached_mixed_unicode_str_args(self):
        """
        Ensure that request_cached can work with mixed str and Unicode parameters.
        """
        RequestCache.clear_request_cache()

        def dummy_function(arg1, arg2):
            """
            A dummy function that expects an str and unicode arguments.
            """
            assert isinstance(arg1, str), 'First parameter has to be of type `str`'
            assert isinstance(arg2, unicode), 'Second parameter has to be of type `unicode`'
            return True

        self.assertTrue(dummy_function('Hello', u'World'), 'Should be callable with ASCII chars')
        self.assertTrue(dummy_function('H∂llå', u'Wørld'), 'Should be callable with non-ASCII chars')

        wrapped = request_cached(dummy_function)

        self.assertTrue(wrapped('Hello', u'World'), 'Wrapper should handle ASCII only chars')
        self.assertTrue(wrapped('H∂llå', u'Wørld'), 'Wrapper should handle non-ASCII chars')
    def dump_courses_to_neo4j(self, graph, override_cache=False):
        """
        Method that iterates through a list of courses in a modulestore,
        serializes them, then submits tasks to write them to neo4j.
        Arguments:
            graph: py2neo graph object
            override_cache: serialize the courses even if they'be been recently
                serialized

        Returns: two lists--one of the courses that were successfully written
            to neo4j and one of courses that were not.
        """

        total_number_of_courses = len(self.course_keys)

        submitted_courses = []
        skipped_courses = []

        for index, course_key in enumerate(self.course_keys):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now exporting %s to neo4j: course %d of %d total courses",
                course_key,
                index + 1,
                total_number_of_courses,
            )

            if not (override_cache
                    or self.should_dump_course(course_key, graph)):
                log.info("skipping dumping %s, since it hasn't changed",
                         course_key)
                skipped_courses.append(unicode(course_key))

            else:
                self.dump_course_to_neo4j(course_key, graph)
                submitted_courses.append(unicode(course_key))

        return submitted_courses, skipped_courses
Example #47
0
    def test_request_cached_miss_and_then_hit(self):
        """
        Ensure that after a cache miss, we fill the cache and can hit it.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.return_value = 42
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)
        result = wrapped()
        self.assertEqual(result, 42)
        self.assertEqual(to_be_wrapped.call_count, 1)

        result = wrapped()
        self.assertEqual(result, 42)
        self.assertEqual(to_be_wrapped.call_count, 1)
Example #48
0
    def test_request_cached_miss_and_then_hit(self):
        """
        Ensure that after a cache miss, we fill the cache and can hit it.
        """
        RequestCache.clear_request_cache()

        to_be_wrapped = Mock()
        to_be_wrapped.return_value = 42
        self.assertEqual(to_be_wrapped.call_count, 0)

        def mock_wrapper(*args, **kwargs):
            """Simple wrapper to let us decorate our mock."""
            return to_be_wrapped(*args, **kwargs)

        wrapped = request_cached(mock_wrapper)
        result = wrapped()
        self.assertEqual(result, 42)
        self.assertEqual(to_be_wrapped.call_count, 1)

        result = wrapped()
        self.assertEqual(result, 42)
        self.assertEqual(to_be_wrapped.call_count, 1)
    def instrument_course_progress_render(self, dataset_index, queries, reads, xblocks):
        """
        Renders the progress page, instrumenting Mongo reads and SQL queries.
        """
        self.setup_course(dataset_index + 1)

        # 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()

            with self.assertNumQueries(queries):
                with check_mongo_calls(reads):
                    with check_sum_of_calls(XBlock, ['__init__'], xblocks):
                        self.grade_course(self.course)
Example #50
0
    def test_request_context_caching(self):
        """
        Test that the RequestContext is cached in the RequestCache.
        """
        with patch('edxmako.request_context.get_current_request', return_value=None):
            # requestcontext should be None, because the cache isn't filled
            self.assertIsNone(get_template_request_context())

        with patch('edxmako.request_context.get_current_request', return_value=self.request):
            # requestcontext should not be None, and should fill the cache
            self.assertIsNotNone(get_template_request_context())

        mock_get_current_request = Mock()
        with patch('edxmako.request_context.get_current_request', mock_get_current_request):
            # requestcontext should not be None, because the cache is filled
            self.assertIsNotNone(get_template_request_context())
        mock_get_current_request.assert_not_called()

        RequestCache.clear_request_cache()

        with patch('edxmako.request_context.get_current_request', return_value=None):
            # requestcontext should be None, because the cache isn't filled
            self.assertIsNone(get_template_request_context())
Example #51
0
    def test_course_waffle_flag(self, data):
        """
        Tests various combinations of a flag being set in waffle and overridden
        for a course.
        """
        RequestCache.clear_request_cache()

        with patch.object(WaffleFlagCourseOverrideModel,
                          'override_value',
                          return_value=data['course_override']):
            with override_flag(self.NAMESPACED_FLAG_NAME,
                               active=data['waffle_enabled']):
                # check twice to test that the result is properly cached
                self.assertEqual(
                    self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY),
                    data['result'])
                self.assertEqual(
                    self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_KEY),
                    data['result'])
                # result is cached, so override check should happen once
                WaffleFlagCourseOverrideModel.override_value.assert_called_once_with(
                    self.NAMESPACED_FLAG_NAME, self.TEST_COURSE_KEY)

        # check flag for a second course
        if data['course_override'] == WaffleFlagCourseOverrideModel.ALL_CHOICES.unset:
            # When course override wasn't set for the first course, the second course will get the same
            # cached value from waffle.
            self.assertEqual(
                self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_2_KEY),
                data['waffle_enabled'])
        else:
            # When course override was set for the first course, it should not apply to the second
            # course which should get the default value of False.
            self.assertEqual(
                self.TEST_COURSE_FLAG.is_enabled(self.TEST_COURSE_2_KEY),
                False)
Example #52
0
    def dump_courses_to_neo4j(self, graph, override_cache=False):
        """
        Parameters
        ----------
        graph: py2neo graph object
        override_cache: serialize the courses even if they'be been recently
            serialized

        Returns two lists: one of the courses that were successfully written
          to neo4j, and one of courses that were not.
        -------
        """
        total_number_of_courses = len(self.course_keys)

        successful_courses = []
        unsuccessful_courses = []

        for index, course_key in enumerate(self.course_keys):
            # first, clear the request cache to prevent memory leaks
            RequestCache.clear_request_cache()

            log.info(
                "Now exporting %s to neo4j: course %d of %d total courses",
                course_key,
                index + 1,
                total_number_of_courses,
            )

            if not (override_cache or self.should_dump_course(course_key)):
                log.info("skipping dumping %s, since it hasn't changed",
                         course_key)
                continue

            nodes, relationships = self.serialize_course(course_key)
            log.info(
                "%d nodes and %d relationships in %s",
                len(nodes),
                len(relationships),
                course_key,
            )

            transaction = graph.begin()
            course_string = six.text_type(course_key)
            try:
                # first, delete existing course
                transaction.run(
                    "MATCH (n:item) WHERE n.course_key='{}' DETACH DELETE n".
                    format(course_string))

                # now, re-add it
                self.add_to_transaction(nodes, transaction)
                self.add_to_transaction(relationships, transaction)
                transaction.commit()

            except Exception:  # pylint: disable=broad-except
                log.exception(
                    "Error trying to dump course %s to neo4j, rolling back",
                    course_string)
                transaction.rollback()
                unsuccessful_courses.append(course_string)

            else:
                COMMAND_LAST_RUN_CACHE.set(course_key)
                successful_courses.append(course_string)

        return successful_courses, unsuccessful_courses
Example #53
0
def invalidate_course_mode_cache(sender, **kwargs):   # pylint: disable=unused-argument
    """Invalidate the cache of course modes. """
    RequestCache.clear_request_cache(name=CourseMode.CACHE_NAMESPACE)
Example #54
0
def invalidate_course_mode_cache(sender, **kwargs):   # pylint: disable=unused-argument
    """Invalidate the cache of course modes. """
    RequestCache.clear_request_cache(name=CourseMode.CACHE_NAMESPACE)
Example #55
0
def invalidate_verified_track_cache(sender, **kwargs):   # pylint: disable=unused-argument
    """Invalidate the cache of VerifiedTrackCohortedCourse. """
    RequestCache.clear_request_cache(name=VerifiedTrackCohortedCourse.CACHE_NAMESPACE)