diff --git a/server/features/events.feature b/server/features/events.feature index 2b51dc12a..8295f66df 100644 --- a/server/features/events.feature +++ b/server/features/events.feature @@ -1671,4 +1671,88 @@ Feature: Events "lock_session": "#SESSION_ID#" } """ - Then we get OK response \ No newline at end of file + Then we get OK response + + @auth + Scenario: Link new Event with many_secondary link method + Given config update + """ + {"PLANNING_EVENT_LINK_METHOD": "many_secondary"} + """ + Given "planning" + """ + [{ + "_id": "plan1", + "guid": "plan1", + "slugline": "TestEvent", + "state": "draft", + "lock_user": "#CONTEXT_USER_ID#", + "lock_session": "#SESSION_ID#", + "lock_action": "add_as_event", + "lock_time": "#DATE#", + "planning_date": "2016-01-02" + }] + """ + When we post to "events" + """ + { + "guid": "event_1", + "name": "Primary Event 1", + "slugline": "event-1", + "_planning_item": "plan1", + "dates": { + "start": "2029-11-21T12:00:00.000Z", + "end": "2029-11-21T14:00:00.000Z", + "tz": "Australia/Sydney" + } + } + """ + Then we get OK response + When we get "/planning/plan1" + Then we get existing resource + """ + {"related_events": [{"_id": "#events._id#", "link_type": "secondary"}]} + """ + + @wip + @auth + Scenario: Link new Event with one_primary link method + Given config update + """ + {"PLANNING_EVENT_LINK_METHOD": "one_primary"} + """ + Given "planning" + """ + [{ + "_id": "plan1", + "guid": "plan1", + "slugline": "TestEvent", + "state": "draft", + "lock_user": "#CONTEXT_USER_ID#", + "lock_session": "#SESSION_ID#", + "lock_action": "add_as_event", + "lock_time": "#DATE#", + "planning_date": "2016-01-02" + }] + """ + When we post to "events" + """ + { + "guid": "event_1", + "name": "Primary Event 1", + "slugline": "event-1", + "_planning_item": "plan1", + "dates": { + "start": "2029-11-21T12:00:00.000Z", + "end": "2029-11-21T14:00:00.000Z", + "tz": "Australia/Sydney" + } + } + """ + Then we get OK response + When we get "/planning/plan1" + Then we get existing resource + """ + {"related_events": [{"_id": "#events._id#", "link_type": "primary"}]} + """ + \ No newline at end of file diff --git a/server/features/events_cancel.feature b/server/features/events_cancel.feature index e15131abd..23ea482e1 100644 --- a/server/features/events_cancel.feature +++ b/server/features/events_cancel.feature @@ -906,7 +906,7 @@ Feature: Events Cancel Scenario: Cancelling an Event does not cancel Planning item with secondary link Given config update """ - {"PLANNING_EVENT_LINK_METHOD": "secondary"} + {"PLANNING_EVENT_LINK_METHOD": "many_secondary"} """ Given we have sessions "/sessions" And "events" diff --git a/server/planning/events/events.py b/server/planning/events/events.py index 6d3b4e788..aca0e9c2d 100644 --- a/server/planning/events/events.py +++ b/server/planning/events/events.py @@ -75,7 +75,11 @@ update_ingest_on_patch, TEMP_ID_PREFIX, ) -from planning.utils import get_related_planning_for_events, get_related_event_ids_for_planning +from planning.utils import ( + get_planning_event_link_method, + get_related_planning_for_events, + get_related_event_ids_for_planning, +) from .events_base_service import EventsBaseService from .events_schema import events_schema from .events_sync import sync_event_metadata_with_planning_items @@ -751,8 +755,12 @@ def _link_to_planning(event: Event): raise SuperdeskApiError.badRequestError("Planning item not found") updates = {"related_events": planning_item.get("related_events") or []} + event_link_method = get_planning_event_link_method() link_type: PLANNING_RELATED_EVENT_LINK_TYPE = ( - "primary" if not len(get_related_event_ids_for_planning(planning_item, "primary")) else "secondary" + "primary" + if not len(get_related_event_ids_for_planning(planning_item, "primary")) + and event_link_method in ("one_primary", "one_primary_many_secondary") + else "secondary" ) related_planning = PlanningRelatedEventLink(_id=event_id, link_type=link_type) updates["related_events"].append(related_planning) diff --git a/server/planning/planning/planning.py b/server/planning/planning/planning.py index a9341b625..cf7686089 100644 --- a/server/planning/planning/planning.py +++ b/server/planning/planning/planning.py @@ -11,6 +11,7 @@ """Superdesk Planning""" from typing import Dict, Any, Optional, List +from typing_extensions import assert_never from copy import deepcopy import logging from datetime import datetime @@ -74,6 +75,7 @@ from planning.signals import planning_created, planning_ingested from .planning_schema import planning_schema from planning.utils import ( + get_planning_event_link_method, get_related_planning_for_events, get_related_event_links_for_planning, get_related_event_ids_for_planning, @@ -361,9 +363,9 @@ def validate_planning(self, updates, original=None): if next_schedule and next_schedule["planning"]["scheduled"] > scheduled_update["planning"]["scheduled"]: raise SuperdeskApiError(message="Scheduled updates of a coverage must be after the previous update") - def _validate_events_links(self, updates): + def _validate_events_links(self, updates) -> None: ONLY_ONE_PRIMARY_LINKED_EVENT_ERROR = "Only 1 primary linked event is allowed" - event_link_method = app.config.get("PLANNING_EVENT_LINK_METHOD", "one_primary") + event_link_method = get_planning_event_link_method() if updates.get("related_events"): related_events_links = updates.get("related_events") @@ -383,6 +385,8 @@ def _validate_events_links(self, updates): assert len(primary_links) + len(secondary_links) == len( related_events_links ), "Missing events link type" + else: + assert_never(event_link_method) def _set_planning_event_info(self, doc: Planning, planning_type: ContentProfile) -> Optional[Event]: """Set the planning event date diff --git a/server/planning/types/__init__.py b/server/planning/types/__init__.py index a00215154..01ac7a15f 100644 --- a/server/planning/types/__init__.py +++ b/server/planning/types/__init__.py @@ -16,6 +16,7 @@ UPDATE_METHOD = Literal["single", "future", "all"] PLANNING_RELATED_EVENT_LINK_TYPE = Literal["primary", "secondary"] +PLANNING_EVENT_LINK_METHOD = Literal["one_primary", "many_secondary", "one_primary_many_secondary"] class StringFieldTranslation(TypedDict): diff --git a/server/planning/utils.py b/server/planning/utils.py index b0d3bd4c5..2ccfcc91d 100644 --- a/server/planning/utils.py +++ b/server/planning/utils.py @@ -20,6 +20,7 @@ import arrow import pytz +from planning import types from superdesk import get_resource_service from superdesk.json_utils import cast_item @@ -213,3 +214,7 @@ def get_first_event_item_for_planning_id( return None return get_resource_service("events").find_one(req=None, _id=first_event_id) + + +def get_planning_event_link_method() -> types.PLANNING_EVENT_LINK_METHOD: + return app.config.get("PLANNING_EVENT_LINK_METHOD", "one_primary") diff --git a/server/settings.py b/server/settings.py index 4b8dfdc2a..80698dc8d 100644 --- a/server/settings.py +++ b/server/settings.py @@ -12,8 +12,9 @@ import os import json +import planning.types as planning_types + from urllib.parse import urlparse -from typing import Literal def env(variable, fallback_value=None): @@ -191,4 +192,4 @@ def env(variable, fallback_value=None): PLANNING_DEFAULT_COVERAGE_STATUS_ON_INGEST = "ncostat:int" -PLANNING_EVENT_LINK_METHOD: Literal["one_primary", "many_secondary", "one_primary_many_secondary"] = "one_primary" +PLANNING_EVENT_LINK_METHOD: planning_types.PLANNING_EVENT_LINK_METHOD = "one_primary"