Beispiel #1
0
    def test_signals_categories_publish(self, mock_bulk, *_):
        """
        Publishing a category should update its document in the Elasticsearch categories
        index, and the documents for published courses to which it is related, excluding snapshots.
        """
        parent = CategoryFactory(should_publish=True)
        category = CategoryFactory(page_parent=parent.extended_object)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        self.assertTrue(category.extended_object.publish("en"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 3)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_categories")
        self.assertEqual(actions[2]["_id"], parent.get_es_id())
        self.assertEqual(actions[2]["_op_type"], "index")
        self.assertEqual(actions[2]["_index"], "richie_categories")
Beispiel #2
0
    def test_signals_categories_unpublish(self, mock_bulk, *_):
        """
        Unpublishing a category in one language should update its document in the Elasticsearch
        categories index or delete it if there is no language published anymore.
        It should also update the documents for published courses to which it is related
        excluding snapshots.
        """
        category = CategoryFactory(page_languages=["en", "fr"],
                                   should_publish=True)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        # - Unpublish the first language
        self.assertTrue(category.extended_object.unpublish("en"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_categories")

        mock_bulk.reset_mock()

        # - Unpublish the second language
        self.assertTrue(category.extended_object.unpublish("fr"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "delete")
        self.assertEqual(actions[1]["_index"], "richie_categories")
    def test_indexers_categories_get_es_documents_unpublished(self):
        """
        Unpublished categories and children of unpublished categories should not be indexed
        """
        # Our meta category and its page
        meta = CategoryFactory(
            page_parent=create_i18n_page("Categories", published=True),
            page_reverse_id="subjects",
            page_title="Subjects",
            should_publish=True,
        )
        parent = CategoryFactory(page_parent=meta.extended_object,
                                 should_publish=True)
        CategoryFactory(
            page_parent=parent.extended_object,
            page_title="my second subject",
            should_publish=True,
        )

        # Unpublish the parent category
        self.assertTrue(parent.extended_object.unpublish("en"))

        # The unpublished parent category and its child should not get indexed
        self.assertEqual(
            list(
                CategoriesIndexer.get_es_documents(index="some_index",
                                                   action="some_action")),
            [
                {
                    "_id": meta.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en": "/en/categories/subjects/",
                        "fr": "/fr/categories/subjects/",
                    },
                    "complete": {
                        "en": ["Subjects"]
                    },
                    "description": {},
                    "icon": {},
                    "is_meta": True,
                    "kind": "meta",
                    "logo": {},
                    "nb_children": 1,
                    "path": "00010001",
                    "title": {
                        "en": "Subjects"
                    },
                    "title_raw": {
                        "en": "Subjects"
                    },
                },
            ],
        )
    def test_filter_definitions_indexable_filter_aggs_include_published_page(
            self):
        """
        The indexable filters (subjects, levels and organizations) should return a list of objects
        that are direct children of the filter pages if those exist and are published.
        """
        for filter_name in ["levels", "subjects", "organizations"]:
            filter_page = CategoryFactory(page_reverse_id=filter_name,
                                          should_publish=True)
            item = CategoryFactory(page_parent=filter_page.extended_object,
                                   should_publish=True)
            expected_include = [str(item.get_es_id())]

            with self.assertNumQueries(2):
                self.assertEqual(
                    FILTERS[filter_name].aggs_include,
                    expected_include,
                )

            with self.assertNumQueries(0):
                self.assertEqual(
                    FILTERS[filter_name].aggs_include,
                    expected_include,
                )

            # Reset cache for subsequent tests...
            # pylint: disable=protected-access
            FILTERS[filter_name]._base_page = None
            caches["search"].clear()

            with self.assertNumQueries(2):
                self.assertEqual(
                    FILTERS[filter_name].aggs_include,
                    expected_include,
                )

            with self.assertNumQueries(0):
                self.assertEqual(
                    FILTERS[filter_name].aggs_include,
                    expected_include,
                )

            # Reset cache for subsequent tests...
            # pylint: disable=protected-access
            FILTERS[filter_name]._base_page = None
    def test_indexable_filters_internationalization(self):
        """
        Indexable filters (such as categories and organizations by default) should have
        their names localized in the filter definitions in course search responses.
        """
        # Create the meta categories, each with a child category that should appear in facets
        subjects_meta = CategoryFactory(page_reverse_id="subjects",
                                        should_publish=True)
        subject = CategoryFactory(page_parent=subjects_meta.extended_object,
                                  should_publish=True)
        levels_meta = CategoryFactory(page_reverse_id="levels",
                                      should_publish=True)
        level = CategoryFactory(page_parent=levels_meta.extended_object,
                                should_publish=True)
        # Create 2 organizations that should appear in facets
        org_meta = OrganizationFactory(page_reverse_id="organizations",
                                       should_publish=True)
        org_1 = OrganizationFactory(
            page_parent=org_meta.extended_object,
            page_title="First organization",
            should_publish=True,
        )
        org_2 = OrganizationFactory(
            page_parent=org_meta.extended_object,
            page_title="Second organization",
            should_publish=True,
        )
        # Create a course linked to our categories and organizations
        CourseFactory(
            fill_categories=[subject, level],
            fill_organizations=[org_1, org_2],
            should_publish=True,
        )
        # Index our objects into ES
        bulk_compat(
            actions=[
                *ES_INDICES.categories.get_es_documents(),
                *ES_INDICES.organizations.get_es_documents(),
                *ES_INDICES.courses.get_es_documents(),
            ],
            chunk_size=500,
            client=ES_CLIENT,
        )
        ES_INDICES_CLIENT.refresh()

        response = self.client.get("/api/v1.0/courses/?scope=filters")
        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.json()["filters"]["subjects"],
            {
                "base_path":
                "0001",
                "human_name":
                "Subjects",
                "is_autocompletable":
                True,
                "is_drilldown":
                False,
                "is_searchable":
                True,
                "name":
                "subjects",
                "position":
                2,
                "has_more_values":
                False,
                "values": [{
                    "count": 1,
                    "human_name": subject.extended_object.get_title(),
                    "key": subject.get_es_id(),
                }],
            },
        )
        self.assertEqual(
            response.json()["filters"]["levels"],
            {
                "base_path":
                "0002",
                "human_name":
                "Levels",
                "is_autocompletable":
                True,
                "is_drilldown":
                False,
                "is_searchable":
                True,
                "name":
                "levels",
                "position":
                3,
                "has_more_values":
                False,
                "values": [{
                    "count": 1,
                    "human_name": level.extended_object.get_title(),
                    "key": level.get_es_id(),
                }],
            },
        )
        self.assertEqual(
            response.json()["filters"]["organizations"],
            {
                "base_path":
                "0003",
                "human_name":
                "Organizations",
                "is_autocompletable":
                True,
                "is_drilldown":
                False,
                "is_searchable":
                True,
                "name":
                "organizations",
                "position":
                4,
                "has_more_values":
                False,
                "values": [
                    {
                        "count": 1,
                        "human_name": org_1.extended_object.get_title(),
                        "key": org_1.get_es_id(),
                    },
                    {
                        "count": 1,
                        "human_name": org_2.extended_object.get_title(),
                        "key": org_2.get_es_id(),
                    },
                ],
            },
        )
    def test_indexers_categories_get_es_documents(self, _mock_picture):
        """
        Happy path: the data is fetched from the models properly formatted
        """
        # Our meta category and its page
        meta = CategoryFactory(
            page_parent=create_i18n_page(
                {
                    "en": "Categories",
                    "fr": "Catégories"
                }, published=True),
            page_reverse_id="subjects",
            page_title={
                "en": "Subjects",
                "fr": "Sujets"
            },
            fill_icon=True,
            fill_logo=True,
            should_publish=True,
        )
        category1 = CategoryFactory(
            page_parent=meta.extended_object,
            page_title={
                "en": "my first subject",
                "fr": "ma première thématique"
            },
            fill_icon=True,
            fill_logo=True,
            should_publish=True,
        )
        category2 = CategoryFactory(
            page_parent=category1.extended_object,
            page_title={
                "en": "my second subject",
                "fr": "ma deuxième thématic"
            },
            should_publish=True,
        )

        # Add a description in several languages to the first category
        placeholder = category1.public_extension.extended_object.placeholders.get(
            slot="description")
        plugin_params = {
            "placeholder": placeholder,
            "plugin_type": "CKEditorPlugin"
        }
        add_plugin(body="english description line 1.",
                   language="en",
                   **plugin_params)
        add_plugin(body="english description line 2.",
                   language="en",
                   **plugin_params)
        add_plugin(body="description français ligne 1.",
                   language="fr",
                   **plugin_params)
        add_plugin(body="description français ligne 2.",
                   language="fr",
                   **plugin_params)

        # The results were properly formatted and passed to the consumer
        self.assertEqual(
            list(
                CategoriesIndexer.get_es_documents(index="some_index",
                                                   action="some_action")),
            [
                {
                    "_id": category2.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en":
                        "/en/categories/subjects/my-first-subject/my-second-subject/",
                        "fr":
                        "/fr/categories/sujets/ma-premiere-thematique/ma-deuxieme-thematic/",
                    },
                    "complete": {
                        "en":
                        ["my second subject", "second subject", "subject"],
                        "fr": [
                            "ma deuxième thématic", "deuxième thématic",
                            "thématic"
                        ],
                    },
                    "description": {},
                    "icon": {},
                    "is_meta": False,
                    "kind": "subjects",
                    "logo": {},
                    "nb_children": 0,
                    "path": "0001000100010001",
                    "title": {
                        "en": "my second subject",
                        "fr": "ma deuxième thématic"
                    },
                    "title_raw": {
                        "en": "my second subject",
                        "fr": "ma deuxième thématic",
                    },
                },
                {
                    "_id": category1.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en": "/en/categories/subjects/my-first-subject/",
                        "fr": "/fr/categories/sujets/ma-premiere-thematique/",
                    },
                    "complete": {
                        "en": ["my first subject", "first subject", "subject"],
                        "fr": [
                            "ma première thématique",
                            "première thématique",
                            "thématique",
                        ],
                    },
                    "description": {
                        "en":
                        "english description line 1. english description line 2.",
                        "fr":
                        "description français ligne 1. description français ligne 2.",
                    },
                    "icon": {
                        "en": "picture info",
                        "fr": "picture info"
                    },
                    "is_meta": False,
                    "kind": "subjects",
                    "logo": {
                        "en": "picture info",
                        "fr": "picture info"
                    },
                    "nb_children": 1,
                    "path": "000100010001",
                    "title": {
                        "en": "my first subject",
                        "fr": "ma première thématique"
                    },
                    "title_raw": {
                        "en": "my first subject",
                        "fr": "ma première thématique",
                    },
                },
                {
                    "_id": meta.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en": "/en/categories/subjects/",
                        "fr": "/fr/categories/sujets/",
                    },
                    "complete": {
                        "en": ["Subjects"],
                        "fr": ["Sujets"]
                    },
                    "description": {},
                    "icon": {
                        "en": "picture info",
                        "fr": "picture info"
                    },
                    "is_meta": True,
                    "kind": "meta",
                    "logo": {
                        "en": "picture info",
                        "fr": "picture info"
                    },
                    "nb_children": 1,
                    "path": "00010001",
                    "title": {
                        "en": "Subjects",
                        "fr": "Sujets"
                    },
                    "title_raw": {
                        "en": "Subjects",
                        "fr": "Sujets"
                    },
                },
            ],
        )