Ejemplo n.º 1
0
    def _find_articles_to_kill(self, lookup):
        """
        Finds the article to kill. If the article is associated with Digital Story then Digital Story will
        also be fetched. If the Digital Story has more takes then all of them would be fetched.

        :param lookup: query to find the main article to be killed
        :type lookup: dict
        :return: list of articles to be killed
        :rtype: list
        """

        archived_doc = self.find_one(req=None, **lookup)

        req = ParsedRequest()
        req.sort = '[("%s", -1)]' % config.VERSION
        archived_doc = list(
            self.get(req=req, lookup={'item_id': archived_doc['item_id']}))[0]
        articles_to_kill = [archived_doc]
        takes_package_service = TakesPackageService()
        takes_package_id = takes_package_service.get_take_package_id(
            archived_doc)
        if takes_package_id:
            takes_package = list(
                self.get(req=req, lookup={'item_id': takes_package_id}))[0]
            articles_to_kill.append(takes_package)

            for takes_ref in takes_package_service.get_package_refs(
                    takes_package):
                if takes_ref[RESIDREF] != archived_doc[GUID_FIELD]:
                    take = list(
                        self.get(req=req,
                                 lookup={'item_id': takes_ref[RESIDREF]}))[0]
                    articles_to_kill.append(take)

        return articles_to_kill
Ejemplo n.º 2
0
    def _find_articles_to_kill(self, lookup):
        """
        Finds the article to kill. If the article is associated with Digital Story then Digital Story will
        also be fetched. If the Digital Story has more takes then all of them would be fetched.

        :param lookup: query to find the main article to be killed
        :type lookup: dict
        :return: list of articles to be killed
        :rtype: list
        """

        archived_doc = self.find_one(req=None, **lookup)

        req = ParsedRequest()
        req.sort = '[("%s", -1)]' % config.VERSION
        archived_doc = list(self.get(req=req, lookup={'item_id': archived_doc['item_id']}))[0]
        articles_to_kill = [archived_doc]
        takes_package_service = TakesPackageService()
        takes_package_id = takes_package_service.get_take_package_id(archived_doc)
        if takes_package_id:
            takes_package = list(self.get(req=req, lookup={'item_id': takes_package_id}))[0]
            articles_to_kill.append(takes_package)

            for takes_ref in takes_package_service.get_package_refs(takes_package):
                if takes_ref[RESIDREF] != archived_doc[GUID_FIELD]:
                    take = list(self.get(req=req, lookup={'item_id': takes_ref[RESIDREF]}))[0]
                    articles_to_kill.append(take)

        return articles_to_kill
Ejemplo n.º 3
0
    def get_archived_takes_package(self, package_id, take_id, version, include_other_takes=True):
        req = ParsedRequest()
        req.sort = '[("%s", -1)]' % config.VERSION
        takes_package_service = TakesPackageService()
        take_packages = list(self.get(req=req, lookup={'item_id': package_id}))

        for take_package in take_packages:
            for ref in takes_package_service.get_package_refs(take_package):
                if ref[RESIDREF] == take_id and (include_other_takes or ref['_current_version'] == version):
                    return take_package
Ejemplo n.º 4
0
    def _validate_rewrite(self, original, update):
        """
        Validates the article to be rewritten
        :param original: article to be rewritten
        :param update: article as the rewrite
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(
                message='Cannot find the article')

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError(
                "Rewrite of an Item having embargo isn't possible")

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(
                message='Event id does not exist')

        if original.get('rewritten_by'):
            raise SuperdeskApiError.badRequestError(
                message='Article has been rewritten before !')

        if not is_workflow_state_transition_valid('rewrite',
                                                  original[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if not TakesPackageService().is_last_takes_package_item(original):
            raise SuperdeskApiError.badRequestError(
                message="Only last take of the package can be rewritten.")

        if original.get('rewrite_of') and not (original.get(ITEM_STATE)
                                               in PUBLISH_STATES):
            raise SuperdeskApiError.badRequestError(
                message=
                "Rewrite is not published. Cannot rewrite the story again.")

        if update:
            # in case of associate as update
            if update.get('rewrite_of'):
                raise SuperdeskApiError.badRequestError(
                    "Rewrite story has been used as update before !")

            if update.get(ITEM_STATE) in [
                    CONTENT_STATE.PUBLISHED, CONTENT_STATE.CORRECTED,
                    CONTENT_STATE.KILLED, CONTENT_STATE.SCHEDULED,
                    CONTENT_STATE.SPIKED
            ]:
                raise InvalidStateTransitionError()

            if update.get(ITEM_TYPE) not in [
                    CONTENT_TYPE.TEXT, CONTENT_TYPE.PREFORMATTED
            ]:
                raise SuperdeskApiError.badRequestError(
                    "Rewrite story can only be text or pre-formatted !")

            if update.get('genre') and \
                    any(genre.get('value', '').lower() == BROADCAST_GENRE.lower() for genre in update.get('genre')):
                raise SuperdeskApiError.badRequestError(
                    "Broadcast cannot be a update story !")
Ejemplo n.º 5
0
    def create(self, docs, **kwargs):
        doc = docs[0] if len(docs) > 0 else {}
        original_id = request.view_args['original_id']
        update_document = doc.get('update')

        archive_service = get_resource_service(ARCHIVE)
        original = archive_service.find_one(req=None, _id=original_id)
        self._validate_rewrite(original, update_document)

        digital = TakesPackageService().get_take_package(original)
        rewrite = self._create_rewrite_article(original,
                                               digital,
                                               existing_item=update_document,
                                               desk_id=doc.get('desk_id'))

        if update_document:
            # process the existing story
            archive_service.patch(update_document[config.ID_FIELD], rewrite)
            rewrite[config.ID_FIELD] = update_document[config.ID_FIELD]
            ids = [update_document[config.ID_FIELD]]
        else:
            ids = archive_service.post([rewrite])
            build_custom_hateoas(CUSTOM_HATEOAS, rewrite)

        self._add_rewritten_flag(original, digital, rewrite)
        get_resource_service('archive_broadcast').on_broadcast_master_updated(
            ITEM_CREATE, item=original, rewrite_id=ids[0])
        return [rewrite]
Ejemplo n.º 6
0
    def _validate_rewrite(self, original):
        """
        Validates the article to be rewritten
        :param original: article to be rewritten
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(
                message='Cannot find the article')

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError(
                "Rewrite of an Item having embargo isn't possible")

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(
                message='Event id does not exist')

        if get_resource_service('published').is_rewritten_before(
                original['_id']):
            raise SuperdeskApiError.badRequestError(
                message='Article has been rewritten before !')

        if not is_workflow_state_transition_valid('rewrite',
                                                  original[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if not TakesPackageService().is_last_takes_package_item(original):
            raise SuperdeskApiError.badRequestError(
                message="Only last take of the package can be rewritten.")
Ejemplo n.º 7
0
 def create(self, docs, **kwargs):
     original_id = request.view_args['original_id']
     archive_service = get_resource_service('archive')
     original = archive_service.find_one(req=None, _id=original_id)
     self._validate_rewrite(original)
     digital = TakesPackageService().get_take_package(original)
     rewrite = self._create_rewrite_article(original, digital)
     archive_service.post([rewrite])
     build_custom_hateoas(CUSTOM_HATEOAS, rewrite)
     self._add_rewritten_flag(original, digital, rewrite)
     return [rewrite]
Ejemplo n.º 8
0
 def create(self, docs, **kwargs):
     original_id = request.view_args['original_id']
     archive_service = get_resource_service(ARCHIVE)
     original = archive_service.find_one(req=None, _id=original_id)
     self._validate_rewrite(original)
     digital = TakesPackageService().get_take_package(original)
     rewrite = self._create_rewrite_article(original, digital)
     ids = archive_service.post([rewrite])
     build_custom_hateoas(CUSTOM_HATEOAS, rewrite)
     self._add_rewritten_flag(original, digital, rewrite)
     get_resource_service('archive_broadcast').on_broadcast_master_updated(
         ITEM_CREATE, item=original, rewrite_id=ids[0])
     return [rewrite]
Ejemplo n.º 9
0
    def move_content(self, id, doc):
        archive_service = get_resource_service(ARCHIVE)
        archived_doc = archive_service.find_one(req=None, _id=id)

        if not archived_doc:
            raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' % id)

        self._validate(archived_doc, doc)
        self._move(archived_doc, doc)

        # move the takes package where the first take is located.
        takes_service = TakesPackageService()
        takes_package = takes_service.get_take_package(archived_doc)
        if takes_package and \
                takes_service.get_take_by_take_no(archived_doc, package=takes_package) == id:
            self._move(takes_package, doc)

        # get the recent updates again
        archived_doc = archive_service.find_one(req=None, _id=id)
        # finally apply any on stage rules/macros
        apply_onstage_rule(archived_doc, id)

        return archived_doc
Ejemplo n.º 10
0
    def move_content(self, id, doc):
        archive_service = get_resource_service(ARCHIVE)
        archived_doc = archive_service.find_one(req=None, _id=id)

        if not archived_doc:
            raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' % id)

        self._validate(archived_doc, doc)
        self._move(archived_doc, doc)

        # move the takes package where the first take is located.
        takes_service = TakesPackageService()
        takes_package = takes_service.get_take_package(archived_doc)
        if takes_package and \
                takes_service.get_take_by_take_no(archived_doc, package=takes_package) == id:
            self._move(takes_package, doc)

        # get the recent updates again
        archived_doc = archive_service.find_one(req=None, _id=id)
        # finally apply any on stage rules/macros
        apply_onstage_rule(archived_doc, id)

        return archived_doc
Ejemplo n.º 11
0
 def _inject_take(self, item, take1):
     """
     Given the item and a the first take, link the item into the package sequence and publish it.
     :param item:
     :param take1:
     :return:
     """
     item.pop(LINKED_IN_PACKAGES, None)
     get_resource_service('archive').post([item])
     take1_item = get_resource_service('archive').find_one(
         req=None, _id=take1.get(RESIDREF))
     TakesPackageService().link_as_next_take(take1_item, item)
     get_resource_service('archive_publish').patch(
         id=item[config.ID_FIELD],
         updates={
             ITEM_STATE: CONTENT_STATE.PUBLISHED,
             'auto_publish': True
         })
Ejemplo n.º 12
0
    def test_can_remove_from_production_second_rule(self):
        """
        Test if can_remove_production() returns false when the expired published item is part of a package.
        """

        doc = self.articles[0].copy()

        get_resource_service(ARCHIVE_PUBLISH).patch(
            id=doc[config.ID_FIELD],
            updates={
                ITEM_STATE: CONTENT_STATE.PUBLISHED,
                config.VERSION: doc[config.VERSION] + 1
            })

        item_in_production = get_resource_service(ARCHIVE).find_one(
            req=None, _id=doc[config.ID_FIELD])
        self.assertIsNotNone(
            TakesPackageService().get_take_package_id(item_in_production))

        self._move_to_archived_and_assert_can_remove_from_production(
            doc[config.ID_FIELD], self.assertFalse)
Ejemplo n.º 13
0
    def on_delete(self, doc):
        """
        Overriding to validate the item being killed is actually eligible for kill. Validates the following:
            1. Is item of type Text?
            2. Is item a Broadcast Script?
            3. Does item acts as a Master Story for any of the existing broadcasts?
            4. Is item available in production or part of a normal package?
            5. Is the associated Digital Story is available in production or part of normal package?
            6. If item is a Take then is any take available in production or part of normal package?
        :param doc: represents the article in archived collection
        :type doc: dict
        :raises SuperdeskApiError.badRequestError() if any of the above validation conditions fail.
        """

        bad_req_error = SuperdeskApiError.badRequestError

        id_field = doc[config.ID_FIELD]
        item_id = doc['item_id']

        doc['item_id'] = id_field
        doc[config.ID_FIELD] = item_id

        if doc[ITEM_TYPE] != CONTENT_TYPE.TEXT:
            raise bad_req_error(
                message=
                'Only Text articles are allowed to Kill in Archived repo')

        if is_genre(doc, BROADCAST_GENRE):
            raise bad_req_error(
                message=
                "Killing of Broadcast Items isn't allowed in Archived repo")

        if get_resource_service(
                'archive_broadcast').get_broadcast_items_from_master_story(
                    doc, True):
            raise bad_req_error(
                message=
                "Can't kill as this article acts as a Master Story for existing broadcast(s)"
            )

        if get_resource_service(ARCHIVE).find_one(req=None,
                                                  _id=doc[GUID_FIELD]):
            raise bad_req_error(
                message="Can't Kill as article is still available in production"
            )

        if is_item_in_package(doc):
            raise bad_req_error(
                message="Can't kill as article is part of a Package")

        takes_package_service = TakesPackageService()
        takes_package_id = takes_package_service.get_take_package_id(doc)
        if takes_package_id:
            if get_resource_service(ARCHIVE).find_one(req=None,
                                                      _id=takes_package_id):
                raise bad_req_error(
                    message=
                    "Can't Kill as the Digital Story is still available in production"
                )

            req = ParsedRequest()
            req.sort = '[("%s", -1)]' % config.VERSION
            takes_package = list(
                self.get(req=req, lookup={'item_id': takes_package_id}))
            if not takes_package:
                raise bad_req_error(
                    message=
                    'Digital Story of the article not found in Archived repo')

            takes_package = takes_package[0]
            if is_item_in_package(takes_package):
                raise bad_req_error(
                    message="Can't kill as Digital Story is part of a Package")

            for takes_ref in takes_package_service.get_package_refs(
                    takes_package):
                if takes_ref[RESIDREF] != doc[GUID_FIELD]:
                    if get_resource_service(ARCHIVE).find_one(
                            req=None, _id=takes_ref[RESIDREF]):
                        raise bad_req_error(
                            message=
                            "Can't Kill as Take(s) are still available in production"
                        )

                    take = list(
                        self.get(req=None,
                                 lookup={'item_id': takes_ref[RESIDREF]}))
                    if not take:
                        raise bad_req_error(
                            message='One of Take(s) not found in Archived repo'
                        )

                    if is_item_in_package(take[0]):
                        raise bad_req_error(
                            message=
                            "Can't kill as one of Take(s) is part of a Package"
                        )

        doc['item_id'] = item_id
        doc[config.ID_FIELD] = id_field
Ejemplo n.º 14
0
    def _process_item(self, item):
        """
        Process an item that has been retrieved from the remote instance of Superdesk
        :param item:
        :return:
        """
        if '_id' in item:
            print(
                "\n\n\n\nProcessing {} headline:[{}] slugline:[{}] takekey:[{} state:{}]"
                .format(item['_id'], item.get('headline', ''),
                        item.get('slugline', ''),
                        item.get('anpa_take_key', ''), item.get('state', '')))
            if 'rewritten_by' in item:
                print('Item has been rewritten so ignoring it')
                return

            if item['state'] != 'published':
                print('State:  {}  id: {}'.format(item.get('state', ''),
                                                  item.get('_id', '')))

            if (item.get('state', '')) == 'killed':
                print("Item has been killed, ignoring it")

            fields_to_remove = ('unique_name', 'unique_id', 'takes', '_etag',
                                '_type', '_current_version', '_updated')
            for field in fields_to_remove:
                item.pop(field, None)
            item['state'] = 'in_progress'
            item['_current_version'] = 1

            service = get_resource_service('archive')

            local_item = service.find_one(req=None, _id=item.get('_id'))
            if local_item is None:
                # Test if the item is linked into any takes package
                if 'linked_in_packages' in item and len(item.get('linked_in_packages', [])) > 0 and \
                        any(p.get('package_type', '') == 'takes' for p in item.get('linked_in_packages', [])):
                    pkg_id = TakesPackageService().get_take_package_id(item)
                    remote_pkg = self._get_remote_package(pkg_id)
                    refs = TakesPackageService().get_package_refs(remote_pkg)
                    take1 = next(
                        (ref for ref in refs if ref.get(SEQUENCE) == 1), None)
                    if LAST_TAKE not in remote_pkg or remote_pkg[
                            LAST_TAKE] == item[config.ID_FIELD]:
                        if len(refs) == 1:
                            # simple safe to publish single take
                            print("Single take")
                            self._inject_item(item)
                        elif len(refs) > 1:
                            print("Last take")
                            self._inject_take(item, take1)
                    else:
                        if take1.get(RESIDREF) == item[config.ID_FIELD]:
                            print("Take 1")
                            self._inject_item(item)
                        else:
                            print("Other take")
                            self._inject_take(item, take1)
                else:
                    print("Item is not linked in packages {}".format(
                        item.get('_id', '')))
                    self._inject_item(item)
            else:
                print("Already Imported")
        else:
            print("Rubish item {}".format(item))
Ejemplo n.º 15
0
    def on_delete(self, doc):
        """
        Overriding to validate the item being killed is actually eligible for kill. Validates the following:
            1. Is item of type Text?
            2. Is item a Broadcast Script?
            3. Does item acts as a Master Story for any of the existing broadcasts?
            4. Is item available in production or part of a normal package?
            5. Is the associated Digital Story is available in production or part of normal package?
            6. If item is a Take then is any take available in production or part of normal package?
        :param doc: represents the article in archived collection
        :type doc: dict
        :raises SuperdeskApiError.badRequestError() if any of the above validation conditions fail.
        """

        bad_req_error = SuperdeskApiError.badRequestError

        id_field = doc[config.ID_FIELD]
        item_id = doc['item_id']

        doc['item_id'] = id_field
        doc[config.ID_FIELD] = item_id

        if doc[ITEM_TYPE] != CONTENT_TYPE.TEXT:
            raise bad_req_error(message='Only Text articles are allowed to Kill in Archived repo')

        if is_genre(doc, BROADCAST_GENRE):
            raise bad_req_error(message="Killing of Broadcast Items isn't allowed in Archived repo")

        if get_resource_service('archive_broadcast').get_broadcast_items_from_master_story(doc, True):
            raise bad_req_error(message="Can't kill as this article acts as a Master Story for existing broadcast(s)")

        if get_resource_service(ARCHIVE).find_one(req=None, _id=doc[GUID_FIELD]):
            raise bad_req_error(message="Can't Kill as article is still available in production")

        if is_item_in_package(doc):
            raise bad_req_error(message="Can't kill as article is part of a Package")

        takes_package_service = TakesPackageService()
        takes_package_id = takes_package_service.get_take_package_id(doc)
        if takes_package_id:
            if get_resource_service(ARCHIVE).find_one(req=None, _id=takes_package_id):
                raise bad_req_error(message="Can't Kill as the Digital Story is still available in production")

            req = ParsedRequest()
            req.sort = '[("%s", -1)]' % config.VERSION
            takes_package = list(self.get(req=req, lookup={'item_id': takes_package_id}))
            if not takes_package:
                raise bad_req_error(message='Digital Story of the article not found in Archived repo')

            takes_package = takes_package[0]
            if is_item_in_package(takes_package):
                raise bad_req_error(message="Can't kill as Digital Story is part of a Package")

            for takes_ref in takes_package_service.get_package_refs(takes_package):
                if takes_ref[RESIDREF] != doc[GUID_FIELD]:
                    if get_resource_service(ARCHIVE).find_one(req=None, _id=takes_ref[RESIDREF]):
                        raise bad_req_error(message="Can't Kill as Take(s) are still available in production")

                    take = list(self.get(req=None, lookup={'item_id': takes_ref[RESIDREF]}))
                    if not take:
                        raise bad_req_error(message='One of Take(s) not found in Archived repo')

                    if is_item_in_package(take[0]):
                        raise bad_req_error(message="Can't kill as one of Take(s) is part of a Package")

        doc['item_id'] = item_id
        doc[config.ID_FIELD] = id_field