Skip to content

Commit

Permalink
[SDESK-2456] Add editable and visible planning-date attribute to plan…
Browse files Browse the repository at this point in the history
…ning item

flake changes
  • Loading branch information
nrvikas committed Feb 27, 2018
1 parent 70e53c5 commit 375b4da
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 57 deletions.
1 change: 1 addition & 0 deletions client/actions/agenda.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ const _createPlanningFromEvent = (event) => (
return dispatch(planning.api.save({
event_item: event._id,
slugline: event.slugline,
planning_date: event.dates.start,
headline: event.name,
place: event.place,
subject: event.subject,
Expand Down
2 changes: 2 additions & 0 deletions client/actions/tests/agenda_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ describe('agenda', () => {
slugline: 'Slugger',
subject: '123',
anpa_category: 'abc',
dates: {start: '2016-10-15T13:01:11'}
}];

initialState = {
Expand Down Expand Up @@ -381,6 +382,7 @@ describe('agenda', () => {
{},
{
event_item: events[0]._id,
planning_date: events[0].dates.start,
slugline: events[0].slugline,
headline: events[0].name,
subject: events[0].subject,
Expand Down
3 changes: 2 additions & 1 deletion client/components/Coverages/CoverageEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ export const CoverageEditor = ({
openItemTopBar={coverageTopBar}
openItem={coverageForm}
scrollInView={true}
isOpen={openComponent || isEqual(value, COVERAGES.DEFAULT_VALUE(newsCoverageStatus, props.diff))}
isOpen={openComponent || !props.item._id ||
isEqual(value, COVERAGES.DEFAULT_VALUE(newsCoverageStatus, props.item))}
invalid={invalid}
tabEnabled
/>
Expand Down
63 changes: 44 additions & 19 deletions client/components/Planning/PlanningEditor/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import moment from 'moment';
import {get, cloneDeep, remove as _remove, some, isEqual} from 'lodash';
import * as selectors from '../../../selectors';

Expand All @@ -14,6 +15,7 @@ import {
ToggleInput,
ColouredValueInput,
Field,
DateTimeInput,
} from '../../UI/Form';
import {ToggleBox} from '../../UI';

Expand All @@ -37,32 +39,39 @@ export class PlanningEditorComponent extends React.Component {
this.onChange = this.onChange.bind(this);
this.onDuplicateCoverage = this.onDuplicateCoverage.bind(this);
this.onCancelCoverage = this.onCancelCoverage.bind(this);
this.createNewPlanningFromNewsItem = this.createNewPlanningFromNewsItem.bind(this);
this.onPlanningDateChange = this.onPlanningDateChange.bind(this);
}

componentWillMount() {
if (!this.props.addNewsItemToPlanning ||
(get(this.props, 'item._id') && !planningUtils.isLockedForAddToPlanning(this.props.item))) {
return;
}
if (!this.props.addNewsItemToPlanning) {
// Creating a new planning item from planning module
if (!get(this.props, 'item._id')) {
this.props.onChangeHandler('planning_date', moment());
}
} else {
// In add-to-planning modal
if (get(this.props, 'item._id') && !planningUtils.isLockedForAddToPlanning(this.props.item)) {
return;
}

// If we are creating a new planning item for 'add-to-planning'
if (!get(this.props, 'item._id')) {
const newPlanning = this.createNewPlanningFromNewsItem();
// If we are creating a new planning item for 'add-to-planning'
if (!get(this.props, 'item._id')) {
const newPlanning = this.createNewPlanningFromNewsItem();

this.props.onChangeHandler(null, newPlanning);
} else {
let dupItem = cloneDeep(this.props.item);
this.props.onChangeHandler(null, newPlanning);
} else {
let dupItem = cloneDeep(this.props.item);

dupItem.coverages.push(planningUtils.createCoverageFromNewsItem(
this.props.addNewsItemToPlanning,
this.props.newsCoverageStatus,
this.props.desk,
this.props.user,
this.props.contentTypes));
dupItem.coverages.push(planningUtils.createCoverageFromNewsItem(
this.props.addNewsItemToPlanning,
this.props.newsCoverageStatus,
this.props.desk,
this.props.user,
this.props.contentTypes));

// reset the object to trigger a save
this.props.onChangeHandler(null, dupItem);
// reset the object to trigger a save
this.props.onChangeHandler(null, dupItem);
}
}
}

Expand Down Expand Up @@ -224,6 +233,10 @@ export class PlanningEditorComponent extends React.Component {
}
}

onPlanningDateChange(field, value) {
this.onChange('planning_date', value);
}

render() {
const {
item,
Expand Down Expand Up @@ -306,6 +319,18 @@ export class PlanningEditorComponent extends React.Component {
{...fieldProps}
/>

<Field
component={DateTimeInput}
field="planning_date"
label={gettext('Planning Date')}
timeFormat={timeFormat}
dateFormat={dateFormat}
defaultValue={null}
row={false}
{...fieldProps}
onChange={this.onPlanningDateChange}
/>

<Field
component={TextAreaInput}
field="description_text"
Expand Down
7 changes: 6 additions & 1 deletion client/components/Planning/PlanningPreviewContent.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {gettext, getCreator, getItemInArrayById} from '../../utils';
import {gettext, getCreator, getItemInArrayById, getDateTimeString} from '../../utils';
import * as selectors from '../../selectors';
import * as actions from '../../actions';
import {get} from 'lodash';
Expand Down Expand Up @@ -70,6 +70,11 @@ export class PlanningPreviewContentComponent extends React.Component {
value={item.slugline || ''}
className="slugline"
/>
<Row
enabled={get(formProfile, 'planning.editor.planning_date.enabled')}
label={gettext('Planning Date')}
value={getDateTimeString(item.planning_date, dateFormat, timeFormat) || ''}
/>
<Row
enabled={get(formProfile, 'planning.editor.description_text.enabled')}
label={gettext('Description')}
Expand Down
2 changes: 2 additions & 0 deletions client/constants/coverages.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {get} from 'lodash';
import moment from 'moment';

export const COVERAGES = {
DEFAULT_VALUE: (newsCoverageStatus, planningItem) => ({
planning: {
slugline: get(planningItem, 'slugline'),
internal_note: get(planningItem, 'internal_note'),
ednote: get(planningItem, 'ednote'),
scheduled: get(planningItem, 'planning_date', moment())
},
news_coverage_status: newsCoverageStatus[0]
})
Expand Down
1 change: 1 addition & 0 deletions client/reducers/planning.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const modifyPlanningsBeingAdded = (state, payload) => {
plannings = cloneDeep(get(state, 'plannings'));
// add to state.plannings, use _id as key
plans.forEach((planning) => {
planning.planning_date = moment(planning.planning_date);
// Make sure that the Planning item has the coverages array
planning.coverages = get(planning, 'coverages', []);
modifyCoveragesForPlanning(planning);
Expand Down
42 changes: 22 additions & 20 deletions client/reducers/tests/planning_test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import moment from 'moment';
import planning from '../planning';
import actions from '../../actions/planning';
import {PLANNING} from '../../constants';
Expand Down Expand Up @@ -77,51 +78,52 @@ describe('planning', () => {
type: 'RECEIVE_PLANNINGS',
payload: [{
_id: 'p1',
planning_date: '2016-10-15T13:01:11',
coverages: [{_id: 'c1'}],
}],
}
);

expect(result.plannings).toEqual({
p1: {
_id: 'p1',
coverages: [{_id: 'c1'}],
},
});
expect(result.plannings.p1._id).toBe('p1');
expect(result.plannings.p1.coverages[0]._id).toBe('c1');
expect(result.plannings.p1.planning_date.toString()).toBe(
moment('2016-10-15T13:01:11').toString());
});

it('defaults coverages to empty array', () => {
const result = planning(
initialState,
{
type: 'RECEIVE_PLANNINGS',
payload: [{_id: 'p1'}],
payload: [{
_id: 'p1',
planning_date: '2016-10-15T13:01:11',
}],
}
);

expect(result.plannings).toEqual({
p1: {
_id: 'p1',
coverages: [],
},
});
expect(result.plannings.p1._id).toBe('p1');
expect(result.plannings.p1.coverages.length).toBe(0);
expect(result.plannings.p1.planning_date.toString()).toBe(
moment('2016-10-15T13:01:11').toString());
});

it('converts payload to array', () => {
const result = planning(
initialState,
{
type: 'RECEIVE_PLANNINGS',
payload: {_id: 'p1'},
payload: {
_id: 'p1',
planning_date: '2016-10-15T13:01:11',
},
}
);

expect(result.plannings).toEqual({
p1: {
_id: 'p1',
coverages: [],
},
});
expect(result.plannings.p1._id).toBe('p1');
expect(result.plannings.p1.coverages.length).toBe(0);
expect(result.plannings.p1.planning_date.toString()).toBe(
moment('2016-10-15T13:01:11').toString());
});
});

Expand Down
2 changes: 1 addition & 1 deletion client/utils/planning.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ const getPlanningByDate = (plansInList, events, startDate, endDate) => {

plan.event = get(events, get(plan, 'event_item'));
plan.coverages.forEach((coverage) => {
groupDate = moment(get(coverage, 'planning.scheduled', plan._planning_date));
groupDate = moment(get(coverage, 'planning.scheduled', plan.planning_date));

if (!isDateInRange(groupDate, startDate, endDate)) {
return;
Expand Down
2 changes: 2 additions & 0 deletions client/utils/testData.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ export const plannings = [
_id: 'p1',
_type: 'planning',
slugline: 'Planning1',
planning_date: '2016-10-15T13:01:11',
headline: 'Some Plan 1',
state: 'draft',
coverages: [
Expand Down Expand Up @@ -583,6 +584,7 @@ export const plannings = [
_id: 'p2',
_type: 'planning',
slugline: 'Planning2',
planning_date: '2016-10-15T13:01:11',
headline: 'Some Plan 2',
event_item: 'e1',
coverages: [
Expand Down
7 changes: 2 additions & 5 deletions client/validators/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,7 @@ export const validateCoverages = (dispatch, getState, field, value, profile, err
};

const validateCoverageScheduleDate = (dispatch, getState, field, value, profile, errors) => {
if (!profile.schema.scheduled) {
return;
}

if (profile.schema.scheduled.required && !value) {
if (get(profile, 'schema.scheduled.required') && !value) {
errors[field] = gettext('Required');
return;
}
Expand All @@ -111,6 +107,7 @@ export const validators = {
dates: [eventValidators.validateDates],
},
planning: {
planning_date: [formProfile],
agendas: [formProfile],
anpa_category: [formProfile],
description_text: [formProfile],
Expand Down
17 changes: 7 additions & 10 deletions server/planning/planning/planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,11 @@ def _set_planning_event_info(self, doc):
:param dict doc: planning document
"""

doc['_planning_date'] = utcnow()

event_id = doc.get('event_item')
event = {}
if event_id:
event = get_resource_service('events').find_one(req=None, _id=event_id)
if event:
doc['_planning_date'] = event.get('dates', {}).get('start')
if event.get('recurrence_id'):
doc['recurrence_id'] = event.get('recurrence_id')

Expand Down Expand Up @@ -314,8 +311,8 @@ def _set_coverage(self, updates, original=None):
def set_planning_schedule(self, updates, original=None):
"""This set the list of schedule based on the coverage and planning.
Sorting currently works on two fields "_planning_date" and "scheduled" date.
"_planning_date" is stored on the planning and is equal to event start date for planning items
Sorting currently works on two fields "planning_date" and "scheduled" date.
"planning_date" is stored on the planning and is equal to event start date for planning items
created from event or current date for adhoc planning item
"scheduled" is stored on the coverage nested document and it is optional.
Hence to sort and filter planning based on these two dates a
Expand All @@ -326,7 +323,7 @@ def set_planning_schedule(self, updates, original=None):
"""

coverages = updates.get('coverages') or (original or {}).get('coverages') or []
planning_date = updates.get('_planning_date') or (original or {}).get('_planning_date') or utcnow()
planning_date = updates.get('planning_date') or (original or {}).get('planning_date') or utcnow()

add_default_schedule = True
schedule = []
Expand Down Expand Up @@ -720,10 +717,10 @@ def remove_assignment(self, assignment_item, unlock_planning=False):
}
}
},
# date to hold the event date when planning item is created from event or _created
'_planning_date': {

'planning_date': {
'type': 'datetime',
'nullable': True
"nullable": False,
},

'flags': {
Expand Down Expand Up @@ -777,6 +774,6 @@ class PlanningResource(superdesk.Resource):
privileges = {'POST': 'planning_planning_management',
'PATCH': 'planning_planning_management',
'DELETE': 'planning'}
etag_ignore_fields = ['_planning_schedule', '_planning_date']
etag_ignore_fields = ['_planning_schedule']

mongo_indexes = {'event_item': ([('event_item', 1)], {'background': True})}
15 changes: 15 additions & 0 deletions server/planning/planning/planning_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
from superdesk.utils import ListCursor


class DateTimeField(schema.SchemaField):
"""Dict schema field."""

def __repr__(self):
return 'datetime'

def __init__(self, required=False, schema=None):
"""Initialize"""
super().__init__()
self.schema['type'] = 'datetime'
self.schema['required'] = required


class BaseSchema(schema.Schema):
slugline = schema.StringField()

Expand Down Expand Up @@ -57,6 +70,7 @@ class PlanningSchema(BaseSchema):
The planning schema used to validate the planning form
"""

planning_date = DateTimeField(required=True)
slugline = schema.StringField(required=True)
place = schema.ListField()
anpa_category = schema.ListField()
Expand Down Expand Up @@ -115,6 +129,7 @@ class CoverageSchema(BaseSchema):
{
'name': 'planning',
'editor': {
'planning_date': {'enabled': True},
'slugline': {'enabled': True},
'place': {'enabled': False},
'anpa_category': {'enabled': True},
Expand Down

0 comments on commit 375b4da

Please sign in to comment.