Пример #1
0
    def get(self, request, subscription_id, content_format, **_kwargs):
        send_email = as_bool(request.GET, self.PARAM_SEND, False)

        subscription = DocumentNotificationSubscription.objects.get(pk=subscription_id)

        document_type = subscription.document_type

        document_id = as_int(request.GET, self.PARAM_DOCUMENT, None)
        if document_id:
            document = Document.objects.filter(document_type=document_type, pk=document_id).first()
            if not document:
                return HttpResponseBadRequest('Document with id = {0} not found or has wrong type.'.format(document_id))
        else:
            document = Document.objects.filter(document_type=document_type).first()
            if not document:
                return HttpResponseBadRequest('Document id not provided and '
                                              'there are no example documents of type {0}.'.format(document_type.code))

        document_id = document.pk
        field_handlers = build_field_handlers(document_type, include_suggested_fields=False)
        field_values = get_document_field_values(document_type, document_id, handlers=field_handlers)

        example_changes = dict()
        if subscription.event in {DocumentAssignedEvent.code, DocumentChangedEvent.code} and field_values:
            for h in field_handlers:
                if random.random() > 0.3:
                    continue
                field_type = FIELD_TYPES_REGISTRY.get(h.field_type)  # type: FieldType
                field = DocumentField.objects.filter(code=h.field_code).first()
                if not field:
                    continue
                example_value = field_type.example_python_value(field=field)
                example_changes[h.field_code] = (example_value, field_values.get(h.field_code))

        notification = render_notification(already_sent_user_ids=set(),
                                           subscription=subscription,
                                           document=document,
                                           field_handlers=field_handlers,
                                           field_values=field_values,
                                           changes=example_changes,
                                           changed_by_user=request.user
                                           )
        if not notification:
            return HttpResponse('Notification contains no data.', status=200)

        if content_format == self.FORMAT_HTML:
            content = notification.html
            content_type = 'text/html'
        else:
            content = notification.txt
            content_type = 'text/plain'

        if send_email:
            log = ErrorCollectingLogger()
            notification.send(log=log)
            error = log.get_error()
            if error:
                return HttpResponseServerError(content=error, content_type='application/json')

        return HttpResponse(content=content, content_type=content_type, status=200)
def process_notifications_on_document_change(task: ExtendedTask,
                                             document_event: str,
                                             document_id: int,
                                             fields_before: Optional[Dict],
                                             fields_after: Optional[Dict],
                                             changed_by_user_id: int):
    document_type_id = Document.all_objects.filter(pk=document_id).values_list(
        'document_type', flat=True).first()  # type: str
    document_type = DocumentType.objects.get(pk=document_type_id)  # type: DocumentType
    changed_by_user = User.objects.get(pk=changed_by_user_id)  # type: User
    field_handlers = build_field_handlers(document_type,
                                          include_annotation_fields=False)  # List[RawdbFieldHandler]
    field_handlers_by_field_code = {h.field_code: h for h in field_handlers}  # Dict[str, RawdbFieldHandler]

    log_msgs = []
    package_id = uuid.uuid4().hex

    if document_event == DocumentEvent.CREATED.value or fields_before is None:
        if fields_after.get(FIELD_CODE_ASSIGNEE_ID) is not None:
            send_notification(package_id=package_id,
                              event=DocumentAssignedEvent.code,
                              document_id=document_id,
                              field_values=fields_after,
                              changed_by_user=changed_by_user)

        send_notification(package_id=package_id,
                          event=DocumentLoadedEvent.code,
                          document_id=document_id,
                          field_values=fields_after,
                          changed_by_user=changed_by_user)
    elif document_event == DocumentEvent.DELETED.value:
        send_notification(package_id=package_id,
                          event=DocumentDeletedEvent.code,
                          document_id=document_id,
                          field_values=fields_before,
                          changed_by_user=changed_by_user)
    else:
        changes = dict()
        for field_code, old_value in fields_before.items():
            if field_code not in field_handlers_by_field_code:
                continue
            new_value = fields_after.get(field_code)
            if not values_look_equal(old_value, new_value):
                changes[field_code] = (old_value, new_value)
                log_msgs.append(format_values_difference(field_code, old_value, new_value))

        if not changes:
            return

        if len(log_msgs) > 0:
            msgs_str = 'Following fields are different:\n    ' + '\n    '.join(log_msgs)
            log = CeleryTaskLogger(task)
            log.info(msgs_str)

        if FIELD_CODE_ASSIGNEE_ID in changes:
            send_notification(package_id=package_id,
                              event=DocumentAssignedEvent.code,
                              document_id=document_id,
                              field_values=fields_after,
                              changes=changes,
                              changed_by_user=changed_by_user)

        send_notification(package_id=package_id,
                          event=DocumentChangedEvent.code,
                          document_id=document_id,
                          field_values=fields_after,
                          changes=changes,
                          changed_by_user=changed_by_user)
    def send_notifications_packet(ntfs: List[DocumentNotification],
                                  event: str,
                                  task: BaseTask):
        documents_data = list(Document.all_objects.filter(
            pk__in={d.document_id for d in ntfs}))  # type: List[Document]
        doc_type_by_id = {dt.document_type.pk:dt.document_type for dt in documents_data}
        doc_types = [doc_type_by_id[pk] for pk in doc_type_by_id]

        doc_by_id = {}  # type: Dict[int, Document]
        for doc in documents_data:
            doc_by_id[doc.pk] = doc

        users = User.objects.filter(pk__in={d.changed_by_user_id for d in ntfs})
        user_by_id = {u.pk: u for u in users}

        handlers_by_doctype = {d: build_field_handlers(d, include_annotation_fields=False)
                               for d in doc_types}  # type:Dict[str, RawdbFieldHandler]

        log = CeleryTaskLogger(task)

        # { (doc_type, event,) : [notification0, notification1, ...], ... }
        messages_by_subscr_key = {}  # type: Dict[Tuple[str, str], List[DocumentNotification]]
        # { (doc_type, event,) : [DocumentNotificationSubscription0, ... ], ... }
        subscr_by_key = {}  # type: Dict[Tuple[str, str], List[DocumentNotificationSubscription]]

        for ntf in ntfs:
            if ntf.document_id not in doc_by_id:
                continue
            document = doc_by_id[ntf.document_id]
            key = (document.document_type, ntf.event,)
            if key in messages_by_subscr_key:
                messages_by_subscr_key[key].append(ntf)
            else:
                subscriptions = DocumentNotificationSubscription.objects \
                    .filter(enabled=True,
                            document_type=document.document_type,
                            event=event,
                            recipients__isnull=False) \
                    .select_related('specified_user', 'specified_role') \
                    .prefetch_related(Prefetch('user_fields',
                                               queryset=DocumentField.objects.all().order_by('order')))
                subscr_by_key[key] = subscriptions
                messages_by_subscr_key[key] = [ntf]

        notifications_to_send = []  # type: List[RenderedNotification]

        for key in messages_by_subscr_key:
            messages = messages_by_subscr_key[key]
            subscriptions = subscr_by_key[key]
            for sub in subscriptions:
                for msg_pack in chunks(messages, sub.max_stack):
                    # render pack of notifications or just one notification
                    if len(msg_pack) < 2:
                        # render single notification
                        if msg_pack[0].document_id not in doc_by_id or \
                                not doc_by_id[msg_pack[0].document_id]:
                            raise Exception(f'Error in send_notifications_packet(1): doc '
                                            f'with id={msg_pack[0].document_id} was not obtained')
                        document = doc_by_id[msg_pack[0].document_id]
                        handlers = handlers_by_doctype[document.document_type]
                        user = user_by_id[msg_pack[0].changed_by_user_id]

                        try:
                            notification = NotificationRenderer.render_notification(
                                msg_pack[0].package_id,
                                sub,
                                DocumentNotificationSource(
                                    document=document,
                                    field_handlers=handlers,
                                    field_values=msg_pack[0].field_values,
                                    changes=msg_pack[0].changes,
                                    changed_by_user=user))
                            if notification:
                                notifications_to_send.append(notification)
                        except Exception as e:
                            log.error(f'Error in send_notifications_packet(1), '
                                      f'sending render_notification()', exc_info=e)
                    else:
                        not_sources = []  # List[DocumentNotificationSource
                        # render pack of notifications in a single message
                        for msg in msg_pack:
                            if msg.document_id not in doc_by_id or \
                                    not doc_by_id[msg.document_id]:
                                raise Exception(f'Error in send_notifications_packet({len(msg_pack)}: doc '
                                                f'with id={msg.document_id} was not obtained')

                            document = doc_by_id[msg.document_id]
                            handlers = handlers_by_doctype[document.document_type]
                            user = user_by_id[msg.changed_by_user_id]
                            not_src = DocumentNotificationSource(
                                document=document,
                                field_handlers=handlers,
                                field_values=msg.field_values,
                                changes=msg.changes,
                                changed_by_user=user)
                            not_sources.append(not_src)
                        try:
                            notifications = NotificationRenderer.render_notification_pack(
                                [m.package_id for m in msg_pack],
                                sub, not_sources)
                            notifications_to_send += notifications
                        except Exception as e:
                            log.error(f'Error in send_notifications_packet(), '
                                      f'sending render_notification_pack()', exc_info=e)

        log.info(f'notification.send({len(notifications_to_send)})')
        for notification in notifications_to_send:
            notification.send(log=log)
Пример #4
0
def process_notifications_on_document_change(task: ExtendedTask,
                                             document_event: str, document_id,
                                             fields_before: Optional[Dict],
                                             fields_after: Optional[Dict],
                                             changed_by_user_id):
    log = CeleryTaskLogger(task)

    document = Document.objects.filter(pk=document_id).select_related(
        'document_type').first()  # type: Document
    document_type = document.document_type
    changed_by_user = User.objects.get(pk=changed_by_user_id)
    field_handlers = build_field_handlers(document_type,
                                          include_suggested_fields=False)
    field_handlers_by_field_code = {h.field_code: h
                                    for h in field_handlers
                                    }  # Dict[str, FieldHandler]
    already_sent_user_ids = set()

    log_msgs = []

    if document_event == DocumentEvent.CREATED.value:
        if fields_after.get(FIELD_CODE_ASSIGNEE_ID) is not None:
            send_notification(event=DocumentAssignedEvent.code,
                              log=log,
                              already_sent_user_ids=already_sent_user_ids,
                              document=document,
                              field_handlers=field_handlers,
                              field_values=fields_after,
                              changed_by_user=changed_by_user)

        send_notification(event=DocumentLoadedEvent.code,
                          log=log,
                          already_sent_user_ids=already_sent_user_ids,
                          document=document,
                          field_handlers=field_handlers,
                          field_values=fields_after,
                          changed_by_user=changed_by_user)
    elif document_event == DocumentEvent.DELETED.value:
        send_notification(event=DocumentDeletedEvent.code,
                          log=log,
                          already_sent_user_ids=already_sent_user_ids,
                          document=document,
                          field_handlers=field_handlers,
                          field_values=fields_before,
                          changed_by_user=changed_by_user)
    else:
        changes = dict()
        for field_code, old_value in fields_before.items():
            if field_code not in field_handlers_by_field_code \
                    or field_handlers_by_field_code[field_code].is_suggested:
                continue
            new_value = fields_after.get(field_code)
            if not values_look_equal(old_value, new_value):
                changes[field_code] = (old_value, new_value)
                log_msgs.append(
                    format_values_difference(field_code, old_value, new_value))

        if not changes:
            return

        if len(log_msgs) > 0:
            msgs_str = 'Following fields are different:\n    ' + '\n    '.join(
                log_msgs)
            log = CeleryTaskLogger(task)
            log.info(msgs_str)

        if FIELD_CODE_ASSIGNEE_ID in changes:
            send_notification(event=DocumentAssignedEvent.code,
                              log=log,
                              already_sent_user_ids=already_sent_user_ids,
                              document=document,
                              field_handlers=field_handlers,
                              field_values=fields_after,
                              changes=changes,
                              changed_by_user=changed_by_user)

        send_notification(event=DocumentChangedEvent.code,
                          log=log,
                          already_sent_user_ids=already_sent_user_ids,
                          document=document,
                          field_handlers=field_handlers,
                          field_values=fields_after,
                          changes=changes,
                          changed_by_user=changed_by_user)