Skip to content

Commit

Permalink
[SDBELGA-773] Support adding Planning series to existing Event series (
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkLark86 authored Mar 5, 2024
1 parent 7ac9140 commit f5a6086
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 27 deletions.
12 changes: 8 additions & 4 deletions client/api/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ISearchParams,
ISearchSpikeState,
IPlanningConfig,
IEventUpdateMethod,
} from '../interfaces';
import {appConfig as config} from 'appConfig';
import {IRestApiResponse} from 'superdesk-api';
Expand Down Expand Up @@ -122,14 +123,16 @@ function getEventSearchProfile() {
}

function create(updates: Partial<IEventItem>): Promise<Array<IEventItem>> {
const url = appConfig.planning.default_create_planning_series_with_event_series === true ?
'events?add_to_series=true' :
'events';
const {default_create_planning_series_with_event_series} = appConfig.planning;
const planningDefaultCreateMethod: IEventUpdateMethod = default_create_planning_series_with_event_series === true ?
'all' :
'single';

return superdeskApi.dataApi.create<IEventItem | IRestApiResponse<IEventItem>>(url, {
return superdeskApi.dataApi.create<IEventItem | IRestApiResponse<IEventItem>>('events', {
...updates,
associated_plannings: undefined,
embedded_planning: updates.associated_plannings.map((planning) => ({
update_method: planning.update_method ?? planningDefaultCreateMethod,
coverages: planning.coverages.map((coverage) => ({
coverage_id: coverage.coverage_id,
g2_content_type: coverage.planning.g2_content_type,
Expand All @@ -142,6 +145,7 @@ function create(updates: Partial<IEventItem>): Promise<Array<IEventItem>> {
slugline: coverage.planning.slugline,
ednote: coverage.planning.ednote,
internal_note: coverage.planning.internal_note,
headline: coverage.planning.headline,
})),
})),
update_method: updates.update_method?.value ?? updates.update_method
Expand Down
12 changes: 6 additions & 6 deletions client/api/planning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,8 @@ function getPlanningSearchProfile() {
return planningSearchProfile(planningApi.redux.store.getState());
}

function create(updates: Partial<IPlanningItem>, addToSeries?: boolean): Promise<IPlanningItem> {
return superdeskApi.dataApi.create<IPlanningItem>(
addToSeries === true ? 'planning?add_to_series=true' : 'planning',
updates
);
function create(updates: Partial<IPlanningItem>): Promise<IPlanningItem> {
return superdeskApi.dataApi.create<IPlanningItem>('planning', updates);
}

function update(original: IPlanningItem, updates: Partial<IPlanningItem>): Promise<IPlanningItem> {
Expand All @@ -161,6 +158,10 @@ function update(original: IPlanningItem, updates: Partial<IPlanningItem>): Promi
}

function createFromEvent(event: IEventItem, updates: Partial<IPlanningItem>): Promise<IPlanningItem> {
if (updates.update_method == null && appConfig.planning.default_create_planning_series_with_event_series === true) {
updates.update_method = 'all';
}

return create(
planningUtils.modifyForServer({
slugline: event.slugline,
Expand All @@ -176,7 +177,6 @@ function createFromEvent(event: IEventItem, updates: Partial<IPlanningItem>): Pr
...updates,
event_item: event._id,
}),
appConfig.planning.default_create_planning_series_with_event_series === true,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface IState {
diff: Partial<IEventItem>;
eventModified: boolean;
recurringPlanningItemsToUpdate: Array<IPlanningItem['_id']>;
recurringPlanningItemsToCreate: Array<IPlanningItem['_id']>;
planningUpdateMethods: {[planningId: string]: IEventUpdateMethod};
}

Expand Down Expand Up @@ -109,6 +110,12 @@ function getRecurringPlanningToUpdate(
.map((planningItem) => planningItem._id);
}

function getRecurringPlanningToCreate(updates: Partial<IEventItem>): Array<IPlanningItem['_id']> {
return (updates.associated_plannings ?? [])
.filter((planningItem) => (planningItem._id.startsWith(TEMP_ID_PREFIX)))
.map((planningItem) => planningItem._id);
}

export class UpdateRecurringEventsComponent extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
Expand Down Expand Up @@ -143,6 +150,7 @@ export class UpdateRecurringEventsComponent extends React.Component<IProps, ISta
this.props.updates,
this.props.originalPlanningItems
),
recurringPlanningItemsToCreate: getRecurringPlanningToCreate(this.props.updates),
planningUpdateMethods: {},
};

Expand Down Expand Up @@ -183,16 +191,49 @@ export class UpdateRecurringEventsComponent extends React.Component<IProps, ISta
return this.props.onSubmit(this.props.original, updates);
}

renderPlanningCreateForm() {
if (this.state.recurringPlanningItemsToCreate.length > 0) {
return this.props.updates.associated_plannings
.filter((planningItem) => (
this.state.recurringPlanningItemsToCreate.includes(planningItem._id)
))
.map((planningItem) => (
<div key={planningItem._id}>
<Select
label={gettext('Create planning for all events or just this one?')}
value={this.state.planningUpdateMethods[planningItem._id] ?? EVENTS.UPDATE_METHODS[0].value}
onChange={(updateMethod) => {
this.onPlanningUpdateMethodChange(planningItem._id, updateMethod as IEventUpdateMethod);
}}
>
<Option value={EVENTS.UPDATE_METHODS[0].value}>
{gettext('This event only')}
</Option>
<Option value={EVENTS.UPDATE_METHODS[1].value}>
{gettext('This and all future events')}
</Option>
<Option value={EVENTS.UPDATE_METHODS[2].value}>
{gettext('All Events')}
</Option>
</Select>
<PlanningMetaData plan={planningItem} />
</div>
));
}

return null;
}

renderPlanningUpdateForm() {
if (Object.keys(this.state.recurringPlanningItemsToUpdate).length > 0) {
if (this.state.recurringPlanningItemsToUpdate.length > 0) {
return this.props.updates.associated_plannings
.filter((planningItem) => (
this.state.recurringPlanningItemsToUpdate.includes(planningItem._id)
))
.map((planningItem) => (
<div key={planningItem._id}>
<Select
label="Update all recurring planning or just this one?"
label={gettext('Update all recurring planning or just this one?')}
value={this.state.planningUpdateMethods[planningItem._id] ?? EVENTS.UPDATE_METHODS[0].value}
onChange={(updateMethod) => {
this.onPlanningUpdateMethodChange(planningItem._id, updateMethod as IEventUpdateMethod);
Expand Down Expand Up @@ -277,6 +318,7 @@ export class UpdateRecurringEventsComponent extends React.Component<IProps, ISta
/>
</div>
)}
{this.renderPlanningCreateForm()}
{this.renderPlanningUpdateForm()}
</React.Fragment>
);
Expand Down
3 changes: 2 additions & 1 deletion server/features/planning_recurring.feature
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ Feature: Recurring Events & Planning
"dates": {"start": "2024-11-23T12:00:00+0000", "end": "2024-11-23T14:00:00+0000"}
}]}
"""
When we post to "/planning?add_to_series=true"
When we post to "/planning"
"""
[{
"event_item": "#EVENT1._id#",
"planning_date": "2024-11-21T12:00:00.000Z",
"update_method": "all",
"coverages": [{
"workflow_status": "draft",
"news_coverage_status": {"qcode": "ncostat:int"},
Expand Down
172 changes: 171 additions & 1 deletion server/features/recurring_event_and_planning.feature
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,74 @@ Feature: Recurring Events & Planning
}]}
"""

@auth
Scenario: Create planning for future events
When we post to "/planning"
"""
[{
"headline": "test headline",
"event_item": "#EVENT2._id#",
"planning_date": "2024-11-22T12:00:00.000Z",
"update_method": "future",
"coverages": [{
"workflow_status": "draft",
"news_coverage_status": {"qcode": "ncostat:int"},
"planning": {
"headline": "test headline",
"slugline": "test slugline",
"g2_content_type": "text",
"scheduled": "2024-11-22T15:00:00.000Z"
}
}, {
"workflow_status": "draft",
"news_coverage_status": {"qcode": "ncostat:int"},
"planning": {
"headline": "test headline",
"slugline": "test slugline",
"g2_content_type": "picture",
"scheduled": "2024-11-22T16:00:00.000Z"
}
}]
}]
"""
Then we get OK response
When we get "/planning"
Then we get list with 2 items
"""
{"_items": [{
"guid": "__any_value__",
"type": "planning",
"headline": "test headline",
"planning_date": "2024-11-22T12:00:00+0000",
"event_item": "#EVENT2._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-22T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-22T16:00:00+0000"}}
]
}, {
"guid": "__any_value__",
"type": "planning",
"headline": "test headline",
"planning_date": "2024-11-23T12:00:00+0000",
"event_item": "#EVENT3._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-23T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-23T16:00:00+0000"}}
]
}]}
"""

@auth
Scenario: Create planning for each event in the series
When we post to "/planning?add_to_series=true"
When we post to "/planning"
"""
[{
"headline": "test headline",
"event_item": "#EVENT1._id#",
"planning_date": "2024-11-21T12:00:00.000Z",
"update_method": "all",
"coverages": [{
"workflow_status": "draft",
"news_coverage_status": {"qcode": "ncostat:int"},
Expand Down Expand Up @@ -157,3 +217,113 @@ Feature: Recurring Events & Planning
]
}]}
"""

@auth
Scenario: Create planning for future events through events endpoint
When we patch "/events/#EVENT2._id#"
"""
{
"embedded_planning": [{
"update_method": "future",
"coverages": [{
"g2_content_type": "text",
"news_coverage_status": "ncostat:int",
"scheduled": "2024-11-22T15:00:00.000Z",
"slugline": "test slugline",
"headline": "test headline"
}, {
"g2_content_type": "picture",
"news_coverage_status": "ncostat:int",
"scheduled": "2024-11-22T16:00:00.000Z",
"slugline": "test slugline",
"headline": "test headline"
}]
}]
}
"""
Then we get OK response
When we get "/planning"
Then we get list with 2 items
"""
{"_items": [{
"guid": "__any_value__",
"type": "planning",
"planning_date": "2024-11-22T12:00:00+0000",
"event_item": "#EVENT2._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-22T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-22T16:00:00+0000"}}
]
}, {
"guid": "__any_value__",
"type": "planning",
"planning_date": "2024-11-23T12:00:00+0000",
"event_item": "#EVENT3._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-23T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-23T16:00:00+0000"}}
]
}]}
"""

@auth
Scenario: Create planning for each event in the series through events endpoint
When we patch "/events/#EVENT2._id#"
"""
{
"embedded_planning": [{
"update_method": "all",
"coverages": [{
"g2_content_type": "text",
"news_coverage_status": "ncostat:int",
"scheduled": "2024-11-22T15:00:00.000Z",
"slugline": "test slugline",
"headline": "test headline"
}, {
"g2_content_type": "picture",
"news_coverage_status": "ncostat:int",
"scheduled": "2024-11-22T16:00:00.000Z",
"slugline": "test slugline",
"headline": "test headline"
}]
}]
}
"""
Then we get OK response
When we get "/planning"
Then we get list with 3 items
"""
{"_items": [{
"guid": "__any_value__",
"type": "planning",
"planning_date": "2024-11-21T12:00:00+0000",
"event_item": "#EVENT1._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-21T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-21T16:00:00+0000"}}
]
}, {
"guid": "__any_value__",
"type": "planning",
"planning_date": "2024-11-22T12:00:00+0000",
"event_item": "#EVENT2._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-22T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-22T16:00:00+0000"}}
]
}, {
"guid": "__any_value__",
"type": "planning",
"planning_date": "2024-11-23T12:00:00+0000",
"event_item": "#EVENT3._id#",
"recurrence_id": "#EVENT1.recurrence_id#",
"coverages": [
{"planning": {"g2_content_type": "text", "scheduled": "2024-11-23T15:00:00+0000"}},
{"planning": {"g2_content_type": "picture", "scheduled": "2024-11-23T16:00:00+0000"}}
]
}]}
"""
5 changes: 5 additions & 0 deletions server/planning/events/events_sync/embedded_planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ def map_event_to_planning_translation(translation: StringFieldTranslation):
"coverages": [],
}

try:
new_planning["update_method"] = plan["update_method"]
except KeyError:
pass

if event.get("recurrence_id"):
new_planning["recurrence_id"] = event["recurrence_id"]

Expand Down
Loading

0 comments on commit f5a6086

Please sign in to comment.