Ejemplo n.º 1
0
    def test_signals_organizations_publish(self, mock_bulk, *_):
        """
        Publishing an organization should update its document in the Elasticsearch organizations
        index, and the documents for published courses to which it is related, excluding snapshots.
        """
        parent = OrganizationFactory(should_publish=True)
        organization = OrganizationFactory(page_parent=parent.extended_object)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_organizations=[organization])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        self.assertTrue(organization.extended_object.publish("en"))
        organization.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"], organization.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_organizations")
        self.assertEqual(actions[2]["_id"], parent.get_es_id())
        self.assertEqual(actions[2]["_op_type"], "index")
        self.assertEqual(actions[2]["_index"], "richie_organizations")
Ejemplo n.º 2
0
    def test_signals_organizations_unpublish(self, mock_bulk, *_):
        """
        Unpublishing an organization in a language should update its document in the Elasticsearch
        organizations index or delete it if there is no language published anymore.
        It should also reindex the documents for published courses to which it is related,
        excluding snapshots.
        """
        organization = OrganizationFactory(page_languages=["en", "fr"],
                                           should_publish=True)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_organizations=[organization])
        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(organization.extended_object.unpublish("en"))
        organization.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"], organization.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_organizations")

        mock_bulk.reset_mock()

        # - Unpublish the second language
        self.assertTrue(organization.extended_object.unpublish("fr"))
        organization.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"], organization.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "delete")
        self.assertEqual(actions[1]["_index"], "richie_organizations")
Ejemplo n.º 3
0
 def test_get_es_id_for_draft_organization_with_public_extension(self):
     """
     A draft organization with a public extension. Its ES ID is the ID of the page linked to the
     public extension.
     """
     organization = OrganizationFactory(should_publish=True)
     self.assertEqual(
         organization.get_es_id(),
         str(organization.public_extension.extended_object_id),
     )
Ejemplo n.º 4
0
    def test_indexers_courses_get_es_documents_from_models(self, _mock_picture):
        """
        Happy path: the data is retrieved from the models properly formatted
        """
        # Create a course with a page in both english and french
        published_categories = [
            CategoryFactory(
                fill_icon=True,
                page_title={"en": "Title cat 1", "fr": "Titre cat 1"},
                should_publish=True,
            ),
            CategoryFactory(
                fill_icon=True,
                page_title={"en": "Title cat 2", "fr": "Titre cat 2"},
                should_publish=True,
            ),
        ]
        draft_category = CategoryFactory(fill_icon=True)

        main_organization = OrganizationFactory(
            page_title={
                "en": "english main organization title",
                "fr": "titre organisation principale français",
            },
            should_publish=True,
        )
        other_draft_organization = OrganizationFactory(
            page_title={
                "en": "english other organization title",
                "fr": "titre autre organisation français",
            }
        )
        other_published_organization = OrganizationFactory(
            page_title={
                "en": "english other organization title",
                "fr": "titre autre organisation français",
            },
            should_publish=True,
        )

        person1 = PersonFactory(
            page_title={"en": "Eugène Delacroix", "fr": "Eugène Delacroix"},
            should_publish=True,
        )
        person2 = PersonFactory(
            page_title={"en": "Comte de Saint-Germain", "fr": "Earl of Saint-Germain"},
            should_publish=True,
        )
        person_draft = PersonFactory(
            page_title={"en": "Jules de Polignac", "fr": "Jules de Polignac"}
        )

        course = CourseFactory(
            duration=[3, WEEK],
            effort=[2, HOUR],
            fill_categories=published_categories + [draft_category],
            fill_cover=True,
            fill_icons=published_categories + [draft_category],
            fill_organizations=[
                main_organization,
                other_draft_organization,
                other_published_organization,
            ],
            fill_team=[person1, person_draft, person2],
            page_title={
                "en": "an english course title",
                "fr": "un titre cours français",
            },
        )
        CourseRunFactory.create_batch(2, direct_course=course)
        course.extended_object.publish("en")
        course.extended_object.publish("fr")
        course.refresh_from_db()

        # Add a description in several languages
        placeholder = course.public_extension.extended_object.placeholders.get(
            slot="course_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="a propos français ligne 1.", language="fr", **plugin_params)
        add_plugin(body="a propos français ligne 2.", language="fr", **plugin_params)

        # Add an introduction in several languages
        placeholder = course.public_extension.extended_object.placeholders.get(
            slot="course_introduction"
        )
        plugin_params = {"placeholder": placeholder, "plugin_type": "PlainTextPlugin"}
        add_plugin(body="english introduction.", language="en", **plugin_params)
        add_plugin(body="introduction française.", language="fr", **plugin_params)

        # The results were properly formatted and passed to the consumer
        expected_course = {
            "_id": course.get_es_id(),
            "_index": "some_index",
            "_op_type": "some_action",
            "absolute_url": {
                "en": "/en/an-english-course-title/",
                "fr": "/fr/un-titre-cours-francais/",
            },
            "categories": [
                published_categories[0].get_es_id(),
                published_categories[1].get_es_id(),
            ],
            "categories_names": {
                "en": ["Title cat 1", "Title cat 2"],
                "fr": ["Titre cat 1", "Titre cat 2"],
            },
            "code": course.code,
            "complete": {
                "en": [
                    "an english course title",
                    "english course title",
                    "course title",
                    "title",
                ],
                "fr": [
                    "un titre cours français",
                    "titre cours français",
                    "cours français",
                    "français",
                ],
            },
            "course_runs": [
                {
                    "start": course_run.public_course_run.start,
                    "end": course_run.public_course_run.end,
                    "enrollment_start": course_run.public_course_run.enrollment_start,
                    "enrollment_end": course_run.public_course_run.enrollment_end,
                    "languages": course_run.public_course_run.languages,
                }
                for course_run in course.course_runs.order_by("-end")
            ],
            "cover_image": {
                "en": {"info": "picture info"},
                "fr": {"info": "picture info"},
            },
            "description": {
                "en": "english description line 1. english description line 2.",
                "fr": "a propos français ligne 1. a propos français ligne 2.",
            },
            "duration": {"en": "3 weeks", "fr": "3 semaines"},
            "effort": {"en": "2 hours", "fr": "2 heures"},
            "icon": {
                "en": {
                    "color": published_categories[0].color,
                    "info": "picture info",
                    "title": "Title cat 1",
                },
                "fr": {
                    "color": published_categories[0].color,
                    "info": "picture info",
                    "title": "Titre cat 1",
                },
            },
            "introduction": {
                "en": "english introduction.",
                "fr": "introduction française.",
            },
            "is_new": False,
            "is_listed": True,
            "organization_highlighted": {
                "en": "english main organization title",
                "fr": "titre organisation principale français",
            },
            "organization_highlighted_cover_image": {},
            "organizations": [
                main_organization.get_es_id(),
                other_published_organization.get_es_id(),
            ],
            "organizations_names": {
                "en": [
                    "english main organization title",
                    "english other organization title",
                ],
                "fr": [
                    "titre organisation principale français",
                    "titre autre organisation français",
                ],
            },
            "persons": [
                person1.get_es_id(),
                person2.get_es_id(),
            ],
            "persons_names": {
                "en": ["Eugène Delacroix", "Comte de Saint-Germain"],
                "fr": ["Eugène Delacroix", "Earl of Saint-Germain"],
            },
            "title": {"fr": "un titre cours français", "en": "an english course title"},
        }
        indexed_courses = list(
            CoursesIndexer.get_es_documents(index="some_index", action="some_action")
        )
        self.assertEqual(len(indexed_courses), 1)
        self.assertEqual(indexed_courses[0], expected_course)
Ejemplo n.º 5
0
    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(),
                    },
                ],
            },
        )
Ejemplo n.º 6
0
 def test_get_es_id_for_draft_organization_with_no_public_extension(self):
     """
     A draft organization with no public extension. It has no ES ID.
     """
     organization = OrganizationFactory()
     self.assertEqual(organization.get_es_id(), None)
Ejemplo n.º 7
0
    def test_indexers_organizations_get_es_documents(self, _mock_picture):
        """
        Happy path: organization data is fetched from the models properly formatted
        """
        organization1 = OrganizationFactory(
            page_title={
                "en": "my first organization",
                "fr": "ma première organisation",
            },
            fill_logo=True,
            should_publish=True,
        )
        organization2 = OrganizationFactory(
            page_title={
                "en": "my second organization",
                "fr": "ma deuxième organisation",
            },
            should_publish=True,
        )

        # Add a description in several languages to the first organization
        placeholder = organization1.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(
                OrganizationsIndexer.get_es_documents(index="some_index",
                                                      action="some_action")),
            [
                {
                    "_id": organization2.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en": "/en/my-second-organization/",
                        "fr": "/fr/ma-deuxieme-organisation/",
                    },
                    "complete": {
                        "en": [
                            "my second organization",
                            "second organization",
                            "organization",
                        ],
                        "fr": [
                            "ma deuxième organisation",
                            "deuxième organisation",
                            "organisation",
                        ],
                    },
                    "description": {},
                    "logo": {},
                    "title": {
                        "en": "my second organization",
                        "fr": "ma deuxième organisation",
                    },
                    "title_raw": {
                        "en": "my second organization",
                        "fr": "ma deuxième organisation",
                    },
                },
                {
                    "_id": organization1.get_es_id(),
                    "_index": "some_index",
                    "_op_type": "some_action",
                    "absolute_url": {
                        "en": "/en/my-first-organization/",
                        "fr": "/fr/ma-premiere-organisation/",
                    },
                    "complete": {
                        "en": [
                            "my first organization",
                            "first organization",
                            "organization",
                        ],
                        "fr": [
                            "ma première organisation",
                            "première organisation",
                            "organisation",
                        ],
                    },
                    "description": {
                        "en":
                        "english description line 1. english description line 2.",
                        "fr":
                        "description français ligne 1. description français ligne 2.",
                    },
                    "logo": {
                        "en": "logo info",
                        "fr": "logo info"
                    },
                    "title": {
                        "en": "my first organization",
                        "fr": "ma première organisation",
                    },
                    "title_raw": {
                        "en": "my first organization",
                        "fr": "ma première organisation",
                    },
                },
            ],
        )