def _send_email(self, to, subject, text_body, html_body=None, sender=None, attachments_info=[]): if sender is None: sender = current_app.config['MAIL_DEFAULT_SENDER'] decoded_attachments = [] for a in attachments_info: try: content = base64.b64decode(a['file']) decoded_attachments.append( Attachment(a['file_name'], a['content_type'], data=content)) except Exception as e: logger.error( 'Error attaching {} file to mail. Receipient(s): {}. Error: {}' .format(a['file_desc'], to, e)) msg = SuperdeskMessage(subject=subject, sender=sender, recipients=to, attachments=decoded_attachments) msg.body = text_body msg.html = html_body app = current_app._get_current_object() with app.mail.connect() as connection: if connection: return connection.send(msg) return app.mail.send(msg)
def _change_request(self, endpoint_name, id, updates, original): backend = self._backend(endpoint_name) search_backend = self._lookup_backend(endpoint_name) try: backend.update(endpoint_name, id, updates, original) except eve.io.base.DataLayer.OriginalChangedError: if not backend.find_one(endpoint_name, req=None, _id=id) and search_backend: # item is in elastic, not in mongo - not good logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id)) self.remove_from_search(endpoint_name, id) raise SuperdeskApiError.notFoundError() else: # item is there, but no change was done - ok logger.error('Item : {} not updated in collection {}. ' 'Updates are : {}'.format(id, endpoint_name, updates)) return updates if search_backend: doc = backend.find_one(endpoint_name, req=None, _id=id) if not doc: # there is no doc in mongo, remove it from elastic logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id)) self.remove_from_search(endpoint_name, id) raise SuperdeskApiError.notFoundError() search_backend.update(endpoint_name, id, doc) return updates
def _validate_media_metadata(self, validate, associations_field, associations): if not validate: return media_metadata_schema = app.config.get('VALIDATOR_MEDIA_METADATA') if not media_metadata_schema: return for assoc_name, assoc_data in associations.items(): if assoc_data is None: continue for field, schema in media_metadata_schema.items(): if schema.get('required', False) and not assoc_data.get(field): self._error("media's " + field, REQUIRED_FIELD) try: max_length = int(schema['maxlength']) except KeyError: pass except ValueError: logger.error( 'Invalid max length value for media field {field}'. format(field=field)) else: if assoc_data.get(field) is not None and len( assoc_data[field]) > max_length: self._error("media's " + field, MAX_LENGTH.format(length=max_length))
def find_one(self, endpoint_name, req, **lookup): """Find single item. :param endpoint_name: resource name :param req: parsed request :param lookup: additional filter """ backend = self._backend(endpoint_name) item = backend.find_one(endpoint_name, req=req, **lookup) search_backend = self._lookup_backend(endpoint_name, fallback=True) if search_backend: # set the parent for the parent child in elastic search self._set_parent(endpoint_name, item, lookup) item_search = search_backend.find_one(endpoint_name, req=req, **lookup) if item is None and item_search: item = item_search logger.warn(item_msg('item is only in elastic', item)) elif item_search is None and item: logger.warn(item_msg('item is only in mongo', item)) try: logger.info(item_msg('trying to add item to elastic', item)) search_backend.insert(endpoint_name, [item]) except RequestError as e: logger.error(item_msg('failed to add item into elastic error={}'.format(str(e)), item)) return item
def find_one(self, endpoint_name, req, **lookup): """Find single item. :param endpoint_name: resource name :param req: parsed request :param lookup: additional filter """ backend = self._backend(endpoint_name) item = backend.find_one(endpoint_name, req=req, **lookup) search_backend = self._lookup_backend(endpoint_name, fallback=True) if search_backend: # set the parent for the parent child in elastic search self._set_parent(endpoint_name, item, lookup) item_search = search_backend.find_one(endpoint_name, req=req, **lookup) if item is None and item_search: item = item_search logger.warn(item_msg('item is only in elastic', item)) elif item_search is None and item: logger.warn(item_msg('item is only in mongo', item)) try: logger.info(item_msg('trying to add item to elastic', item)) search_backend.insert(endpoint_name, [item]) except RequestError as e: logger.error( item_msg( 'failed to add item into elastic error={}'.format( str(e)), item)) return item
def _change_request(self, endpoint_name, id, updates, original): backend = self._backend(endpoint_name) search_backend = self._lookup_backend(endpoint_name) try: backend.update(endpoint_name, id, updates, original) except eve.io.base.DataLayer.OriginalChangedError: if not backend.find_one(endpoint_name, req=None, _id=id) and search_backend: # item is in elastic, not in mongo - not good logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id)) item = search_backend.find_one(endpoint_name, req=None, _id=id) if item: self.remove_from_search(endpoint_name, item) raise SuperdeskApiError.notFoundError() else: # item is there, but no change was done - ok logger.error('Item : {} not updated in collection {}. ' 'Updates are : {}'.format(id, endpoint_name, updates)) return updates if search_backend: doc = backend.find_one(endpoint_name, req=None, _id=id) if not doc: # there is no doc in mongo, remove it from elastic logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id)) item = search_backend.find_one(endpoint_name, req=None, _id=id) if item: self.remove_from_search(endpoint_name, item) raise SuperdeskApiError.notFoundError() search_backend.update(endpoint_name, id, doc) return updates
def _change_request(self, endpoint_name, id, updates, original, change_request=False, push_notification=True): backend = self._backend(endpoint_name) search_backend = self._lookup_backend(endpoint_name) try: if change_request: # allows using mongo operations other than $set backend._change_request(endpoint_name, id, updates, original) else: backend.update(endpoint_name, id, updates, original) if push_notification: self._push_resource_notification("updated", endpoint_name, _id=str(id), fields=get_diff_keys( updates, original)) except eve.io.base.DataLayer.OriginalChangedError: if not backend.find_one(endpoint_name, req=None, _id=id) and search_backend: # item is in elastic, not in mongo - not good logger.warn( "Item is missing in mongo resource={} id={}".format( endpoint_name, id)) item = search_backend.find_one(endpoint_name, req=None, _id=id) if item: self.remove_from_search(endpoint_name, item) raise SuperdeskApiError.notFoundError() else: # item is there, but no change was done logger.error( "Item was not updated in mongo, it has changed from the original.", extra=dict( id=id, resource=endpoint_name, updates=updates, original=original, ), ) return updates if search_backend: doc = backend.find_one(endpoint_name, req=None, _id=id) if not doc: # there is no doc in mongo, remove it from elastic logger.warn( "Item is missing in mongo resource={} id={}".format( endpoint_name, id)) item = search_backend.find_one(endpoint_name, req=None, _id=id) if item: self.remove_from_search(endpoint_name, item) raise SuperdeskApiError.notFoundError() search_backend.update(endpoint_name, id, doc) return updates
def run(self, now=None): if now: now_utc = now if isinstance(now, datetime) else local_to_utc( app.config['DEFAULT_TIMEZONE'], datetime.strptime(now, '%Y-%m-%dT%H')) else: now_utc = utcnow() now_local = utc_to_local(app.config['DEFAULT_TIMEZONE'], now_utc) logger.info('Starting to send scheduled reports: {}'.format(now_utc)) schedules = self.get_schedules() if len(schedules) < 1: logger.info('No enabled schedules found, not continuing') return # Set now to the beginning of the hour (in local time) now_local = now_local.replace(minute=0, second=0, microsecond=0) for scheduled_report in schedules: schedule_id = str(scheduled_report.get('_id')) try: if not self.should_send_report(scheduled_report, now_local): logger.info( 'Scheduled Report {} not scheduled to be sent'.format( schedule_id)) continue logger.info('Attempting to send Scheduled Report {}'.format( schedule_id)) self._send_report(scheduled_report) # Update the _last_sent of the schedule get_resource_service('scheduled_reports').system_update( scheduled_report.get('_id'), {'_last_sent': now_utc}, scheduled_report) except Exception as e: logger.error( 'Failed to generate report for {}. Error: {}'.format( schedule_id, str(e))) logger.exception(e) logger.info('Completed sending scheduled reports: {}'.format(now_utc))
def find_one(self, endpoint_name, req, **lookup): backend = self._backend(endpoint_name) item = backend.find_one(endpoint_name, req=req, **lookup) search_backend = self._lookup_backend(endpoint_name, fallback=True) if search_backend: item_search = search_backend.find_one(endpoint_name, req=req, **lookup) if item is None and item_search: item = item_search logger.warn(item_msg('item is only in elastic', item)) elif item_search is None and item: logger.warn(item_msg('item is only in mongo', item)) try: logger.info(item_msg('trying to add item to elastic', item)) search_backend.insert(endpoint_name, [item]) except RequestError as e: logger.error(item_msg('failed to add item into elastic error={}'.format(str(e)), item)) return item
def send_email_report(self, _id, subject, sender, recipients, text_body='', html_body='', cc=None, bcc=None, attachments=None, txt_template='analytics_scheduled_report.txt', html_template='analytics_scheduled_report.html'): lock_id = 'analytics_email:{}'.format(str(_id)) if not lock(lock_id, expire=120): return try: charset = Charset('utf-8') charset.header_encoding = QP charset.body_encoding = QP msg = AnalyticsMessage(subject, sender=sender, recipients=recipients, cc=cc, bcc=bcc, body=text_body, charset=charset) reports = [] if attachments is not None: for attachment in attachments: try: uuid = str(uuid4()) if attachment.get('mimetype') == MIME_TYPES.HTML: reports.append({ 'id': uuid, 'type': 'html', 'html': attachment.get('file') }) else: msg.attach(filename=attachment.get('filename'), content_type='{}; name="{}"'.format( attachment.get('mimetype'), attachment.get('filename')), data=b64decode(attachment.get('file')), disposition='attachment', headers={ 'Content-ID': '<{}>'.format(uuid), 'X-Attachment-Id': uuid }.items()) reports.append({ 'id': uuid, 'type': 'image', 'filename': attachment.get('filename'), 'width': attachment.get('width') }) msg.body += '\n[image: {}]'.format( attachment.get('filename')) except Exception as e: logger.error('Failed to generate attachment.') logger.exception(e) msg.body = render_template(txt_template, text_body=text_body, reports=reports) msg.html = render_template(html_template, html_body=html_body.replace( '\r', '').replace('\n', '<br>'), reports=reports) return app.mail.send(msg) except Exception as e: logger.error('Failed to send report email. Error: {}'.format(str(e))) logger.exception(e) finally: unlock(lock_id, remove=True)
def _gen_attachments(report): report_service = get_report_service(report.get('type')) if report_service is None: raise SuperdeskApiError.badRequestError( 'Unknown report type "{}"'.format(report.get('type'))) if report.get('mimetype') in [ MIME_TYPES.PNG, MIME_TYPES.JPEG, MIME_TYPES.GIF, MIME_TYPES.PDF, MIME_TYPES.SVG ]: # This mimetype is handled by highcharts, so generate the highcharts config return_type = 'highcharts_config' elif report.get('mimetype') == MIME_TYPES.CSV: return_type = MIME_TYPES.CSV else: return_type = 'aggregations' generated_report = list( report_service.get(req=None, params=report.get('params') or {}, translations=report.get('translations') or {}, return_type=return_type))[0] if return_type == 'highcharts_config': options = generated_report.get('highcharts') elif return_type == MIME_TYPES.CSV: options = [generated_report.get('csv')] else: options = [] attachments = [] i = 1 for option in options: mime_type = report.get('mimetype') report_width = report.get('width') or 800 if isinstance(option, dict) and option.get('type') == 'table': mime_type = MIME_TYPES.HTML try: attachments.append({ 'file': generate_report(option, mimetype=mime_type, base64=True, width=report_width), 'mimetype': mime_type, 'filename': 'chart_{}.{}'.format(i, get_mime_type_extension(mime_type)), 'width': report_width }) i += 1 except Exception as e: logger.error('Failed to generate chart.') logger.exception(e) return attachments