Esempio n. 1
0
    def test_models_category_get_courses_descendants(self):
        """
        Related courses should include the courses linked to the category's descendants,
        unless specifically deactivated by the "include_descendants" argument.
        """
        category_page = create_page("Subjects",
                                    "courses/cms/category_detail.html",
                                    "en",
                                    published=True)
        category = CategoryFactory(extended_object=category_page,
                                   should_publish=True)
        courses = CourseFactory.create_batch(2,
                                             fill_categories=[category],
                                             should_publish=True)

        child_category_page = create_page(
            "Literature",
            "courses/cms/category_detail.html",
            "en",
            parent=category_page,
            published=True,
        )
        child_category = CategoryFactory(extended_object=child_category_page,
                                         should_publish=True)
        courses_child = CourseFactory.create_batch(
            2, fill_categories=[child_category], should_publish=True)

        grand_child_category_page = create_page(
            "Literature",
            "courses/cms/category_detail.html",
            "en",
            parent=child_category_page,
            published=True,
        )
        grand_child_category = CategoryFactory(
            extended_object=grand_child_category_page, should_publish=True)
        courses_grand_child = CourseFactory.create_batch(
            2, fill_categories=[grand_child_category], should_publish=True)

        # Check that each category gets courses from its descendants
        # ...unless we pass an argument to deactivate it
        self.assertEqual(list(category.get_courses()),
                         courses + courses_child + courses_grand_child)
        self.assertEqual(list(category.get_courses(include_descendants=False)),
                         courses)

        self.assertEqual(list(child_category.get_courses()),
                         courses_child + courses_grand_child)
        self.assertEqual(
            list(child_category.get_courses(include_descendants=False)),
            courses_child)

        self.assertEqual(
            list(grand_child_category.get_courses(include_descendants=False)),
            courses_grand_child,
        )
        self.assertEqual(list(grand_child_category.get_courses()),
                         courses_grand_child)
Esempio n. 2
0
    def test_signals_categories_no_parent(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.
        """
        category = CategoryFactory()
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        published_course.extended_object.publish("en")
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        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"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(
            actions[0]["_id"],
            str(published_course.public_extension.extended_object_id))
        self.assertEqual(actions[0]["_type"], "course")
        self.assertEqual(actions[1]["_id"], "L-0001")
        self.assertEqual(actions[1]["_type"], "category")
Esempio n. 3
0
    def test_signals_persons_publish(self, mock_bulk, *_):
        """
        Publishing a person should update its document in the Elasticsearch persons
        index, and the documents for published courses to which it is related, excluding snapshots.
        """
        person = PersonFactory()
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_team=[person])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        self.assertTrue(person.extended_object.publish("en"))
        person.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"], person.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_persons")
Esempio n. 4
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")
Esempio n. 5
0
    def test_models_subject_courses_copied_when_publishing(self):
        """
        When publishing an subject, the links courses on the draft version of the
        subject should be copied.
        """
        # Create draft courses
        course1, course2 = CourseFactory.create_batch(2)

        # Create a draft subject
        draft_subject = SubjectFactory(with_courses=[course1, course2])

        # Publish course1
        course1.extended_object.publish("en")
        course1.refresh_from_db()

        # The draft subject should see all courses
        self.assertEqual(set(draft_subject.courses.all()), {course1, course2})
        self.assertEqual(set(draft_subject.courses.drafts()),
                         {course1, course2})

        # Publish the subject and check that the courses are copied
        draft_subject.extended_object.publish("en")
        published_subject = Subject.objects.get(
            extended_object__publisher_is_draft=False)
        self.assertEqual(set(published_subject.courses.all()),
                         {course1.public_extension})
Esempio n. 6
0
    def test_signals_organizations(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])
        published_course.extended_object.publish("en")
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        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"],
            str(published_course.public_extension.extended_object_id))
        self.assertEqual(actions[0]["_type"], "course")
        self.assertEqual(actions[1]["_id"], "L-00010001")
        self.assertEqual(actions[1]["_type"], "organization")
        self.assertEqual(actions[2]["_id"], "P-0001")
        self.assertEqual(actions[2]["_type"], "organization")
    def test_templates_organization_detail_cms_draft_content(self):
        """
        A staff user should see a draft organization including its draft elements with an
        annotation.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        organization = OrganizationFactory(page_title="La Sorbonne",
                                           fill_courses=courses)
        page = organization.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should be visible as draft to the staff user
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertContains(response,
                            "<title>La Sorbonne</title>",
                            html=True,
                            status_code=200)
        self.assertContains(
            response,
            '<h1 class="organization-detail__title">La Sorbonne</h1>',
            html=True,
        )

        # The published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<p class="course-glimpse__content__title">{:s}</p>'.format(
                    course.extended_object.get_title()),
                html=True,
            )
        # Draft courses should also be present on the page with an annotation for styling
        for course in courses[-2:]:
            self.assertContains(
                response,
                ('<a class="{name:s} {name:s}--link {name:s}--draft" '
                 'href="{link:s}">').format(
                     name="course-glimpse",
                     link=course.extended_object.get_absolute_url(),
                 ),
            )
            self.assertContains(
                response,
                '<p class="course-glimpse__content__title">{title:s}</p>'.
                format(title=course.extended_object.get_title()),
                html=True,
            )
    def test_templates_program_detail_cms_draft_content(self):
        """
        A staff user should see a draft program including only its public elements.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should be visible as draft to the staff user
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertContains(response,
                            "<title>Preums</title>",
                            html=True,
                            status_code=200)
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # The published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<p class="course-glimpse__title">{:s}</p>'.format(
                    course.extended_object.get_title()),
                html=True,
            )
        # Draft courses should not be present on the page
        for course in courses[-2:]:
            self.assertNotIn(
                course.extended_object.get_absolute_url(),
                re.sub(" +", " ",
                       str(response.content).replace("\\n", "")),
            )
            self.assertNotContains(
                response,
                course.extended_object.get_title(),
                html=True,
            )
    def test_subject_cms_draft_content(self):
        """
        A staff user should see a draft subject including its draft elements with an annotation
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        subject = SubjectFactory(title="Very interesting subject",
                                 with_courses=courses)
        page = subject.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should be visible as draft to the staff user
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertContains(
            response,
            "<title>Very interesting subject</title>",
            status_code=200,
            html=True,
        )
        self.assertContains(
            response,
            '<h1 class="subject-detail__title">Very interesting subject</h1>',
            html=True,
        )
        # The published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<li class="subject-detail__content__courses__item">{:s}</li>'.
                format(course.extended_object.get_title()),
                html=True,
            )
        # The draft course should also be present on the page with an annotation for styling
        for course in courses[-2:]:
            self.assertContains(
                response,
                '<li class="{element:s} {element:s}--draft">{title:s}</li>'.
                format(
                    element="subject-detail__content__courses__item",
                    title=course.extended_object.get_title(),
                ),
                html=True,
            )
Esempio n. 10
0
    def test_templates_program_detail_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published program page
        """
        courses = CourseFactory.create_batch(4)

        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should not be visible before it is published
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # Publish the program and ensure the content is correct
        page.publish("en")
        response = self.client.get(url)
        self.assertContains(
            response,
            "<title>Preums - Program - example.com</title>",
            html=True,
            status_code=200,
        )
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # Only published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                    course.extended_object.get_title()),
                html=True,
            )
        for course in courses[-2:]:
            self.assertNotContains(response,
                                   course.extended_object.get_title())
Esempio n. 11
0
    def test_templates_category_detail_cms_published_content_max_courses(
            self, _mock_page_url):
        """
        Make sure the category detail page does not display too many courses, even when a large
        number are related to the current category, as this can cause the page to load very slowly
        and is not a great experience for the user anyway.
        """
        # Create our dummy category and the 3 courses we'll attach to it
        meta = CategoryFactory(
            page_parent=create_i18n_page(
                {
                    "en": "Categories",
                    "fr": "Catégories"
                }, published=True),
            page_reverse_id="subjects",
            page_title={
                "en": "Subjects",
                "fr": "Sujets"
            },
            should_publish=True,
        )
        category = CategoryFactory(page_parent=meta.extended_object,
                                   should_publish=True)
        courses = CourseFactory.create_batch(3,
                                             fill_categories=[category],
                                             should_publish=True)
        # Link the 3 courses with our category through the relevant placeholder
        for course in courses:
            add_plugin(
                course.extended_object.placeholders.get(
                    slot="course_categories"),
                CategoryPlugin,
                "en",
                page=category.extended_object,
            )
        # Make sure we do have 3 courses on the category
        self.assertEqual(category.get_courses().count(), 3)

        # Only the first two are rendered in the template
        response = self.client.get(category.extended_object.get_absolute_url())
        self.assertContains(response, courses[0].extended_object.get_title())
        self.assertContains(response, courses[1].extended_object.get_title())
        self.assertNotContains(response,
                               courses[2].extended_object.get_title())

        # There is a link to view more related courses directly in the Search view
        self.assertContains(
            response,
            f'href="/the/courses/?subjects={category.get_es_id():s}"')
        self.assertContains(
            response,
            f"See all courses related to {category.extended_object.get_title():s}",
        )
Esempio n. 12
0
    def test_signals_persons_unpublish(self, mock_bulk, *_):
        """
        Unpublishing a person in a language should update its document in the Elasticsearch
        persons 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.
        """
        person = PersonFactory(page_languages=["en", "fr"],
                               should_publish=True)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_team=[person])
        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(person.extended_object.unpublish("en"))
        person.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"], person.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_persons")

        mock_bulk.reset_mock()

        # - Unpublish the second language
        self.assertTrue(person.extended_object.unpublish("fr"))
        person.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"], person.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "delete")
        self.assertEqual(actions[1]["_index"], "richie_persons")
    def test_organization_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published organization page
        """
        courses = CourseFactory.create_batch(4)
        organization = OrganizationFactory(title="La Sorbonne",
                                           logo="my_logo.jpg",
                                           with_courses=courses)
        page = organization.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should not be visible before it is published
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # Publish and ensure content is correct
        page.publish("en")
        response = self.client.get(url)
        self.assertContains(response,
                            "<title>La Sorbonne</title>",
                            html=True,
                            status_code=200)
        self.assertContains(
            response,
            '<h1 class="organization-detail__title">La Sorbonne</h1>',
            html=True,
        )
        self.assertContains(
            response,
            '<img src="/media/my_logo.jpg" alt="La Sorbonne logo">',
            html=True)

        # Only published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<li class="organization-detail__content__courses__item">{:s}</li>'
                .format(course.extended_object.get_title()),
                html=True,
            )
        for course in courses[-2:]:
            self.assertNotContains(response,
                                   course.extended_object.get_title())
Esempio n. 14
0
    def test_templates_category_detail_cms_published_content_max_courses_on_meta(
            self, _mock_page_url):
        """
        Meta categories are a special case: technically, any page linked to one of their children
        is linked to the meta-category too. This means in a lot of cases we just want a link to
        *all* courses not just their related courses.
        The Search view is also not equipped to filter on a meta-category. In this case, we just
        link to it with a different link text.
        """
        # Create our dummy category and the 3 courses we'll attach to it
        meta = CategoryFactory(
            page_parent=create_i18n_page(
                {
                    "en": "Categories",
                    "fr": "Catégories"
                }, published=True),
            page_reverse_id="subjects",
            page_title={
                "en": "Subjects",
                "fr": "Sujets"
            },
            should_publish=True,
        )
        category = CategoryFactory(page_parent=meta.extended_object,
                                   should_publish=True)
        courses = CourseFactory.create_batch(3,
                                             fill_categories=[category],
                                             should_publish=True)
        # Link the 3 courses with our category through the relevant placeholder
        for course in courses:
            add_plugin(
                course.extended_object.placeholders.get(
                    slot="course_categories"),
                CategoryPlugin,
                "en",
                page=category.extended_object,
            )
        # Make sure we do have 3 courses on the category
        self.assertEqual(category.get_courses().count(), 3)

        # NB: here we are requesting the meta category's page, not the child category
        # Only the first two are rendered in the template
        response = self.client.get(meta.extended_object.get_absolute_url())
        self.assertContains(response, courses[0].extended_object.get_title())
        self.assertContains(response, courses[1].extended_object.get_title())
        self.assertNotContains(response,
                               courses[2].extended_object.get_title())

        # There is a link to view all courses
        self.assertContains(response, 'href="/the/courses/')
        self.assertContains(response, "See all courses")
    def test_templates_category_detail_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published category page
        """
        courses = CourseFactory.create_batch(4)
        category = CategoryFactory(
            page_title="Very interesting category", fill_courses=courses
        )
        page = category.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[2].extended_object.publish("en")
        courses[2].extended_object.unpublish("en")

        # The page should not be visible before it is published
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # Publish the category and ensure the content is correct
        page.publish("en")
        response = self.client.get(url)
        self.assertContains(
            response,
            "<title>Very interesting category</title>",
            status_code=200,
            html=True,
        )
        self.assertContains(
            response,
            '<h1 class="category-detail__title">Very interesting category</h1>',
            html=True,
        )

        # Only published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<p class="course-glimpse__content__title">{:s}</p>'.format(
                    course.extended_object.get_title()
                ),
                html=True,
            )
        for course in courses[-2:]:
            self.assertNotContains(response, course.extended_object.get_title())
Esempio n. 16
0
    def test_models_category_get_courses_ordering(self):
        """The related courses should be sorted by their position in the pages tree."""
        category = CategoryFactory(should_publish=True)
        course1, course2, course3 = CourseFactory.create_batch(
            3, fill_categories=[category], should_publish=True)
        self.assertEqual(list(category.get_courses()),
                         [course1, course2, course3])

        # Move pages in the tree and check that they are returned in the new order
        course3.extended_object.move_page(course1.extended_object.node,
                                          position="left")
        self.assertEqual(list(category.get_courses()),
                         [course3, course1, course2])

        course1.extended_object.move_page(course3.extended_object.node,
                                          position="left")
        self.assertEqual(list(category.get_courses()),
                         [course1, course3, course2])
Esempio n. 17
0
    def test_models_category_get_courses(self):
        """
        It should be possible to retrieve the list of related courses on the category instance.
        The number of queries should be minimal.
        """
        category = CategoryFactory(should_publish=True)
        courses = CourseFactory.create_batch(
            3, page_title="my title", fill_categories=[category], should_publish=True
        )
        retrieved_courses = category.get_courses()

        with self.assertNumQueries(2):
            self.assertEqual(set(retrieved_courses), set(courses))

        with self.assertNumQueries(0):
            for course in retrieved_courses:
                self.assertEqual(
                    course.extended_object.prefetched_titles[0].title, "my title"
                )
    def test_organization_courses_copied_when_publishing(self):
        """
        When publishing a organization, the links to draft courses on the draft version of the
        organization should be copied (clear then add) to the published version.
        Links to published courses should not be copied as they are redundant and not
        up-to-date.
        """
        # Create draft courses
        course1, course2 = CourseFactory.create_batch(2)

        # Create a draft organization
        draft_organization = OrganizationFactory(
            with_courses=[course1, course2])

        # Publish course1
        course1.extended_object.publish("en")
        course1.refresh_from_db()

        # The draft organization should see all courses and propose a custom filter to easily
        # access the draft versions
        self.assertEqual(
            set(draft_organization.courses.all()),
            {course1, course1.public_extension, course2},
        )
        self.assertEqual(set(draft_organization.courses.drafts()),
                         {course1, course2})

        # Publish the organization and check that the courses are copied
        draft_organization.extended_object.publish("en")
        published_organization = Organization.objects.get(
            extended_object__publisher_is_draft=False)
        self.assertEqual(set(published_organization.courses.all()),
                         {course1, course2})

        # When publishing, the courses that are obsolete should be cleared
        draft_organization.courses.remove(course2)
        self.assertEqual(set(published_organization.courses.all()),
                         {course1, course2})

        # courses on the published organization are only cleared after publishing the draft page
        draft_organization.extended_object.publish("en")
        self.assertEqual(set(published_organization.courses.all()), {course1})
    def test_templates_organization_detail_cms_published_content_max_courses(
            self, _mock_page_url):
        """
        Make sure the organization detail page does not display too many courses, even when a large
        number are related to the current organization, as this can cause the page to load very
        slowly and is not a great experience for the user anyway.
        """
        # Create our dummy organization and the 3 courses we'll attach to it
        organization = OrganizationFactory(should_publish=True)
        courses = CourseFactory.create_batch(3,
                                             fill_organizations=[organization],
                                             should_publish=True)
        # Link the 3 courses with our organization through the relevant placeholder
        for course in courses:
            add_plugin(
                course.extended_object.placeholders.get(
                    slot="course_organizations"),
                OrganizationPlugin,
                "en",
                page=organization.extended_object,
            )
        # Make sure we do have 3 courses on the organization
        self.assertEqual(organization.get_courses().count(), 3)

        # Only the first two are rendered in the template
        response = self.client.get(
            organization.extended_object.get_absolute_url())
        self.assertContains(response, courses[0].extended_object.get_title())
        self.assertContains(response, courses[1].extended_object.get_title())
        self.assertNotContains(response,
                               courses[2].extended_object.get_title())

        # There is a link to view more related courses directly in the Search view
        self.assertContains(
            response,
            f'href="/the/courses/?organizations={organization.get_es_id():s}"')
        self.assertContains(
            response,
            f"See all courses related to {organization.extended_object.get_title():s}",
        )
Esempio n. 20
0
    def test_models_organization_get_courses_language_fallback_published(self):
        """
        Validate that the reverse courses lookup works as expected with language fallback
        on a published page.
        """
        organization1, organization2, organization3 = OrganizationFactory.create_batch(
            3, should_publish=True)
        public_organization1 = organization1.public_extension
        public_organization2 = organization2.public_extension
        public_organization3 = organization3.public_extension

        course, course_unpublished = CourseFactory.create_batch(
            2, page_languages=["en", "fr", "de"], should_publish=True)

        public_course = course.public_extension

        public_course_unpublished = course_unpublished.public_extension
        course_unpublished.extended_object.unpublish("en")
        course_unpublished.extended_object.unpublish("fr")
        course_unpublished.extended_object.unpublish("de")

        placeholder = public_course.extended_object.placeholders.get(
            slot="course_organizations")
        placeholder_unpublished = (
            public_course_unpublished.extended_object.placeholders.get(
                slot="course_organizations"))
        # Reverse plugin lookups should fallback up to the second priority language
        add_plugin(
            language="de",
            placeholder=placeholder,
            plugin_type="OrganizationPlugin",
            **{"page": organization1.extended_object},
        )
        add_plugin(
            language="de",
            placeholder=placeholder_unpublished,
            plugin_type="OrganizationPlugin",
            **{"page": organization1.extended_object},
        )

        with translation.override("en"):
            self.assertEqual(list(public_organization1.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()), [])

        with translation.override("fr"):
            self.assertEqual(list(public_organization1.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()), [])

        with translation.override("de"):
            self.assertEqual(list(public_organization1.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()), [])

        # Reverse plugin lookups should fallback to the first priority language if available
        # and ignore the second priority language unless it is the current language
        add_plugin(
            language="fr",
            placeholder=placeholder,
            plugin_type="OrganizationPlugin",
            **{"page": organization2.extended_object},
        )
        add_plugin(
            language="fr",
            placeholder=placeholder_unpublished,
            plugin_type="OrganizationPlugin",
            **{"page": organization2.extended_object},
        )
        with translation.override("en"):
            self.assertEqual(list(public_organization1.get_courses()), [])
            self.assertEqual(list(public_organization2.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization3.get_courses()), [])

        with translation.override("fr"):
            self.assertEqual(list(public_organization1.get_courses()), [])
            self.assertEqual(list(public_organization2.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization3.get_courses()), [])

        with translation.override("de"):
            self.assertEqual(list(public_organization1.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()), [])

        # Reverse plugin lookups should stick to the current language if available and
        # ignore plugins on fallback languages
        add_plugin(
            language="en",
            placeholder=placeholder,
            plugin_type="OrganizationPlugin",
            **{"page": organization3.extended_object},
        )
        add_plugin(
            language="en",
            placeholder=placeholder_unpublished,
            plugin_type="OrganizationPlugin",
            **{"page": organization3.extended_object},
        )
        with translation.override("en"):
            self.assertEqual(list(public_organization1.get_courses()), [])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()),
                             [public_course])

        with translation.override("fr"):
            self.assertEqual(list(public_organization1.get_courses()), [])
            self.assertEqual(list(public_organization2.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization3.get_courses()), [])

        with translation.override("de"):
            self.assertEqual(list(public_organization1.get_courses()),
                             [public_course])
            self.assertEqual(list(public_organization2.get_courses()), [])
            self.assertEqual(list(public_organization3.get_courses()), [])
Esempio n. 21
0
    def test_templates_program_detail_cms_draft_content(self):
        """
        A staff user should see a draft program including draft elements.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.extended_object

        # Publish only 2 out of 4 courses
        courses[0].extended_object.publish("en")
        courses[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        courses[3].extended_object.publish("en")
        courses[3].extended_object.unpublish("en")

        # The page should be visible as draft to the staff user
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertContains(
            response,
            "<title>Preums - Program - example.com</title>",
            html=True,
            status_code=200,
        )
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # Draft and published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<a class="course-glimpse__link" '
                f'href="{course.extended_object.get_absolute_url():s}"',
            )
            self.assertContains(
                response,
                '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                    course.extended_object.get_title()),
                html=True,
            )
        self.assertContains(
            response,
            '<div class="course-glimpse course-glimpse--draft">'
            '<div aria-hidden="true" class="course-glimpse__media">'
            f'<a tabindex="-1" href="{courses[2].extended_object.get_absolute_url():s}"',
        )

        self.assertContains(
            response,
            '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                courses[2].extended_object.get_title()),
            html=True,
        )
        # The unpublished course should not be present on the page
        self.assertNotContains(response,
                               courses[3].extended_object.get_title())