Example #1
0
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)
Example #2
0
    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))
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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))
Example #9
0
 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
Example #10
0
 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