Esempio n. 1
0
    def handle(self, *args, **options):
        config = DrupalPublishUuidConfig.get_solo()

        # Publish course runs
        if config.course_run_ids:
            course_run_ids = config.course_run_ids.split(',')
            course_runs = CourseRun.objects.filter(key__in=course_run_ids)
            for course_run in course_runs:
                publisher = CourseRunMarketingSitePublisher(
                    course_run.course.partner)
                publisher.publish_obj(course_run, include_uuid=True)

        # Publish people
        if config.push_people:
            publisher = MarketingSitePeople()
            for person in Person.objects.all():
                logger.info('Updating person node %s [%s].', person.slug,
                            person.uuid)
                try:
                    publisher.update_person(person)
                except (PersonToMarketingException,
                        MarketingSiteAPIClientException):
                    logger.exception(
                        'An error occurred while updating person %s on the marketing site.',
                        person.full_name,
                    )
Esempio n. 2
0
    def save(self, *args, **kwargs):
        suppress_publication = kwargs.pop('suppress_publication', False)
        is_publishable = (
            self.course.partner.has_marketing_site and
            waffle.switch_is_active('publish_course_runs_to_marketing_site')
            and
            # Pop to clean the kwargs for the base class save call below
            not suppress_publication)

        if is_publishable:
            publisher = CourseRunMarketingSitePublisher(self.course.partner)
            previous_obj = CourseRun.objects.get(
                id=self.id) if self.id else None

            if not self.slug:
                # If we are publishing this object to marketing site,
                # let's make sure slug is defined
                self.slug = slugify(self.title)

            with transaction.atomic():
                super(CourseRun, self).save(*args, **kwargs)
                publisher.publish_obj(self, previous_obj=previous_obj)
        else:
            logger.info('Course run [%s] is not publishable.', self.key)
            super(CourseRun, self).save(*args, **kwargs)
Esempio n. 3
0
    def save(self, *args, **kwargs):
        suppress_publication = kwargs.pop('suppress_publication', False)
        is_publishable = (
            self.course.partner.has_marketing_site and
            waffle.switch_is_active('publish_course_runs_to_marketing_site') and
            # Pop to clean the kwargs for the base class save call below
            not suppress_publication
        )

        if is_publishable:
            publisher = CourseRunMarketingSitePublisher(self.course.partner)
            previous_obj = CourseRun.objects.get(id=self.id) if self.id else None

            with transaction.atomic():
                super(CourseRun, self).save(*args, **kwargs)
                publisher.publish_obj(self, previous_obj=previous_obj)
        else:
            super(CourseRun, self).save(*args, **kwargs)
class CourseRunMarketingSitePublisherTests(MarketingSitePublisherTestMixin):
    """
    Tests covering course run-specific publishing logic.
    """
    def setUp(self):
        super().setUp()

        self.partner = PartnerFactory()
        self.publisher = CourseRunMarketingSitePublisher(self.partner)

        self.api_root = self.publisher.client.api_url
        self.username = self.publisher.client.username

        self.obj = CourseRunFactory()

    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'node_id',
                       return_value='node_id')
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'serialize_obj',
                       return_value='data')
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'edit_node',
                       return_value=None)
    def test_publish_obj(self, mock_edit_node, *args):  # pylint: disable=unused-argument
        """
        Verify that the publisher attempts to publish when course run status changes.
        """
        # No previous object. No editing should occur.
        self.publisher.publish_obj(self.obj)
        assert not mock_edit_node.called

        # A previous object is provided, but the status hasn't changed.
        # No editing should occur.
        self.publisher.publish_obj(self.obj, previous_obj=self.obj)
        assert not mock_edit_node.called

        # A previous object is provided, and the status has changed.
        # Editing should occur.
        previous_obj = CourseRunFactory(status=CourseRunStatus.Unpublished)
        self.publisher.publish_obj(self.obj, previous_obj=previous_obj)
        mock_edit_node.assert_called_with('node_id', 'data')

    @responses.activate
    def test_serialize_obj(self):
        """
        Verify that the publisher serializes data required to publish course runs.
        """
        self.mock_api_client()

        actual = self.publisher.serialize_obj(self.obj)
        expected = {
            'field_course_id': self.obj.key,
            'author': {
                'id': self.user_id
            },
            'status': 1,
        }

        assert actual == expected

        self.obj.status = CourseRunStatus.Unpublished

        actual = self.publisher.serialize_obj(self.obj)
        expected['status'] = 0

        assert actual == expected
class CourseRunMarketingSitePublisherTests(MarketingSitePublisherTestMixin):
    """
    Tests covering course run-specific publishing logic.
    """
    def setUp(self):
        super().setUp()

        self.partner = PartnerFactory()
        self.publisher = CourseRunMarketingSitePublisher(self.partner)

        self.api_root = self.publisher.client.api_url
        self.username = self.publisher.client.username

        self.obj = CourseRunFactory()

    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'node_id',
                       return_value=None)
    @mock.patch.object(CourseRunMarketingSitePublisher, 'create_node')
    def test_publish_obj_create_disabled(self, mock_create_node, mock_node_id):
        self.publisher.publish_obj(self.obj)
        mock_node_id.assert_called_with(self.obj)
        assert not mock_create_node.called

    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'serialize_obj',
                       return_value={'data': 'test'})
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'node_id',
                       return_value=None)
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'create_node',
                       return_value='node_id')
    @mock.patch.object(CourseRunMarketingSitePublisher, 'update_node_alias')
    @override_switch('auto_course_about_page_creation', True)
    def test_publish_obj_create_successful(self, mock_update_node_alias,
                                           mock_create_node, *args):  # pylint: disable=unused-argument
        self.publisher.publish_obj(self.obj)
        mock_create_node.assert_called_with({
            'data':
            'test',
            'field_course_uuid':
            str(self.obj.uuid)
        })
        mock_update_node_alias.assert_called_with(self.obj, 'node_id', None)

    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'node_id',
                       return_value=None)
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'serialize_obj',
                       return_value={'data': 'test'})
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'create_node',
                       return_value='node1')
    @mock.patch.object(CourseRunMarketingSitePublisher, 'update_node_alias')
    @override_switch('auto_course_about_page_creation', True)
    def test_publish_obj_create_if_exists_on_discovery(self,
                                                       mock_update_node_alias,
                                                       mock_create_node,
                                                       mock_serialize_obj,
                                                       mock_node_id, *args):  # pylint: disable=unused-argument
        self.publisher.publish_obj(self.obj)
        mock_node_id.assert_called_with(self.obj)
        mock_serialize_obj.assert_called_with(self.obj)
        mock_create_node.assert_called_with({
            'data':
            'test',
            'field_course_uuid':
            str(self.obj.uuid)
        })
        mock_update_node_alias.assert_called_with(self.obj, 'node1', None)

    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'node_id',
                       return_value='node_id')
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'serialize_obj',
                       return_value='data')
    @mock.patch.object(CourseRunMarketingSitePublisher,
                       'edit_node',
                       return_value=None)
    @mock.patch.object(CourseRunMarketingSitePublisher, 'update_node_alias')
    def test_publish_obj_edit(self, mock_node_alias, mock_edit_node, *args):  # pylint: disable=unused-argument
        """
        Verify that the publisher attempts to publish when course run status changes.
        """

        # A previous object is provided, but the status hasn't changed.
        # No editing should occur.
        self.publisher.publish_obj(self.obj, previous_obj=self.obj)
        assert not mock_edit_node.called

        # A previous object is provided, and the status has changed.
        # Editing should occur.
        previous_obj = CourseRunFactory(status=CourseRunStatus.Unpublished)
        self.publisher.publish_obj(self.obj, previous_obj=previous_obj)
        mock_edit_node.assert_called_with('node_id', 'data')
        mock_node_alias.assert_called_with(self.obj, 'node_id', None)

    @responses.activate
    def test_serialize_obj(self):
        """
        Verify that the publisher serializes data required to publish course runs.
        """
        self.mock_api_client()

        actual = self.publisher.serialize_obj(self.obj)
        expected = {
            'field_course_id': self.obj.key,
            'title': self.obj.title,
            'author': {
                'id': self.user_id
            },
            'status': 1,
            'type': 'course',
        }

        assert actual == expected

        self.obj.status = CourseRunStatus.Unpublished

        actual = self.publisher.serialize_obj(self.obj)
        expected['status'] = 0

        assert actual == expected

    @responses.activate
    def test_update_node_alias(self):
        """
        Verify that the publisher attempts to create a new alias associated with the new course_run,
        and that appropriate exceptions are raised for non-200 status codes.
        """
        # No previous object is provided. Create a new node and make sure
        # title alias created, by default, based on the title is deleted
        # and a new alias based on marketing slug is created.
        self.mock_api_client()
        self.mock_get_alias_form()
        self.mock_get_delete_form(self.obj.slug)
        self.mock_delete_alias()
        self.mock_get_delete_form(self.obj.slug)
        self.mock_add_alias()

        self.publisher.update_node_alias(self.obj, self.node_id, None)

        assert responses.calls[-1].request.url == '{}/add'.format(
            self.publisher.alias_api_base)

        responses.reset()

        # Same scenario, but this time a non-200 status code is returned during
        # alias creation. An exception should be raised.
        self.mock_api_client()
        self.mock_get_alias_form()
        self.mock_get_delete_form(self.obj.slug)
        self.mock_delete_alias()
        self.mock_get_delete_form(self.obj.slug)
        self.mock_add_alias(status=500)

        with pytest.raises(AliasCreateError):
            self.publisher.update_node_alias(self.obj, self.node_id, None)

        responses.reset()

        # A previous object is provided, but the marketing slug hasn't changed.
        # Neither alias creation nor alias deletion should occur.
        self.mock_api_client()
        self.mock_get_delete_form(self.obj.slug)

        self.publisher.update_node_alias(self.obj, self.node_id, self.obj)

        responses.reset()

        # In this case, similate the fact that alias form retrival returned error
        # FormRetrievalError should be raised
        self.mock_api_client()
        self.mock_get_delete_form(self.obj.slug)
        self.mock_get_alias_form(status=500)
        with pytest.raises(FormRetrievalError):
            self.publisher.update_node_alias(self.obj, self.node_id, None)

    def test_alias(self):
        """
        Verify that aliases are constructed correctly.
        """
        actual = self.publisher.alias(self.obj)
        expected = 'course/{slug}'.format(slug=self.obj.slug)

        assert actual == expected