def on_updated(self, updates, original): # If this Event was converted to a recurring series # Then update all associated Planning items with the recurrence_id if updates.get('recurrence_id') and not original.get('recurrence_id'): get_resource_service('planning').on_event_converted_to_recurring( updates, original) if not updates.get('duplicate_to'): posted = update_post_item(updates, original) if posted: new_event = get_resource_service('events').find_one( req=None, _id=original.get(config.ID_FIELD)) updates['_etag'] = new_event['_etag'] updates['state_reason'] = new_event.get('state_reason') if original.get( 'lock_user' ) and 'lock_user' in updates and updates.get('lock_user') is None: # when the event is unlocked by the patch. push_notification('events:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates['_etag'], recurrence_id=original.get('recurrence_id') or None) self.delete_event_files(updates, original) if 'location' not in updates and original.get('location'): updates['location'] = original['location'] self._enhance_event_item(updates)
def on_deleted(self, doc): push_notification( 'events:delete', item=str(doc.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), )
def on_create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] existing_locks = list(self.find(where={})) for existing_lock in existing_locks: if str(existing_lock.get(LOCK_USER)) != str(user_id): raise SuperdeskApiError.forbiddenError( message= "Featured stories already being managed by another user.") elif str(existing_lock.get(LOCK_SESSION)) != str(session_id): raise SuperdeskApiError.forbiddenError( message= "Featured stories already being managed by you in another session." ) # get the lock if not raise forbidden exception if not lock(LOCK_ID, expire=5): raise SuperdeskApiError.forbiddenError( message="Unable to obtain lock on Featured stories.") for doc in docs: doc['_id'] = generate_guid(type=GUID_NEWSML) lock_updates = { LOCK_USER: user_id, LOCK_SESSION: session_id, LOCK_TIME: utcnow() } doc.update(lock_updates) return docs
def validate(self, updates, original): """ Generic validation for event actions A lock must be held by the user in their current session As well as the lock must solely be for the action being processed, i.e. lock_action='update_time' """ if not original: raise SuperdeskApiError.notFoundError() if self.REQUIRE_LOCK: user_id = get_user_id() session_id = get_auth().get(config.ID_FIELD, None) lock_user = original.get(LOCK_USER, None) lock_session = original.get(LOCK_SESSION, None) lock_action = original.get(LOCK_ACTION, None) if not lock_user: raise SuperdeskApiError.forbiddenError( message='The event must be locked') elif str(lock_user) != str(user_id): raise SuperdeskApiError.forbiddenError( message='The event is locked by another user') elif str(lock_session) != str(session_id): raise SuperdeskApiError.forbiddenError( message='The event is locked by you in another session') elif str(lock_action) != self.ACTION: raise SuperdeskApiError.forbiddenError( message='The lock must be for the `{}` action'.format( self.ACTION.lower().replace('_', ' '))) get_resource_service('events').validate_event(updates, original)
def on_created(self, docs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] unlock(LOCK_ID, remove=True) push_notification('planning_featured_lock:lock', user=str(user_id), lock_session=str(session_id))
def on_updated(self, updates, original): planning_featured_service = get_resource_service('planning_featured') planning_featured_service.remove_planning_item(original) if original.get( 'lock_user' ) and 'lock_user' in updates and updates.get('lock_user') is None: push_notification('planning:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates.get('_etag'), event_item=original.get('event_item') or None, recurrence_id=original.get('recurrence_id') or None) # Delete assignments in workflow assignments_to_delete = [] coverages = original.get('coverages') or [] for coverage in coverages: if coverage.get('workflow_status') == WORKFLOW_STATE.ACTIVE: assignments_to_delete.append(coverage) notify = True if original.get('event_item'): event = get_resource_service('events').find_one( req=None, _id=original.get('event_item')) notify = event.get('state') != WORKFLOW_STATE.SPIKED get_resource_service('planning').delete_assignments_for_coverages( assignments_to_delete, notify)
def _push_notification(doc, operation): push_notification('savedreports:update', report_type=doc['report'], operation=operation, report_id=str(doc.get('_id')), user_id=str(get_user_id()), session_id=str(get_auth().get('_id')))
def on_updated(self, updates, original): user = get_user(required=True).get(config.ID_FIELD, '') session = get_auth().get(config.ID_FIELD, '') # Save history get_resource_service( 'assignments_history').on_item_revert_availability( updates, original) push_notification('assignments:reverted', item=str(original[config.ID_FIELD]), planning=original.get('planning_item'), assigned_user=(original.get('assigned_to') or {}).get('user'), assigned_desk=(original.get('assigned_to') or {}).get('desk'), assignment_state=updates.get('assigned_to', {})['state'], user=str(user), session=str(session), coverage=original.get('coverage_item')) # publish the planning item get_resource_service('assignments').publish_planning( original.get('planning_item')) # External (slack/browser pop-up) notifications assignments_service = get_resource_service('assignments') assignments_service.send_assignment_notification( updates, original, True)
def on_updated(self, updates, original): added, removed = self._get_added_removed_agendas(updates, original) session_id = get_auth().get(config.ID_FIELD) push_notification('planning:updated', item=str(original[config.ID_FIELD]), user=str(updates.get('version_creator', '')), added_agendas=added, removed_agendas=removed, session=session_id) doc = deepcopy(original) doc.update(updates) self.__generate_related_assignments([doc]) updates['coverages'] = doc.get('coverages') or [] update_post_item(updates, original) # update planning_featured record if schedule has changed if original.get('featured'): removed_schedules = [] for schdl in original.get('_planning_schedule', []): other_schedules_on_day = [ s for s in updates.get('_planning_schedule', []) if schdl.get('scheduled').date() == s.get('scheduled').date() ] if len(other_schedules_on_day) == 0 and schdl.get( 'scheduled') not in removed_schedules: removed_schedules.append(schdl.get('scheduled')) planning_featured_service = get_resource_service( 'planning_featured') for removed_date in removed_schedules: # get the planning_featured record for that day planning_featured_service.remove_planning_item_for_date( removed_date, original)
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] lock_service = LockService() # If the event is a recurrent event, unlock all other events in this series item_id = request.view_args['item_id'] resource_service = get_resource_service('events') item = resource_service.find_one(req=None, _id=item_id) if item.get('recurrence_id') and not item.get(LOCK_USER): # Find the actual event that is locked historic, past, future = resource_service.get_recurring_timeline( item) series = historic + past + future for event in series: if event.get(LOCK_USER): updated_item = lock_service.unlock(event, user_id, session_id, 'events') break else: updated_item = lock_service.unlock(item, user_id, session_id, 'events') if updated_item is None: # version 1 item must have been deleted by now return [0] return _update_returned_document(docs[0], updated_item)
def on_updated(self, updates, original): # Spike associated planning planning_spike_service = get_resource_service('planning_spike') query = { 'query': { 'bool': { 'must': { 'term': { 'event_item': str(original[config.ID_FIELD]) } } } } } results = get_resource_service('planning').search(query) spiked_items = [] if len(results.docs) > 0: for planning in results.docs: if planning['state'] == WORKFLOW_STATE.DRAFT: planning_spike_service.patch(planning[config.ID_FIELD], {'state': 'spiked'}) spiked_items.append(str(planning[config.ID_FIELD])) # When a planning item associated with this event is spiked # If there were any failures in removing assignments # Send those notifications here if len(spiked_items) > 0: query = { 'query': { 'filtered': { 'filter': { 'bool': { 'must': { 'terms': { 'planning_item': spiked_items } } } } } } } req = ParsedRequest() req.args = {'source': json.dumps(query)} assignments = get_resource_service('assignments').get( req=req, lookup=None) if assignments.count() > 0: session_id = get_auth().get('_id') user_id = get_user().get(config.ID_FIELD) push_notification( 'assignments:delete:fail', items=[{ 'slugline': a.get('planning').get('slugline'), 'type': a.get('planning').get('g2_content_type') } for a in assignments], session=session_id, user=user_id)
def on_updated(self, updates, original): user = get_user(required=True).get(config.ID_FIELD, '') session = get_auth().get(config.ID_FIELD, '') push_notification('planning:rescheduled', item=str(original[config.ID_FIELD]), user=str(user), session=str(session))
def create(self, docs, **kwargs): user = get_user(required=True) auth = get_auth() item_id = request.view_args['item_id'] lock_service = LockService() item = lock_service.unlock(item_id, user['_id'], auth['_id'], 'planning') return _update_returned_document(docs[0], item)
def update(self, id, updates, original): user = get_user(required=True).get(config.ID_FIELD, '') session = get_auth().get(config.ID_FIELD, '') coverage_states = get_resource_service('vocabularies').find_one( req=None, _id='newscoveragestatus') event_cancellation = updates.pop('event_cancellation', False) cancel_all_coverage = updates.pop('cancel_all_coverage', False) coverage_cancel_state = None if coverage_states: coverage_cancel_state = next( (x for x in coverage_states.get('items', []) if x['qcode'] == 'ncostat:notint'), None) coverage_cancel_state.pop('is_active', None) ids = [] updates['coverages'] = deepcopy(original.get('coverages')) coverages = updates.get('coverages') or [] reason = updates.pop('reason', None) planning_service = get_resource_service('planning') for coverage in coverages: if coverage['workflow_status'] != WORKFLOW_STATE.CANCELLED: ids.append(coverage.get('coverage_id')) planning_service.cancel_coverage( coverage, coverage_cancel_state, coverage.get('workflow_status'), None, reason, event_cancellation) if cancel_all_coverage: item = None if len(ids) > 0: item = self.backend.update(self.datasource, id, updates, original) push_notification('coverage:cancelled', planning_item=str(original[config.ID_FIELD]), user=str(user), session=str(session), reason=reason, coverage_state=coverage_cancel_state, etag=item.get('_etag'), ids=ids) return item if item else self.find_one(req=None, _id=id) self._cancel_plan(updates, reason) item = self.backend.update(self.datasource, id, updates, original) push_notification('planning:cancelled', item=str(original[config.ID_FIELD]), user=str(user), session=str(session), reason=reason, coverage_state=coverage_cancel_state, event_cancellation=event_cancellation) return item
def update(self, id, updates, original): user = get_user(required=True).get(config.ID_FIELD, '') session = get_auth().get(config.ID_FIELD, '') updates['assigned_to'] = deepcopy(original).get('assigned_to') # If we are confirming availability, save the revert state for revert action coverage_type = original.get('planning', {}).get('g2_content_type') if coverage_type != 'text': updates['assigned_to']['revert_state'] = updates['assigned_to'][ 'state'] updates['assigned_to']['state'] = ASSIGNMENT_WORKFLOW_STATE.COMPLETED remove_lock_information(updates) item = self.backend.update(self.datasource, id, updates, original) # Save history if user initiates complete if coverage_type == 'text': get_resource_service('assignments_history').on_item_complete( updates, original) else: get_resource_service( 'assignments_history').on_item_confirm_availability( updates, original) push_notification('assignments:completed', item=str(original[config.ID_FIELD]), planning=original.get('planning_item'), assigned_user=(original.get('assigned_to') or {}).get('user'), assigned_desk=(original.get('assigned_to') or {}).get('desk'), assignment_state=ASSIGNMENT_WORKFLOW_STATE.COMPLETED, user=str(user), session=str(session), coverage=original.get('coverage_item')) # Send notification that the work has been completed # Determine the display name of the assignee assigned_to_user = get_resource_service('users').find_one(req=None, _id=user) assignee = assigned_to_user.get( 'display_name') if assigned_to_user else 'Unknown' PlanningNotifications().notify_assignment( target_user=str( original.get('assigned_to', {}).get('assignor_user')), message='{{coverage_type}} coverage \"{{slugline}}\" has been ' 'completed by {{assignee}}', assignee=assignee, coverage_type=get_coverage_type_name( original.get('planning', {}).get('g2_content_type', '')), slugline=original.get('planning', {}).get('slugline'), omit_user=True) return item
def unlock_item(self, item_id, doc): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] lock_service = get_component(LockService) resource_service = get_resource_service('events') item = resource_service.find_one(req=None, _id=item_id) updated_item = lock_service.unlock(item, user_id, session_id, 'events') return update_returned_document(doc, updated_item, CUSTOM_HATEOAS_EVENTS)
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] item_id = request.view_args['item_id'] lock_service = get_component(LockService) resource_service = get_resource_service('planning') item = resource_service.find_one(req=None, _id=item_id) updated_item = lock_service.unlock(item, user_id, session_id, 'planning') return update_returned_document(docs[0], updated_item, CUSTOM_HATEOAS_PLANNING)
def is_locked_in_this_session(item, user_id=None, session_id=None): if user_id is None: user = get_user(required=True) user_id = user.get(config.ID_FIELD) if session_id is None: session = get_auth() session_id = session.get(config.ID_FIELD) return item.get(LOCK_USER) == user_id and item.get(LOCK_SESSION) == session_id
def on_duplicated(self, doc, parent_id): self._update_event_history(doc) session_id = get_auth().get('_id') push_notification('planning:duplicated', item=str(doc.get(config.ID_FIELD)), original=str(parent_id), user=str(doc.get('original_creator', '')), added_agendas=doc.get('agendas') or [], removed_agendas=[], session=session_id)
def on_updated(self, updates, original): added, removed = self._get_added_removed_agendas(updates, original) session_id = get_auth().get('_id') push_notification( 'planning:updated', item=str(original[config.ID_FIELD]), user=str(updates.get('version_creator', '')), added_agendas=added, removed_agendas=removed, session=session_id )
def sync_assignment_unlock(self, item, user_id): if item.get('assignment_id'): assignment_update_data = self._get_assignment_data_on_archive_update( {}, item) if assignment_update_data.get('assignment'): assignment = assignment_update_data.get('assignment') if assignment.get(LOCK_USER): lock_service = get_component(LockService) lock_service.unlock(assignment, user_id, get_auth()['_id'], 'assignments')
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] item_id = request.view_args['item_id'] lock_action = docs[0].get('lock_action', 'edit') lock_service = LockService() resource_service = get_resource_service('planning') item = resource_service.find_one(req=None, _id=item_id) updated_item = lock_service.lock(item, user_id, session_id, lock_action, 'planning') return _update_returned_document(docs[0], updated_item)
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] lock_service = get_component(LockService) # If the event is a recurrent event, unlock all other events in this series item_id = request.view_args['item_id'] resource_service = get_resource_service('events') item = resource_service.find_one(req=None, _id=item_id) updated_item = lock_service.unlock(item, user_id, session_id, 'events') return update_returned_document(docs[0], updated_item, CUSTOM_HATEOAS_EVENTS)
def on_created(self, docs): session_id = get_auth().get('_id') for doc in docs: push_notification('planning:created', item=str(doc.get(config.ID_FIELD)), user=str(doc.get('original_creator', '')), added_agendas=doc.get('agendas') or [], removed_agendas=[], session=session_id, event_item=doc.get('event_item', None)) self._update_event_history(doc) self.__generate_related_assignments(docs)
def validate_assignment_lock(self, item, user_id): if item.get('assignment_id'): assignment_update_data = self._get_assignment_data_on_archive_update( {}, item) if assignment_update_data.get('assignment'): assignment = assignment_update_data.get('assignment') if assignment and assignment.get('lock_user'): if assignment['lock_session'] != get_auth( )['_id'] or assignment['lock_user'] != user_id: raise SuperdeskApiError.badRequestError( message="Lock Failed: Related assignment is locked." )
def lock_item(self, item_id, action, doc): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] lock_action = action lock_service = get_component(LockService) item = get_resource_service('events').find_one(req=None, _id=item_id) lock_service.validate_relationship_locks(item, 'events') updated_item = lock_service.lock(item, user_id, session_id, lock_action, 'events') return update_returned_document(doc, updated_item, CUSTOM_HATEOAS_EVENTS)
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] item_id = request.view_args['item_id'] lock_action = docs[0].get('lock_action', 'edit') lock_service = get_component(LockService) item = get_resource_service('planning').find_one(req=None, _id=item_id) if item and item.get('event_item'): lock_service.validate_relationship_locks(item, 'planning') updated_item = lock_service.lock(item, user_id, session_id, lock_action, 'planning') return update_returned_document(docs[0], updated_item, CUSTOM_HATEOAS_PLANNING)
def on_deleted(self, doc): """Validate we can safely delete the Assignment item Make sure to clean up the Archive, Delivery and Planning items by: * Remove 'assignment_id' from Archive item (if linked) * Delete the Delivery record associated with the Assignment & Archive items (if linked) * Removing 'assigned_to' dictionary from the associated Coverage """ archive_service = get_resource_service('archive') delivery_service = get_resource_service('delivery') planning_service = get_resource_service('planning') assignment_id = doc.get(config.ID_FIELD) # If we have a Content Item linked, then we need to remove the # assignment_id from it and remove the delivery record # Then send a notification that the content has been updated archive_item = archive_service.find_one(req=None, assignment_id=assignment_id) if archive_item: archive_service.system_update(archive_item[config.ID_FIELD], {'assignment_id': None}, archive_item) delivery_service.delete_action( lookup={ 'assignment_id': assignment_id, 'item_id': archive_item[config.ID_FIELD] }) # Push content nofitication so connected clients can update the # content views (i.e. removes the Calendar icon from Monitoring) push_content_notification([archive_item]) # Remove assignment information from coverage updated_planning = planning_service.remove_assignment( doc, unlock_planning=True) # Finally send a notification to connected clients that the Assignment # has been removed push_notification( 'assignments:removed', item=archive_item[config.ID_FIELD] if archive_item else None, assignment=assignment_id, planning=doc.get('planning_item'), coverage=doc.get('coverage_item'), planning_etag=updated_planning.get(config.ETAG), session=get_auth()['_id']) # publish planning self.publish_planning(doc.get('planning_item'))
def on_updated(self, updates, original): planning_featured_service = get_resource_service('planning_featured') planning_featured_service.remove_planning_item(original) if original.get('lock_user') and 'lock_user' in updates and updates.get('lock_user') is None: push_notification( 'planning:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates.get('_etag'), event_item=original.get('event_item') or None, recurrence_id=original.get('recurrence_id') or None )
def create(self, docs, **kwargs): user_id = get_user(required=True)['_id'] session_id = get_auth()['_id'] lock_service = get_component(LockService) # If the event is a recurrent event, unlock all other events in this series item_id = request.view_args['item_id'] resource_service = get_resource_service('assignments') item = resource_service.find_one(req=None, _id=item_id) if not self.is_assignment_locked_by_user(item, user_id): updated_item = lock_service.unlock(item, user_id, session_id, 'assignments') return _update_returned_document(docs[0], updated_item) return _update_returned_document(docs[0], item)