def setUp(self): self.formatter = NewsroomNinjsFormatter() init_app(self.app) self.maxDiff = None
class NinjsFormatterTest(TestCase): def setUp(self): self.formatter = NewsroomNinjsFormatter() init_app(self.app) self.maxDiff = None def test_products(self): self.app.data.insert( 'content_filters', [{ "_id": 3, "content_filter": [{ "expression": { "pf": [1], "fc": [2] } }], "name": "soccer-only3" }]) self.app.data.insert('filter_conditions', [{ '_id': 1, 'field': 'headline', 'operator': 'like', 'value': 'test', 'name': 'test-1' }]) self.app.data.insert('filter_conditions', [{ '_id': 2, 'field': 'urgency', 'operator': 'in', 'value': '2', 'name': 'test-2' }]) self.app.data.insert('products', [{ "_id": 1, "content_filter": { "filter_id": 3, "filter_type": "permitting" }, "name": "p-1", "product_type": "api" }]) self.app.data.insert('vocabularies', [{ "_id": "locators", "display_name": "Locators", "type": "unmanageable", "unique_field": "qcode", "items": [ { "is_active": True, "name": "NSW", "qcode": "NSW", "state": "New South Wales", "country": "Australia", "world_region": "Oceania", "group": "Australia" }, ], }]) embargo_ts = (utcnow() + timedelta(days=2)) article = { '_id': 'tag:aap.com.au:20150613:12345', 'guid': 'tag:aap.com.au:20150613:12345', '_current_version': 1, 'anpa_category': [{ 'qcode': 'a' }], 'source': 'AAP', 'headline': 'This is a test headline', 'byline': 'joe', 'slugline': 'slugline', 'subject': [{ 'qcode': '02011001', 'name': 'international court or tribunal', 'parent': None }, { 'qcode': '02011002', 'name': 'extradition' }], 'anpa_take_key': 'take_key', 'unique_id': '1', 'body_html': 'The story body', 'type': 'text', 'word_count': '1', 'priority': 1, 'profile': 'snap', 'state': 'published', 'urgency': 2, 'pubstatus': 'usable', 'creditline': 'sample creditline', 'keywords': ['traffic'], 'abstract': '<p>sample <b>abstract</b></p>', 'place': [{ 'name': 'NSW', 'qcode': 'NSW' }], 'embargo': embargo_ts, 'body_footer': '<p>call helpline 999 if you are planning to quit smoking</p>', 'company_codes': [{ 'name': 'YANCOAL AUSTRALIA LIMITED', 'qcode': 'YAL', 'security_exchange': 'ASX' }], 'genre': [{ 'name': 'Article', 'qcode': 'article' }], 'flags': { 'marked_for_legal': True }, 'extra': { 'foo': 'test' }, 'operation': 'publish' } seq, doc = self.formatter.format(article, {'name': 'Test Subscriber'})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{ "code": "NSW", "name": "New South Wales" }], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [{ "code": "02011001", "name": "international court or tribunal" }, { "code": "02011002", "name": "extradition" }], "service": [{ "code": "a" }], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 2, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", 'keywords': ['traffic'], 'organisation': [{ 'name': 'YANCOAL AUSTRALIA LIMITED', 'rel': 'Securities Identifier', 'symbols': [{ 'ticker': 'YAL', 'exchange': 'ASX' }] }], 'genre': [{ 'name': 'Article', 'code': 'article' }], 'signal': [{ 'name': 'Content Warning', 'code': 'cwarn', 'scheme': 'http://cv.iptc.org/newscodes/signal/' }], 'extra': { 'foo': 'test' }, 'charcount': 67, 'wordcount': 13, 'readtime': 0, 'products': [{ 'code': 1, 'name': 'p-1' }] } self.assertEqual(json.loads(doc), expected) article['urgency'] = 1 seq, doc = self.formatter.format(article, {'name': 'Test Subscriber'})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{ "code": "NSW", "name": "New South Wales" }], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [{ "code": "02011001", "name": "international court or tribunal" }, { "code": "02011002", "name": "extradition" }], "service": [{ "code": "a" }], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 1, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", 'keywords': ['traffic'], 'organisation': [{ 'name': 'YANCOAL AUSTRALIA LIMITED', 'rel': 'Securities Identifier', 'symbols': [{ 'ticker': 'YAL', 'exchange': 'ASX' }] }], 'genre': [{ 'name': 'Article', 'code': 'article' }], 'signal': [{ 'name': 'Content Warning', 'code': 'cwarn', 'scheme': 'http://cv.iptc.org/newscodes/signal/' }], 'extra': { 'foo': 'test' }, 'charcount': 67, 'wordcount': 13, 'readtime': 0, 'products': [] } self.assertEqual(json.loads(doc), expected) def test_planning_data(self): planning_assignments.init_app(self.app) planning_planning.init_app(self.app) assignments = [{ 'coverage_item': 'urn:coverage-id', 'planning_item': 'urn:planning-id' }] self.app.data.insert('assignments', assignments) article = { '_id': 'tag:aap.com.au:20150613:12345', 'guid': 'tag:aap.com.au:20150613:12345', 'type': 'text', 'version': 1, 'assignment_id': assignments[0]['_id'], } seq, doc = self.formatter.format(article, {'name': 'Test Subscriber'})[0] data = json.loads(doc) self.assertEqual('urn:planning-id', data['planning_id']) self.assertEqual('urn:coverage-id', data['coverage_id']) def test_picture_formatter(self): article = { "guid": "20150723001158606583", "_current_version": 1, "slugline": "AMAZING PICTURE", "original_source": "AAP", "renditions": { "viewImage": { "width": 640, "href": "http://localhost:5000/api/upload/55b032041d41c8d278d21b6f/raw?_schema=http", "mimetype": "image/jpeg", "height": 401, }, "original": { "href": "https://one-api.aap.com.au/api/v3/Assets/20150723001158606583/Original/download", "mimetype": "image/jpeg", }, }, "byline": "MICKEY MOUSE", "headline": "AMAZING PICTURE", "versioncreated": "2015-07-23T00:15:00.000Z", "ednote": "TEST ONLY", "type": "picture", "pubstatus": "usable", "source": "AAP", "description": "The most amazing picture you will ever see", "guid": "20150723001158606583", "body_footer": "<p>call helpline 999 if you are planning to quit smoking</p>", } seq, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] expected = { "byline": "MICKEY MOUSE", "renditions": { "original": { "href": "https://one-api.aap.com.au/api/v3/Assets/20150723001158606583/Original/download", "mimetype": "image/jpeg", }, "viewImage": { "href": "http://localhost:5000/api/upload/55b032041d41c8d278d21b6f/raw?_schema=http", "mimetype": "image/jpeg", "width": 640, "height": 401, } }, "headline": "AMAZING PICTURE", "pubstatus": "usable", "version": "1", "versioncreated": "2015-07-23T00:15:00.000Z", "guid": "20150723001158606583", "description_html": "The most amazing picture you will ever see<p>call helpline 999 if you are planning to " "quit smoking</p>", "type": "picture", "priority": 5, "slugline": "AMAZING PICTURE", "ednote": "TEST ONLY", "source": "AAP", "products": [], } self.assertEqual(expected, json.loads(doc)) self.assertIn('viewImage', json.loads(doc).get('renditions')) def test_auto_published_item(self): article = { "guid": "foo", "_current_version": 1, "slugline": "AMAZING PICTURE", "original_source": "AAP", "byline": "MICKEY MOUSE", "headline": "AMAZING PICTURE", "versioncreated": "2015-07-23T00:15:00.000Z", "ednote": "TEST ONLY", "type": "picture", "pubstatus": "usable", "source": "AAP", "description": "The most amazing picture you will ever see", "body_footer": "<p>call helpline 999 if you are planning to quit smoking</p>", } _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed['guid'], 'foo') article['ingest_id'] = 'bar' article['ingest_version'] = '7' _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed['guid'], 'foo') article['auto_publish'] = True _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed['guid'], 'bar') self.assertEqual(processed['version'], '7')
class PublishService(BaseService): """A service for publishing to the content api. Serves mainly as a proxy to the data layer. """ formatter = NewsroomNinjsFormatter() subscriber: Dict[str, Dict] = {"config": {}} def publish(self, item, subscribers=None): """Publish an item to content api. This must be enabled via ``PUBLISH_TO_CONTENT_API`` setting. :param item: item to publish """ if subscribers is None: subscribers = [] if not self._filter_item(item): item = EnqueueService.filter_document(item) item = remove_metadata_for_publish(item) doc = self.formatter._transform_to_ninjs(item, self.subscriber) now = utcnow() doc.setdefault("firstcreated", now) doc.setdefault("versioncreated", now) doc.setdefault(config.VERSION, item.get(config.VERSION, 1)) for _, assoc in doc.get(ASSOCIATIONS, {}).items(): if assoc: assoc.setdefault("subscribers", [ str(subscriber[config.ID_FIELD]) for subscriber in subscribers ]) doc["subscribers"] = [str(sub["_id"]) for sub in subscribers] if "evolvedfrom" in doc: parent_item = self.find_one(req=None, _id=doc["evolvedfrom"]) if parent_item: doc["ancestors"] = copy(parent_item.get("ancestors", [])) doc["ancestors"].append(doc["evolvedfrom"]) doc["bookmarks"] = parent_item.get("bookmarks", []) else: logger.warning( "Failed to find evolvedfrom item '{}' for '{}'".format( doc["evolvedfrom"], doc["guid"])) self._assign_associations(item, doc) logger.info("publishing %s to %s" % (doc["guid"], subscribers)) _id = self._create_doc(doc) if "evolvedfrom" in doc and parent_item: self.system_update(parent_item["_id"], {"nextversion": _id}, parent_item) return _id else: return None def create(self, docs, **kwargs): ids = [] for doc in docs: ids.append(self._create_doc(doc, **kwargs)) return ids def _create_doc(self, doc, **kwargs): """Create a new item or update existing.""" item = copy(doc) item.setdefault("_id", item.get("guid")) _id = item[config.ID_FIELD] = item.pop("guid") # merging the existing and new subscribers original = self.find_one(req=None, _id=_id) if original: item["subscribers"] = list( set(original.get("subscribers", [])) | set(item.get("subscribers", []))) self._process_associations(item, original) self._create_version_doc(item) if original: self.update(_id, item, original) return _id else: return super().create([item], **kwargs)[0] def _create_version_doc(self, item): """ Store the item in the item version collection :param item: :return: """ version_item = copy(item) version_item["_id_document"] = version_item.pop("_id") get_resource_service("items_versions").create([version_item]) # if the update is a cancel we need to cancel all versions if item.get("pubstatus", "") == "canceled": self._cancel_versions(item.get("_id")) def _cancel_versions(self, doc_id): """ Given an id of a document set the pubstatus to canceled for all versions :param doc_id: :return: """ query = {"_id_document": doc_id} update = {"pubstatus": "canceled"} for item in get_resource_service("items_versions").get_from_mongo( req=None, lookup=query): if item.get("pubstatus") != "canceled": get_resource_service("items_versions").update( item["_id"], update, item) def _filter_item(self, item): """ Filter the item out if it matches any API Block filter conditions :param item: :return: True of the item is blocked, False if it is OK to publish it on the API. """ # Get the API blocking Filters req = ParsedRequest() filter_conditions = list( get_resource_service("content_filters").get( req=req, lookup={"api_block": True})) # No API blocking filters if not filter_conditions: return False filter_service = get_resource_service("content_filters") for fc in filter_conditions: if filter_service.does_match(fc, item): logger.info("API Filter block {} matched for item {}.".format( fc, item.get(config.ID_FIELD))) return True return False def _assign_associations(self, item, doc): """Assign Associations to published item :param dict item: item being published :param dit doc: ninjs documents """ for assoc, assoc_item in (item.get("associations") or {}).items(): if not assoc_item: continue doc.get("associations", {}).get(assoc)["subscribers"] = list( map(str, assoc_item.get("subscribers") or [])) def _process_associations(self, updates, original): """Update associations using existing published item and ensure that associated item subscribers are equal or subset of the parent subscribers. :param updates: :param original: :return: """ subscribers = updates.get("subscribers") or [] for assoc, update_assoc in (updates.get("associations") or {}).items(): if not update_assoc: continue if original: original_assoc = (original.get("associations") or {}).get(assoc) if original_assoc and original_assoc.get( config.ID_FIELD) == update_assoc.get(config.ID_FIELD): update_assoc["subscribers"] = list( set(original_assoc.get("subscribers") or []) | set(update_assoc.get("subscribers") or [])) update_assoc["subscribers"] = list( set(update_assoc["subscribers"]) & set(subscribers))
class NewsroomNinjsFormatterTest(TestCase): def setUp(self): self.formatter = NewsroomNinjsFormatter() init_app(self.app) self.maxDiff = None def test_products(self): self.app.data.insert( "content_filters", [{"_id": 3, "content_filter": [{"expression": {"pf": [1], "fc": [2]}}], "name": "soccer-only3"}], ) self.app.data.insert( "filter_conditions", [{"_id": 1, "field": "headline", "operator": "like", "value": "test", "name": "test-1"}], ) self.app.data.insert( "filter_conditions", [{"_id": 2, "field": "urgency", "operator": "in", "value": "2", "name": "test-2"}] ) self.app.data.insert( "products", [ { "_id": 1, "content_filter": {"filter_id": 3, "filter_type": "permitting"}, "name": "p-1", "product_type": "api", } ], ) self.app.data.insert( "vocabularies", [ { "_id": "locators", "display_name": "Locators", "type": "unmanageable", "unique_field": "qcode", "items": [ { "is_active": True, "name": "NSW", "qcode": "NSW", "state": "New South Wales", "country": "Australia", "world_region": "Oceania", "group": "Australia", }, ], } ], ) embargo_ts = utcnow() + timedelta(days=2) article = { "_id": "tag:aap.com.au:20150613:12345", "guid": "tag:aap.com.au:20150613:12345", "_current_version": 1, "anpa_category": [{"qcode": "a"}], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "slugline": "slugline", "subject": [ {"qcode": "02011001", "name": "international court or tribunal", "parent": None}, {"qcode": "02011002", "name": "extradition"}, ], "anpa_take_key": "take_key", "unique_id": "1", "body_html": "The story body", "type": "text", "word_count": "1", "priority": 1, "profile": "snap", "state": "published", "urgency": 2, "pubstatus": "usable", "creditline": "sample creditline", "keywords": ["traffic"], "abstract": "<p>sample <b>abstract</b></p>", "place": [{"name": "NSW", "qcode": "NSW"}], "embargo": embargo_ts, "body_footer": "<p>call helpline 999 if you are planning to quit smoking</p>", "company_codes": [{"name": "YANCOAL AUSTRALIA LIMITED", "qcode": "YAL", "security_exchange": "ASX"}], "genre": [{"name": "Article", "qcode": "article"}], "flags": {"marked_for_legal": True}, "extra": {"foo": "test"}, "operation": "publish", } seq, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{"code": "NSW", "name": "New South Wales"}], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [ {"code": "02011001", "name": "international court or tribunal"}, {"code": "02011002", "name": "extradition"}, ], "service": [{"code": "a"}], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 2, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", "keywords": ["traffic"], "organisation": [ { "name": "YANCOAL AUSTRALIA LIMITED", "rel": "Securities Identifier", "symbols": [{"ticker": "YAL", "exchange": "ASX"}], } ], "genre": [{"name": "Article", "code": "article"}], "signal": [{"name": "Content Warning", "code": "cwarn", "scheme": "http://cv.iptc.org/newscodes/signal/"}], "extra": {"foo": "test"}, "charcount": 67, "wordcount": 13, "readtime": 0, "products": [{"code": 1, "name": "p-1"}], } self.assertEqual(json.loads(doc), expected) article["urgency"] = 1 seq, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{"code": "NSW", "name": "New South Wales"}], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [ {"code": "02011001", "name": "international court or tribunal"}, {"code": "02011002", "name": "extradition"}, ], "service": [{"code": "a"}], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 1, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", "keywords": ["traffic"], "organisation": [ { "name": "YANCOAL AUSTRALIA LIMITED", "rel": "Securities Identifier", "symbols": [{"ticker": "YAL", "exchange": "ASX"}], } ], "genre": [{"name": "Article", "code": "article"}], "signal": [{"name": "Content Warning", "code": "cwarn", "scheme": "http://cv.iptc.org/newscodes/signal/"}], "extra": {"foo": "test"}, "charcount": 67, "wordcount": 13, "readtime": 0, "products": [], } self.assertEqual(json.loads(doc), expected) def test_planning_data(self): planning_assignments.init_app(self.app) planning_planning.init_app(self.app) assignments = [{"coverage_item": "urn:coverage-id", "planning_item": "urn:planning-id"}] self.app.data.insert("assignments", assignments) article = { "_id": "tag:aap.com.au:20150613:12345", "guid": "tag:aap.com.au:20150613:12345", "type": "text", "version": 1, "assignment_id": assignments[0]["_id"], } seq, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] data = json.loads(doc) self.assertEqual("urn:planning-id", data["planning_id"]) self.assertEqual("urn:coverage-id", data["coverage_id"]) def test_picture_formatter(self): article = { "guid": "20150723001158606583", "_current_version": 1, "slugline": "AMAZING PICTURE", "original_source": "AAP", "renditions": { "viewImage": { "width": 640, "href": "http://localhost:5000/api/upload/55b032041d41c8d278d21b6f/raw?_schema=http", "mimetype": "image/jpeg", "height": 401, }, "original": { "href": "https://one-api.aap.com.au/api/v3/Assets/20150723001158606583/Original/download", "mimetype": "image/jpeg", }, }, "byline": "MICKEY MOUSE", "headline": "AMAZING PICTURE", "versioncreated": "2015-07-23T00:15:00.000Z", "ednote": "TEST ONLY", "type": "picture", "pubstatus": "usable", "source": "AAP", "description": "The most amazing picture you will ever see", "guid": "20150723001158606583", "body_footer": "<p>call helpline 999 if you are planning to quit smoking</p>", } seq, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] expected = { "byline": "MICKEY MOUSE", "renditions": { "original": { "href": "https://one-api.aap.com.au/api/v3/Assets/20150723001158606583/Original/download", "mimetype": "image/jpeg", }, "viewImage": { "href": "http://localhost:5000/api/upload/55b032041d41c8d278d21b6f/raw?_schema=http", "mimetype": "image/jpeg", "width": 640, "height": 401, }, }, "headline": "AMAZING PICTURE", "pubstatus": "usable", "version": "1", "versioncreated": "2015-07-23T00:15:00.000Z", "guid": "20150723001158606583", "description_html": "The most amazing picture you will ever see<p>call helpline 999 if you are planning to " "quit smoking</p>", "type": "picture", "priority": 5, "slugline": "AMAZING PICTURE", "ednote": "TEST ONLY", "source": "AAP", "products": [], } self.assertEqual(expected, json.loads(doc)) self.assertIn("viewImage", json.loads(doc).get("renditions")) def test_auto_published_item(self): article = { "guid": "foo", "_current_version": 1, "slugline": "AMAZING PICTURE", "original_source": "AAP", "byline": "MICKEY MOUSE", "headline": "AMAZING PICTURE", "versioncreated": "2015-07-23T00:15:00.000Z", "ednote": "TEST ONLY", "type": "picture", "pubstatus": "usable", "source": "AAP", "description": "The most amazing picture you will ever see", "body_footer": "<p>call helpline 999 if you are planning to quit smoking</p>", } _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed["guid"], "foo") article["ingest_id"] = "bar" article["ingest_version"] = "7" _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed["guid"], "foo") article["auto_publish"] = True _, doc = self.formatter.format(article, {"name": "Test Subscriber"})[0] processed = json.loads(doc) self.assertEqual(processed["guid"], "bar") self.assertEqual(processed["version"], "7")
class NinjsFormatterTest(TestCase): def setUp(self): self.formatter = NewsroomNinjsFormatter() init_app(self.app) self.maxDiff = None def test_products(self): self.app.data.insert('content_filters', [{"_id": 3, "content_filter": [{"expression": {"pf": [1], "fc": [2]}}], "name": "soccer-only3"}]) self.app.data.insert('filter_conditions', [{'_id': 1, 'field': 'headline', 'operator': 'like', 'value': 'test', 'name': 'test-1'}]) self.app.data.insert('filter_conditions', [{'_id': 2, 'field': 'urgency', 'operator': 'in', 'value': '2', 'name': 'test-2'}]) self.app.data.insert('products', [{"_id": 1, "content_filter": {"filter_id": 3, "filter_type": "permitting"}, "name": "p-1", "product_type": "api"}]) self.app.data.insert('vocabularies', [ { "_id": "locators", "display_name": "Locators", "type": "unmanageable", "unique_field": "qcode", "items": [ {"is_active": True, "name": "NSW", "qcode": "NSW", "state": "New South Wales", "country": "Australia", "world_region": "Oceania", "group": "Australia"}, ], } ]) embargo_ts = (utcnow() + timedelta(days=2)) article = { '_id': 'tag:aap.com.au:20150613:12345', 'guid': 'tag:aap.com.au:20150613:12345', '_current_version': 1, 'anpa_category': [{'qcode': 'a'}], 'source': 'AAP', 'headline': 'This is a test headline', 'byline': 'joe', 'slugline': 'slugline', 'subject': [{'qcode': '02011001', 'name': 'international court or tribunal', 'parent': None}, {'qcode': '02011002', 'name': 'extradition'}], 'anpa_take_key': 'take_key', 'unique_id': '1', 'body_html': 'The story body', 'type': 'text', 'word_count': '1', 'priority': 1, 'profile': 'snap', 'state': 'published', 'urgency': 2, 'pubstatus': 'usable', 'creditline': 'sample creditline', 'keywords': ['traffic'], 'abstract': '<p>sample <b>abstract</b></p>', 'place': [{'name': 'NSW', 'qcode': 'NSW'}], 'embargo': embargo_ts, 'body_footer': '<p>call helpline 999 if you are planning to quit smoking</p>', 'company_codes': [{'name': 'YANCOAL AUSTRALIA LIMITED', 'qcode': 'YAL', 'security_exchange': 'ASX'}], 'genre': [{'name': 'Article', 'qcode': 'article'}], 'flags': {'marked_for_legal': True}, 'extra': {'foo': 'test'}, } seq, doc = self.formatter.format(article, {'name': 'Test Subscriber'})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{"code": "NSW", "name": "New South Wales"}], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [{"code": "02011001", "name": "international court or tribunal"}, {"code": "02011002", "name": "extradition"}], "service": [{"code": "a"}], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 2, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", 'keywords': ['traffic'], 'organisation': [{'name': 'YANCOAL AUSTRALIA LIMITED', 'rel': 'Securities Identifier', 'symbols': [{'ticker': 'YAL', 'exchange': 'ASX'}]}], 'genre': [{'name': 'Article', 'code': 'article'}], 'signal': [{'name': 'Content Warning', 'code': 'cwarn', 'scheme': 'http://cv.iptc.org/newscodes/signal/'}], 'extra': {'foo': 'test'}, 'charcount': 67, 'wordcount': 13, 'readtime': 0, 'products': [{'code': 1, 'name': 'p-1'}] } self.assertEqual(json.loads(doc), expected) article['urgency'] = 1 seq, doc = self.formatter.format(article, {'name': 'Test Subscriber'})[0] expected = { "guid": "tag:aap.com.au:20150613:12345", "version": "1", "place": [{"code": "NSW", "name": "New South Wales"}], "pubstatus": "usable", "body_html": "The story body<p>call helpline 999 if you are planning to quit smoking</p>", "type": "text", "subject": [{"code": "02011001", "name": "international court or tribunal"}, {"code": "02011002", "name": "extradition"}], "service": [{"code": "a"}], "source": "AAP", "headline": "This is a test headline", "byline": "joe", "urgency": 1, "priority": 1, "embargoed": embargo_ts.isoformat(), "profile": "snap", "slugline": "slugline", "description_text": "sample abstract", "description_html": "<p>sample <b>abstract</b></p>", 'keywords': ['traffic'], 'organisation': [{'name': 'YANCOAL AUSTRALIA LIMITED', 'rel': 'Securities Identifier', 'symbols': [{'ticker': 'YAL', 'exchange': 'ASX'}]}], 'genre': [{'name': 'Article', 'code': 'article'}], 'signal': [{'name': 'Content Warning', 'code': 'cwarn', 'scheme': 'http://cv.iptc.org/newscodes/signal/'}], 'extra': {'foo': 'test'}, 'charcount': 67, 'wordcount': 13, 'readtime': 0, 'products': [] } self.assertEqual(json.loads(doc), expected)