def get(self, req, lookup): field = request.args.get('field', 'slugline') language = request.args.get('language', app.config.get('DEFAULT_LANGUAGE', 'en')) if not app.config.get(SETTING_ENABLED): raise SuperdeskApiError(_("Archive autocomplete is not enabled"), 404) if field not in self.allowed_fields: raise SuperdeskApiError( _("Field %(field)s is not allowed", field=field), 400) _filter = { 'state': 'published', 'language': language, 'versioncreated': { '$gte': utcnow() - timedelta( days=app.config[SETTING_DAYS], hours=app.config[SETTING_HOURS], ) }, } values = self.backend._backend( 'archive').driver.db['archive'].distinct(field, _filter) sorted_values = sorted(values, key=locale.strxfrm) docs = [{'value': value} for value in sorted_values] return ListCursor(docs)
def get(self, req, lookup): field = request.args.get("field", "slugline") language = request.args.get("language", app.config.get("DEFAULT_LANGUAGE", "en")) if not app.config.get(SETTING_ENABLED): raise SuperdeskApiError(_("Archive autocomplete is not enabled"), 404) if field not in self.allowed_fields: raise SuperdeskApiError( _("Field %(field)s is not allowed", field=field), 400) _filter = { "state": "published", "language": language, "versioncreated": { "$gte": utcnow() - timedelta( days=app.config[SETTING_DAYS], hours=app.config[SETTING_HOURS], ) }, } values = self.backend._backend( "archive").driver.db["archive"].distinct(field, _filter) sorted_values = sorted(values, key=locale.strxfrm) docs = [{"value": value} for value in sorted_values] return ListCursor(docs)
def validate_event(self, event): """Validate the event @:param dict event: event created or updated """ self._validate_multiday_event_duration(event) self._validate_dates(event) if len(event.get('calendars', [])) > 0: existing_calendars = get_resource_service('vocabularies').find_one( req=None, _id='event_calendars') for calendar in event['calendars']: cal = [ x for x in existing_calendars.get('items', []) if x['qcode'] == calendar.get('qcode') ] if not cal: raise SuperdeskApiError(message="Calendar does not exist.") if not cal[0].get('is_active'): raise SuperdeskApiError( message="Disabled calendar cannot be selected.") # Remove duplicated calendars uniq_qcodes = list_uniq_with_order( [o['qcode'] for o in event['calendars']]) event['calendars'] = [ cal for cal in existing_calendars.get('items', []) if cal['qcode'] in uniq_qcodes ]
def on_create(self, docs): for doc in docs: self._validate_items(doc) if doc.get("field_type") and doc["_id"] in self.system_keys: raise SuperdeskApiError(message="{} is in use".format(doc["_id"]), payload={"_id": {"conflict": 1}}) if self.find_one(req=None, **{"_id": doc["_id"], "_deleted": True}): raise SuperdeskApiError( message="{} is used by deleted vocabulary".format(doc["_id"]), payload={"_id": {"deleted": 1}} )
def on_create(self, docs): for doc in docs: self._validate_items(doc) if doc.get('field_type') and doc['_id'] in self.system_keys: raise SuperdeskApiError( message='{} is in use'.format(doc['_id']), payload={'_id': {'conflict': 1}}) if self.find_one(req=None, **{'_id': doc['_id'], '_deleted': True}): raise SuperdeskApiError( message='{} is used by deleted vocabulary'.format(doc['_id']), payload={'_id': {'deleted': 1}})
def post(self, file_storage): try: video_file = {'file': (file_storage.filename, file_storage.read(), file_storage.mimetype)} resp = self.session.post(self.get_base_url(), files=video_file) return self._get_response(resp, 201) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def _validate_poi(self, original, updates, crop_name): """ Validate the crop point of interest in the renditions dictionary for the given crop :param dict original: original item :param dict updates: updated renditions """ renditions = original.get('renditions', {}) updated_renditions = updates.get('renditions', {}) original_image = deepcopy(renditions['original']) original_image.update(updated_renditions.get('original', {})) if 'poi' in updates: if 'x' not in updates['poi'] or 'y' not in updates['poi']: del updates['poi'] return poi = updates['poi'] elif 'poi' not in original: return else: if crop_name not in updated_renditions: return poi = original['poi'] crop_data = updated_renditions[ crop_name] if crop_name in updated_renditions else renditions[ crop_name] orig_poi_x = int(original_image['width'] * poi['x']) orig_poi_y = int(original_image['height'] * poi['y']) if orig_poi_y < crop_data.get('CropTop', 0) \ or orig_poi_y > crop_data.get('CropBottom', original_image['height']) \ or orig_poi_x < crop_data.get('CropLeft', 0) \ or orig_poi_x > crop_data.get('CropRight', original_image['width']): raise SuperdeskApiError( 'Point of interest outside the crop %s limits' % crop_name)
def on_create(self, docs): for doc in docs: self._validate_items(doc) if doc.get('field_type') and doc['_id'] in self.system_keys: raise SuperdeskApiError( message='{} is in use'.format(doc['_id']), payload={'_id': {'conflict': 1}})
def _validate_poi(self, original, updates, crop_name): """Validate the crop point of interest in the renditions dictionary for the given crop :param dict original: original item :param dict updates: updated renditions """ renditions = original.get("renditions", {}) updated_renditions = updates.get("renditions", {}) original_image = deepcopy(renditions["original"]) original_image.update(updated_renditions.get("original", {})) if "poi" in updates: if "x" not in updates["poi"] or "y" not in updates["poi"]: del updates["poi"] return poi = updates["poi"] elif "poi" not in original: return else: if crop_name not in updated_renditions: return poi = original["poi"] crop_data = updated_renditions[crop_name] if crop_name in updated_renditions else renditions[crop_name] orig_poi_x = int(original_image["width"] * poi["x"]) orig_poi_y = int(original_image["height"] * poi["y"]) if ( orig_poi_y < crop_data.get("CropTop", 0) or orig_poi_y > crop_data.get("CropBottom", original_image["height"]) or orig_poi_x < crop_data.get("CropLeft", 0) or orig_poi_x > crop_data.get("CropRight", original_image["width"]) ): raise SuperdeskApiError("Point of interest outside the crop %s limits" % crop_name)
def _get_timeline_thumbnails(self, project_id, amount): try: params = {'type': 'timeline', 'amount': amount} resp = self.session.get(self._url(project_id, 'thumbnails'), params=params) return self._get_response(resp, 202) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def post_preview_thumbnail(self, project_id, file): try: payloads = { 'file': file, } resp = self.session.post(self._url(project_id, 'thumbnails'), json=payloads) return self._get_response(resp, 200) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def _delete(self, project_id): try: resp = self.session.delete(self._url(project_id)) if resp.status_code != 204: logger.exception(resp.text) resp.raise_for_status() return True except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def _post_preview_thumbnail(self, project_id, file_storage): try: video_file = { 'file': (file_storage.filename, file_storage.read(), file_storage.mimetype) } resp = self.session.post(self._url(project_id, 'thumbnails'), files=video_file) return self._get_response(resp, 200) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def on_create(self, docs): """Create corresponding item on file upload.""" for doc in docs: if 'media' not in doc or doc['media'] is None: abort(400, description="No media found") # check content type of video by python-magic content_type = magic.from_buffer(doc['media'].read(1024), mime=True) doc['media'].seek(0) file_type = content_type.split('/')[0] if file_type == 'video' and app.config.get("VIDEO_SERVER_ENABLE"): if not self.videoEditor.check_video_server(): raise SuperdeskApiError( message="Cannot connect to videoserver", status_code=500) # upload media to video server res, renditions, metadata = self.upload_file_to_video_server( doc) # get thumbnails for timeline bar self.videoEditor.get_timeline_thumbnails(doc.get('media'), 40) else: file, content_type, metadata = self.get_file_from_document(doc) inserted = [doc['media']] # if no_custom_crops is set to False the custom crops are generated automatically on media upload # see (SDESK-4742) rendition_spec = get_renditions_spec( no_custom_crops=app.config.get("NO_CUSTOM_CROPS")) with timer('archive:renditions'): renditions = generate_renditions(file, doc['media'], inserted, file_type, content_type, rendition_spec, url_for_media) try: self._set_metadata(doc) doc[ITEM_TYPE] = self.type_av.get(file_type) doc[ITEM_STATE] = CONTENT_STATE.PROGRESS doc['renditions'] = renditions doc['mimetype'] = content_type set_filemeta(doc, metadata) add_activity('upload', 'uploaded media {{ name }}', 'archive', item=doc, name=doc.get('headline', doc.get('mimetype')), renditions=doc.get('renditions')) except Exception as io: logger.exception(io) for file_id in inserted: delete_file_on_error(doc, file_id) if res: self.videoEditor.delete(res.get('_id')) abort(500)
def _validate_convert_to_recurring(self, updates, original): """Validates if the convert to recurring action is valid. :param updates: :param original: :return: """ if not original: return if original.get(LOCK_ACTION) == 'convert_recurring' and \ updates.get('dates', {}).get('recurring_rule', None) is None: raise SuperdeskApiError( message= 'Event recurring rules are mandatory for convert to recurring action.' ) if original.get(LOCK_ACTION) == 'convert_recurring' and original.get( 'recurrence_id'): raise SuperdeskApiError( message='Event is already converted to recurring event.')
def _validate_dates(self, updates, original=None): """Validate the dates @:param dict event: """ event = updates if updates.get('dates') or not original else original start_date = event.get('dates', {}).get('start') end_date = event.get('dates', {}).get('end') if not start_date or not end_date: raise SuperdeskApiError( message="Event START DATE and END DATE are mandatory.") if end_date < start_date: raise SuperdeskApiError( message="END TIME should be after START TIME") if event.get('dates', {}).get('recurring_rule') and not event['dates']['recurring_rule'].get('until') and \ not event['dates']['recurring_rule'].get('count'): raise SuperdeskApiError( message="Recurring event should have an end (until or count)")
def _validate_dates(self, event): """Validate the dates @:param dict event: """ start_date = event.get('dates', {}).get('start') end_date = event.get('dates', {}).get('end') if not start_date or not end_date: return if end_date < start_date: raise SuperdeskApiError( message="END TIME should be after START TIME")
def get_preview_thumbnail(self, project_id, position=0, crop=None, rotate=None): try: params = { "type": "preview", "position": position } if crop: params["crop"] = crop if rotate: params["rotate"] = rotate resp = self.session.get(self._url(project_id, 'thumbnails'), params=params) return self._get_response(resp, 202) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def raise_sams_error(response: Response): """If an error was raised from SAMS, attempts to re-raise it as a Superdesk error Raising Superdesk errors are to be used outside the SAMS workspace functionality i.e. with the Eve MediaStorage provider functionality """ try: response.raise_for_status() except HTTPError as http_error: error = response.json() raise SuperdeskApiError( status_code=response.status_code, message=error.get("description") or "", payload=error, exception=http_error, )
def _validate_multiday_event_duration(self, event): """Validate that the multiday event duration is not greater than PLANNING_MAX_MULTI_DAY_DURATION @:param dict event: event created or updated """ max_duration = get_event_max_multi_day_duration(app) if not max_duration > 0: return if not event.get('dates'): return event_duration = event.get('dates').get('end') - event.get( 'dates').get('start') if event_duration.days > max_duration: raise SuperdeskApiError( message="Event duration is greater than {} days.".format( max_duration))
def validate_planning(self, updates, original=None): if (not original and not updates.get('planning_date')) or \ ('planning_date' in updates and updates['planning_date'] is None): raise SuperdeskApiError(message="Planning item should have a date") # Validate if agendas being added are enabled agendas agenda_service = get_resource_service('agenda') for agenda_id in updates.get('agendas', []): agenda = agenda_service.find_one(req=None, _id=str(agenda_id)) if not agenda: raise SuperdeskApiError.forbiddenError( 'Agenda \'{}\' does not exist'.format(agenda.get('name'))) if not agenda.get('is_enabled', False): raise SuperdeskApiError.forbiddenError( 'Agenda \'{}\' is not enabled'.format(agenda.get('name'))) # Remove duplicate agendas if len(updates.get('agendas', [])) > 0: updates['agendas'] = list_uniq_with_order(updates['agendas'])
def validate_item(doc, event, new_post_status): if new_post_status == POST_STATE.USABLE and event and event.get( 'pubstatus') == POST_STATE.CANCELLED: raise SuperdeskApiError( message= "Can't post the planning item as event is already unposted/cancelled." ) errors = get_resource_service('planning_validator').post([{ 'validate_on_post': True, 'type': 'planning', 'validate': doc }])[0] if errors: # We use abort here instead of raising SuperdeskApiError.badRequestError # as eve handles error responses differently between POST and PATCH methods abort(400, description=errors)
def on_delete(self, doc): if doc.get('is_used'): raise SuperdeskApiError(status_code=202, payload={"is_used": True})
def _validate(self, doc): assignment = get_resource_service('assignments').find_one( req=None, _id=doc.get('assignment_id')) if not assignment: raise SuperdeskApiError.badRequestError('Assignment not found.') item = get_resource_service('archive').find_one(req=None, _id=doc.get('item_id')) if not item: raise SuperdeskApiError.badRequestError('Content item not found.') if not doc.get('force') and item.get('assignment_id'): raise SuperdeskApiError.badRequestError( 'Content is already linked to an assignment. Cannot link assignment and content.' ) if not is_assigned_to_a_desk(item): raise SuperdeskApiError.badRequestError( 'Content not in workflow. Cannot link assignment and content.') if not item.get('rewrite_of'): delivery = get_resource_service('delivery').find_one( req=None, assignment_id=ObjectId(doc.get('assignment_id'))) if delivery: raise SuperdeskApiError.badRequestError( 'Content already exists for the assignment. Cannot link assignment and content.' ) # scheduled update validation if assignment.get('scheduled_update_id'): raise SuperdeskApiError.badRequestError( 'Only updates can be linked to a scheduled update assignment' ) coverage = get_coverage_for_assignment(assignment) allowed_states = [ ASSIGNMENT_WORKFLOW_STATE.IN_PROGRESS, ASSIGNMENT_WORKFLOW_STATE.COMPLETED ] if (coverage and len(coverage.get('scheduled_updates')) > 0 and str(assignment['_id']) != str( (coverage.get('assigned_to') or {}).get('assignment_id'))): if (coverage.get('assigned_to') or {}).get('state') not in allowed_states: raise SuperdeskApiError( 'Previous coverage is not linked to content.') # Check all previous scheduled updated to be linked/completed for s in coverage.get('scheduled_updates'): assigned_to = (s.get('assigned_to') or {}) if str(assigned_to.get('assignment_id')) == str( doc.get('assignment_id')): break if assigned_to.get('state') not in allowed_states: raise SuperdeskApiError( 'Previous scheduled-update pending content-linking/completion' )
def is_valid(self, doc): """Check if the filter is valid""" if not doc.get('calendars') and not doc.get('agendas'): raise SuperdeskApiError(message="Either Calendar or Agenda is required.")
def on_delete(self, doc): if doc.get('is_used'): raise SuperdeskApiError(status_code=202, payload={"is_used": True}) remove_profile_from_templates(doc) remove_profile_from_desks(doc)
def check_comment_length(text): if not 1 <= len(text) <= 300: raise SuperdeskApiError( payload= 'Allowed length: between 1 and 300. You exceeded the allowed length' )
def _get_paginate(self, page): try: resp = self.session.get(self.get_base_url(), params={'page': page}) return self._get_response(resp, 200) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def _get(self, project_id): try: resp = self.session.get(self._url(project_id)) return self._get_response(resp, 200) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)
def _put(self, project_id, updates): try: resp = self.session.put(self._url(project_id), json=updates) return self._get_response(resp, 202) except ConnectionError as ex: raise SuperdeskApiError(message=ex.args[0], status_code=500)