def render_notification( package_id: str, subscription: DocumentNotificationSubscription, data: DocumentNotificationSource, log_routine: Optional[Callable[[str], None]] = None ) -> Optional[RenderedNotification]: if package_id in NotificationRenderer.package_already_sent_user_ids: already_sent_user_ids = NotificationRenderer.package_already_sent_user_ids[ package_id] else: already_sent_user_ids = set() NotificationRenderer.package_already_sent_user_ids[ package_id] = already_sent_user_ids document_type = data.document.document_type event_info = subscription.get_event_info() if not event_info: return None NotificationRenderer.get_document_fields(data, document_type, subscription, data.changes) doc_field_values = data.field_values if not doc_field_values: return None display_fields = set(doc_field_values.keys()) recipients = subscription.resolve_recipients(data.field_values) recipients = { r for r in recipients if r.id not in already_sent_user_ids } if recipients else None if not recipients: return None changes_filtered = dict() if data.changes: for code, old_new in data.changes.items(): if code in FIELD_CODES_HIDE_BY_DEFAULT: continue if old_new[0] == old_new[1] or data.field_values.get( code) == old_new[0]: continue changes_filtered[code] = old_new changes = changes_filtered display_fields.update(changes.keys()) template_context = { 'app_url': root_url(), 'doc_url': doc_editor_url(document_type.code, data.document.project_id, data.document.pk), 'event_code': event_info.code, 'event_title': event_info.title, 'event_initiator': data.changed_by_user, 'document': data.field_values, 'fields': [{ 'code': h.field_code, 'title': h.field_title, 'type': h.field_type, 'value': data.field_values.get(h.field_code), 'changed': h.field_code in changes, 'changes': changes.get(h.field_code), } for h in data.field_handlers if h.field_code in display_fields], 'changes': changes, 'changed_by_user': data.changed_by_user, 'logo_url': 'images/logo.png' } # type: Dict[str, Any] subject_template = subscription.subject or event_info.default_subject header_template = subscription.header or event_info.default_header subject = Template(subject_template).render(template_context) header = Template(header_template).render(template_context) template_context.update({'subject': subject, 'header': header}) html = None html_template = get_notification_template_resource( os.path.join(subscription.template_name, 'template.html')) if html_template: template_src = html_template.decode('utf-8') template_obj = Template(template_src) html = template_obj.render(template_context) txt_template_name = os.path.join(subscription.template_name, 'template.txt') txt_template = get_notification_template_resource(txt_template_name) if not txt_template: raise RuntimeError(f'Txt template not found: {txt_template_name}') txt = Template(txt_template.decode('utf-8')).render(template_context) image_dir = os.path.join(subscription.template_name, 'images') return RenderedNotification(dst_users=recipients, subject=subject, txt=txt, html=html, image_dir=image_dir, cc=subscription.get_cc_addrs())
def render_digest(config: DocumentDigestConfig, dst_user: User, run_date: datetime.datetime, emulate_no_docs: bool = False) -> Optional[RenderedDigest]: period_start = None period_end = None documents_filter = DOC_FILTERS_BY_CODE[config.documents_filter] period = DIGEST_PERIODS_BY_CODE[config.period] if config.period else None if period: period_start, period_end = period.prepare_period( config, dst_user, run_date) user_field_codes = list(config.user_fields.all().values_list('code', flat=True)) generic_field_codes = config.generic_fields or [] field_codes = [FIELD_CODE_DOC_ID, FIELD_CODE_PROJECT_ID, FIELD_CODE_DOC_NAME] \ + generic_field_codes + user_field_codes if emulate_no_docs: documents = EmptyDocumentQueryResults() else: documents = documents_filter.prepare_documents( document_type=config.document_type, user=dst_user, field_codes=field_codes, period_start=period_start, period_end=period_end) if not config.still_send_if_no_docs and (not documents or not documents.documents): return None template_context = { 'to_user': dst_user, 'period_aware': documents_filter.period_aware, 'period_start': period_start, 'period_end': period_end, 'documents': documents, 'document_type': config.document_type, 'doc_url_resolver': doc_editor_url, 'app_url': root_url() } # type: Dict[str, Any] subject_template = config.subject or documents_filter.subject header_template = config.header or documents_filter.header subject = Template(subject_template).render(template_context) header = Template(header_template).render(template_context) no_docs_message = None if config.still_send_if_no_docs: no_docs_message_template = config.message_if_no_docs or documents_filter.message_if_no_docs no_docs_message = Template(no_docs_message_template).render( template_context) template_context.update({ 'subject': subject, 'header': header, 'no_docs_message': no_docs_message }) html = None html_template = get_notification_template_resource( os.path.join(config.template_name, 'template.html')) if html_template: html = Template(html_template.decode('utf-8')).render(template_context) txt_template_name = os.path.join(config.template_name, 'template.txt') txt_template = get_notification_template_resource(txt_template_name) if not txt_template: raise RuntimeError( 'Txt template not found: {0}'.format(txt_template_name)) txt = Template(txt_template.decode('utf-8')).render(template_context) image_dir = os.path.join(config.template_name, 'images') return RenderedDigest(config=config, dst_user=dst_user, date=run_date, subject=subject, txt=txt, html=html, image_dir=image_dir)
def render_notification_pack( package_ids: List[str], subscription: DocumentNotificationSubscription, data: List[DocumentNotificationSource] ) -> List[RenderedNotification]: notifications_pack = [] # type: List[RenderedNotification] if not data: return notifications_pack event_info = subscription.get_event_info() if not event_info: return notifications_pack already_sent_user_ids = set() for pack_id in package_ids: if pack_id in NotificationRenderer.package_already_sent_user_ids: stored = NotificationRenderer.package_already_sent_user_ids[ pack_id] if stored: already_sent_user_ids.update(stored) else: NotificationRenderer.package_already_sent_user_ids[ pack_id] = set() # merge message bodies # all the documents have the same type document_type = data[0].document.document_type template_context = { 'app_url': root_url(), 'event_code': event_info.code, 'event_title': event_info.title, 'documents': [], 'logo_url': 'images/logo.png' } # get recipients per message, split message packs by recipients msgs_by_recipients = {} # type:[str, List[DocumentNotificationSource]] recipients_by_key = {} # type:[str, List[User]] for msg_data in data: NotificationRenderer.get_document_fields(msg_data, document_type, subscription, msg_data.changes) if not msg_data.field_values: continue recipients = subscription.resolve_recipients( msg_data.field_values) # type:Set[User] recipients = { r for r in recipients if r.id not in already_sent_user_ids } if recipients else None if not recipients: continue recp_ids = [str(r.pk) for r in recipients] recp_ids.sort() recp_key = ','.join(recp_ids) if recp_key in msgs_by_recipients: msgs_by_recipients[recp_key].append(msg_data) else: msgs_by_recipients[recp_key] = [msg_data] recipients_by_key[recp_key] = recipients for recp_key in msgs_by_recipients: recipients = recipients_by_key[recp_key] for msg_data in msgs_by_recipients[recp_key]: doc_field_values = msg_data.field_values display_fields = set(doc_field_values.keys()) changes_filtered = dict() if msg_data.changes: for code, old_new in msg_data.changes.items(): if code in FIELD_CODES_HIDE_BY_DEFAULT: continue if old_new[0] == old_new[ 1] or msg_data.field_values.get( code) == old_new[0]: continue changes_filtered[code] = old_new changes = changes_filtered display_fields.update(changes.keys()) document_context = { 'doc_url': doc_editor_url(document_type.code, msg_data.document.project_id, msg_data.document.pk), 'event_initiator': msg_data.changed_by_user, 'document': msg_data.field_values, 'fields': [{ 'code': h.field_code, 'title': h.field_title, 'type': h.field_type, 'value': msg_data.field_values.get(h.field_code), 'changed': h.field_code in changes, 'changes': changes.get(h.field_code), } for h in msg_data.field_handlers if h.field_code in display_fields], 'changes': changes, 'changed_by_user': msg_data.changed_by_user } # type: Dict[str, Any] template_context['documents'].append(document_context) html = None subject_template = subscription.subject or event_info.default_bulk_subject header_template = subscription.header or event_info.default_bulk_header subject = Template(subject_template).render(template_context) header = Template(header_template).render(template_context) template_context.update({'subject': subject, 'header': header}) html_template = get_notification_template_resource( os.path.join(subscription.template_name, 'template_pack.html')) if html_template: html = Template( html_template.decode('utf-8')).render(template_context) txt_template_name = os.path.join(subscription.template_name, 'template_pack.txt') txt_template = get_notification_template_resource( txt_template_name) if not txt_template: raise RuntimeError( 'Txt template not found: {0}'.format(txt_template_name)) txt = Template( txt_template.decode('utf-8')).render(template_context) image_dir = os.path.join(subscription.template_name, 'images') notification = RenderedNotification(dst_users=recipients, subject=subject, txt=txt, html=html, image_dir=image_dir, cc=subscription.get_cc_addrs()) notifications_pack.append(notification) return notifications_pack
def render_notification(already_sent_user_ids: Set, subscription: DocumentNotificationSubscription, document: Document, field_handlers: List[FieldHandler], field_values: Dict[str, Any], changes: Dict[str, Tuple[Any, Any]] = None, changed_by_user: User = None) -> Optional[RenderedNotification]: document_type = document.document_type event_info = subscription.get_event_info() if not event_info: return None recipients = subscription.resolve_recipients(field_values) recipients = {r for r in recipients if r.id not in already_sent_user_ids} if recipients else None if not recipients: return None display_fields = set(subscription.generic_fields or {}) display_fields.update(FIELD_CODES_SHOW_BY_DEFAULT_GENERIC if document_type.is_generic() else FIELD_CODES_SHOW_BY_DEFAULT_NON_GENERIC) display_fields.update({f.code for f in subscription.user_fields.all() if f.document_type == document_type}) changes_filtered = dict() if changes: for code, old_new in changes.items(): if code in FIELD_CODES_HIDE_BY_DEFAULT: continue if old_new[0] == old_new[1] or field_values.get(code) == old_new[0]: continue changes_filtered[code] = old_new changes = changes_filtered display_fields.update(changes.keys()) template_context = { 'app_url': root_url(), 'doc_url': doc_editor_url(document_type.code, document.project_id, document.pk), 'event_code': event_info.code, 'event_title': event_info.title, 'event_initiator': changed_by_user, 'document': field_values, 'fields': [{ 'code': h.field_code, 'title': h.field_title, 'type': h.field_type, 'value': field_values.get(h.field_code), 'changed': h.field_code in changes, 'changes': changes.get(h.field_code), } for h in field_handlers if h.field_code in display_fields], 'changes': changes, 'changed_by_user': changed_by_user } # type: Dict[str, Any] subject_template = subscription.subject or event_info.default_subject header_template = subscription.header or event_info.default_subject subject = Template(subject_template).render(template_context) header = Template(header_template).render(template_context) template_context.update({ 'subject': subject, 'header': header }) html = None html_template = get_notification_template_resource(os.path.join(subscription.template_name, 'template.html')) if html_template: html = Template(html_template.decode('utf-8')).render(template_context) txt_template_name = os.path.join(subscription.template_name, 'template.txt') txt_template = get_notification_template_resource(txt_template_name) if not txt_template: raise RuntimeError('Txt template not found: {0}'.format(txt_template_name)) txt = Template(txt_template.decode('utf-8')).render(template_context) image_dir = os.path.join(subscription.template_name, 'images') return RenderedNotification(dst_users=recipients, subject=subject, txt=txt, html=html, image_dir=image_dir, cc=subscription.get_cc_addrs())