diff --git a/newsroom/agenda/agenda_service.py b/newsroom/agenda/agenda_service.py index 1a6a562e4..f081f8143 100644 --- a/newsroom/agenda/agenda_service.py +++ b/newsroom/agenda/agenda_service.py @@ -194,6 +194,7 @@ async def convert_planning_to_agenda_dict( plan[TO_BE_CONFIRMED_FIELD] = planning_item.get(TO_BE_CONFIRMED_FIELD) plan["language"] = planning_item.get("language") plan["source"] = planning_item.get("source") + plan["event_ids"] = [link["_id"] for link in (planning_item.get("related_events") or [])] if new_plan: agenda["planning_items"].append(plan) diff --git a/newsroom/push/publishing.py b/newsroom/push/publishing.py index 978f996a4..0ce78af6b 100644 --- a/newsroom/push/publishing.py +++ b/newsroom/push/publishing.py @@ -15,7 +15,7 @@ from newsroom import signals from newsroom.core import get_current_wsgi_app -from newsroom.types import WireItem +from newsroom.types import WireItem, AgendaItemType from newsroom.utils import parse_date_str from newsroom.wire import WireSearchServiceAsync from newsroom.agenda import AgendaItemService @@ -150,7 +150,6 @@ async def publish_event(self, event: dict[str, Any], orig: dict[str, Any]): # Retrieve all current Planning items and add them into this Event agenda.setdefault("planning_items", []) agenda.setdefault("planning_ids", []) - agenda.setdefault("event_ids", []) for plan in await (await service.search({"_id": {"$in": plan_ids}}, use_mongo=True)).to_list_raw(): planning_item = plan["planning_items"][0] agenda["planning_items"].append(planning_item) @@ -309,18 +308,18 @@ async def publish_planning_related_events(self, planning: dict[str, Any], relate event_id = related_event.get("_id") event = await service.find_by_id(event_id) - agenda = event.to_dict() + updates: dict[str, Any] = {} - if agenda.get("item_type") == "event" and planning and planning.get("guid") not in agenda["planning_ids"]: - agenda["planning_ids"].append(planning["guid"]) - - if event_id not in agenda["event_ids"]: - agenda["event_ids"].append(event_id) + if event.item_type == AgendaItemType.EVENT and planning and planning.get("guid") not in event.planning_ids: + updates["planning_ids"] = event.planning_ids + [planning["guid"]] planning_item = await service.find_by_id(planning["guid"]) - if planning_item: - agenda["coverages"] = planning_item.coverages + if planning_item and planning_item.coverages: + existing_coverages = {c.coverage_id: c for c in event.coverages} + new_coverages = {c.coverage_id: c for c in planning_item.coverages} - await service.update(agenda["_id"], agenda) - event = await service.find_by_id(event_id) - return agenda["_id"] + merged_coverages = {**existing_coverages, **new_coverages}.values() + + updates["coverages"] = list(merged_coverages) + + await service.update(event_id, updates) diff --git a/newsroom/push/views.py b/newsroom/push/views.py index 0a7d56c07..08aabfdfd 100644 --- a/newsroom/push/views.py +++ b/newsroom/push/views.py @@ -49,7 +49,7 @@ async def handle_publish_planning(item): if item.get("related_events"): for events in item["related_events"]: - event_id = await publisher.publish_planning_related_events(item, events) + await publisher.publish_planning_related_events(item, events) # Prefer parent Event when sending notificaitons _id = event_id or plan_id diff --git a/newsroom/types/agenda.py b/newsroom/types/agenda.py index d13128075..005e9f430 100644 --- a/newsroom/types/agenda.py +++ b/newsroom/types/agenda.py @@ -169,6 +169,7 @@ class AgendaPlanningItem: place: list[Place] = Field(default_factory=list) state_reason: str | None = None products: list[CVItemWithCode] = Field(default_factory=list) + event_ids: list[fields.Keyword] | None = None # Field validators _parse_time_to_be_confirmed = field_validator("time_to_be_confirmed", mode="before")(convert_value_to_bool) @@ -248,7 +249,6 @@ class AgendaItem(ResourceModel, ModelWithVersions): list[AgendaPlanningItem], fields.nested_list(include_in_parent=True), Field(default_factory=list) ] planning_ids: Annotated[list[fields.Keyword], Field(default_factory=list)] - event_ids: Annotated[list[fields.Keyword], Field(default_factory=list)] # Field/Model validators _parse_datetime_fields = field_validator("firstcreated", "versioncreated", mode="before")(convert_none_to_utcnow) diff --git a/tests/core/test_push.py b/tests/core/test_push.py index 4e832f958..0704fdd5d 100644 --- a/tests/core/test_push.py +++ b/tests/core/test_push.py @@ -1269,7 +1269,9 @@ async def test_planning_to_many_events(client, app): assert eitem1["_id"] == "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c169" assert eitem1["item_type"] == "event" assert eitem1["planning_ids"] == [] - assert eitem1["event_ids"] == [] + assert eitem1["planning_items"][0]["event_ids"] == [ + "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c168" + ] assert len(eitem1["coverages"]) == 1 # secondary link event with coverages @@ -1281,7 +1283,248 @@ async def test_planning_to_many_events(client, app): eitem2["planning_ids"][0] == "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b9ad" ) + assert eitem2["planning_items"] == [] + + +async def test_planning_to_many_events_duplicate_coverages(client, app): + event = { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "type": "event", + "occur_status": { + "qcode": "eocstat:eos5", + "name": "Planned, occurs certainly", + "label": "Planned, occurs certainly", + }, + "dates": {"start": "2024-11-14T18:30:00+0000", "end": "2024-11-15T18:29:59+0000", "tz": "Asia/Calcutta"}, + "calendars": [{"name": "Sport", "qcode": "sport"}], + "state": "scheduled", + "language": "en", + "languages": ["en"], + "place": [], + "name": "Event1", + "_updated": "2024-11-15T09:51:44+0000", + "_created": "2024-11-15T09:50:57+0000", + "guid": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "firstcreated": "2024-11-15T09:50:57+0000", + "versioncreated": "2024-11-15T09:51:44+0000", + "actioned_date": "None", + "pubstatus": "usable", + "state_reason": "None", + "version_creator": "66e13583bf2361cacc440666", + "item_id": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "plans": [], + "event_contact_info": [], + "products": [{"code": "6715cb9a62f7204f57cc0ea5", "name": "news"}], + } + + await client.post("/push", json=event) + + planning_item_1 = { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b985", + "agendas": [], + "item_class": "plinat:newscoverage", + "state": "scheduled", + "type": "planning", + "planning_date": "2024-11-14T18:30:00+0000", + "related_events": [ + { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "link_type": "secondary", + } + ], + "coverages": [ + { + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:51:44+0000", + "news_coverage_status": {"qcode": "ncostat:int", "name": "coverage intended", "label": "Planned"}, + "workflow_status": "draft", + "planning": {"language": "nl", "g2_content_type": "text", "scheduled": "2024-11-15T10:30:00+0000"}, + "coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e76", + "original_coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e76", + "scheduled_updates": [], + "assigned_user": {"first_name": "None", "last_name": "None", "display_name": "1admin"}, + "assigned_desk": {"name": "Sports Desk"}, + "coverage_provider": {"name": "Stringer_one"}, + "deliveries": [], + } + ], + "name": "Planning many One", + "guid": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b985", + "language": "en", + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:52:20+0000", + "_created": "2024-11-15T09:51:44+0000", + "_updated": "2024-11-15T09:52:20+0000", + "pubstatus": "usable", + "versionposted": "2024-11-15T09:52:20+0000", + "state_reason": "None", + "item_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b985", + "products": [{"code": "6715cb9a62f7204f57cc0ea5", "name": "news"}], + "events": [ + { + "rel": "secondary", + "uri": "urn:event:urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "literal": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "name": "Event1", + } + ], + } + + await client.post("/push", json=planning_item_1) + + planning_item_2 = { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "agendas": [], + "item_class": "plinat:newscoverage", + "state": "scheduled", + "type": "planning", + "planning_date": "2024-11-14T18:30:00+0000", + "related_events": [ + { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "link_type": "secondary", + } + ], + "coverages": [ + { + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:51:44+0000", + "news_coverage_status": {"qcode": "ncostat:int", "name": "coverage intended", "label": "Planned"}, + "workflow_status": "draft", + "planning": {"language": "nl", "g2_content_type": "text", "scheduled": "2024-11-15T10:30:00+0000"}, + "coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78", + "original_coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78", + "scheduled_updates": [], + "assigned_user": {"first_name": "None", "last_name": "None", "display_name": "1admin"}, + "assigned_desk": {"name": "Sports Desk"}, + "coverage_provider": {"name": "Stringer_Two"}, + "deliveries": [], + } + ], + "name": "Planning many One", + "guid": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "language": "en", + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:52:20+0000", + "_created": "2024-11-15T09:51:44+0000", + "_updated": "2024-11-15T09:52:20+0000", + "pubstatus": "usable", + "versionposted": "2024-11-15T09:52:20+0000", + "state_reason": "None", + "item_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "products": [{"code": "6715cb9a62f7204f57cc0ea5", "name": "news"}], + "events": [ + { + "rel": "secondary", + "uri": "urn:event:urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "literal": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "name": "Event1", + } + ], + } + + await client.post("/push", json=planning_item_2) + + events = await get_json(client, "/agenda/search") + + assert events["_items"][3]["planning_ids"] == [ + "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b985", + "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + ] + assert len(events["_items"][3]["coverages"]) == 2 + assert ( + events["_items"][3]["coverages"][0]["coverage_id"] + == "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e76" + ) + assert ( + events["_items"][3]["coverages"][1]["coverage_id"] + == "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78" + ) + + # add coverage in existing planning_item + planning_item_2 = { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "agendas": [], + "item_class": "plinat:newscoverage", + "state": "scheduled", + "type": "planning", + "planning_date": "2024-11-14T18:30:00+0000", + "related_events": [ + { + "_id": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "link_type": "secondary", + } + ], + "coverages": [ + { + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:51:44+0000", + "news_coverage_status": {"qcode": "ncostat:int", "name": "coverage intended", "label": "Planned"}, + "workflow_status": "draft", + "planning": {"language": "nl", "g2_content_type": "text", "scheduled": "2024-11-15T10:30:00+0000"}, + "coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78", + "original_coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78", + "scheduled_updates": [], + "assigned_user": {"first_name": "None", "last_name": "None", "display_name": "1admin"}, + "assigned_desk": {"name": "Sports Desk"}, + "coverage_provider": {"name": "Stringer_Two"}, + "deliveries": [], + }, + { + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:51:44+0000", + "news_coverage_status": {"qcode": "ncostat:int", "name": "coverage intended", "label": "Planned"}, + "workflow_status": "draft", + "planning": {"language": "nl", "g2_content_type": "text", "scheduled": "2024-11-15T10:30:00+0000"}, + "coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e70", + "original_coverage_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e70", + "scheduled_updates": [], + "assigned_user": {"first_name": "None", "last_name": "None", "display_name": "1admin"}, + "assigned_desk": {"name": "Sports Desk"}, + "coverage_provider": {"name": "Stringer_Two_updated"}, + "deliveries": [], + }, + ], + "name": "Planning many One", + "guid": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "language": "en", + "firstcreated": "2024-11-15T09:51:44+0000", + "versioncreated": "2024-11-15T09:52:20+0000", + "_created": "2024-11-15T09:51:44+0000", + "_updated": "2024-11-15T09:52:20+0000", + "pubstatus": "usable", + "versionposted": "2024-11-15T09:55:20+0000", + "state_reason": "None", + "item_id": "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + "products": [{"code": "6715cb9a62f7204f57cc0ea5", "name": "news"}], + "events": [ + { + "rel": "secondary", + "uri": "urn:event:urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "literal": "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c170", + "name": "Event1", + } + ], + } + + await client.post("/push", json=planning_item_2) + + events = await get_json(client, "/agenda/search") + + assert events["_items"][3]["planning_ids"] == [ + "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b985", + "urn:newsml:localhost:5000:2024-11-15T15:21:44.624942:c734e329-c43b-4acd-991b-a53e1769b986", + ] + assert len(events["_items"][3]["coverages"]) == 3 + assert ( + events["_items"][3]["coverages"][0]["coverage_id"] + == "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e76" + ) + assert ( + events["_items"][3]["coverages"][1]["coverage_id"] + == "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e78" + ) assert ( - eitem2["event_ids"][0] - == "urn:newsml:localhost:5000:2024-11-15T15:20:57.904056:bcf346bd-3f59-4b28-87c1-4a5bb324c168" + events["_items"][3]["coverages"][2]["coverage_id"] + == "urn:newsml:localhost:5000:2024-11-15T15:21:44.625299:4ebaa306-a03e-4cd7-8454-ad65cc9c6e70" )