feat: create base notification templates
This commit is contained in:
parent
e703fe9598
commit
bf93eae041
7 changed files with 298 additions and 0 deletions
|
|
@ -867,3 +867,75 @@ def _parse_receiver_by_document_field(s):
|
|||
else:
|
||||
data_field, child_field = fragments[0], None
|
||||
return data_field, child_field
|
||||
|
||||
|
||||
def create_notifications(notifications: list[dict], update: bool = False):
|
||||
"""
|
||||
Unlike standard notifications, these are NOT marked as is_standard=1,
|
||||
so they won't be overwritten during migrations. Users can freely customize them.
|
||||
|
||||
Args:
|
||||
notifications: List of notification dicts.
|
||||
update: If True, update existing notification. If False (default), skip if exists.
|
||||
"""
|
||||
for notif_dict in notifications:
|
||||
name = notif_dict.get("name")
|
||||
existing = frappe.db.exists("Notification", name)
|
||||
|
||||
if existing and not update:
|
||||
continue
|
||||
|
||||
if existing and update:
|
||||
doc = frappe.get_doc("Notification", name)
|
||||
doc.update(notif_dict)
|
||||
doc.flags.ignore_validate = True
|
||||
doc.save(ignore_permissions=True)
|
||||
continue
|
||||
|
||||
notif_dict["doctype"] = "Notification"
|
||||
notif_dict["is_standard"] = 0
|
||||
notif_dict["owner"] = "Administrator"
|
||||
|
||||
doc = frappe.get_doc(notif_dict)
|
||||
doc.flags.ignore_validate = True
|
||||
doc.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def get_notification_templates(templates_dir: str) -> list[dict]:
|
||||
"""
|
||||
Load notification templates from the templates directory.
|
||||
|
||||
Templates are stored in subdirectories:
|
||||
<templates_dir>/<name>/<name>.json
|
||||
<templates_dir>/<name>/<name>.html|.md|.txt (optional message content based on message_type)
|
||||
"""
|
||||
templates = []
|
||||
|
||||
if not os.path.exists(templates_dir):
|
||||
return templates
|
||||
|
||||
for folder_name in os.listdir(templates_dir):
|
||||
folder_path = os.path.join(templates_dir, folder_name)
|
||||
if not os.path.isdir(folder_path):
|
||||
continue
|
||||
|
||||
json_file = os.path.join(folder_path, f"{folder_name}.json")
|
||||
template = frappe.get_file_json(json_file) if os.path.exists(json_file) else None
|
||||
if not template:
|
||||
continue
|
||||
|
||||
message_type = template.get("message_type", "HTML")
|
||||
ext = FORMATS.get(message_type, ".html")
|
||||
message_file = os.path.join(folder_path, f"{folder_name}{ext}")
|
||||
if message := frappe.read_file(message_file):
|
||||
template["message"] = message
|
||||
|
||||
templates.append(template)
|
||||
|
||||
return templates
|
||||
|
||||
|
||||
def install_notification_templates():
|
||||
templates_dir = frappe.get_module_path("Email", "doctype", "notification", "templates")
|
||||
templates = get_notification_templates(templates_dir)
|
||||
create_notifications(templates, update=False)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
{% set error_lines = (doc.error or "").split('\n') %}
|
||||
{% set first_lines = 10 %}
|
||||
{% set last_lines = 15 %}
|
||||
{% set max_lines = first_lines + last_lines %}
|
||||
{% set total_lines = error_lines | length %}
|
||||
{% set needs_truncation = total_lines > max_lines %}
|
||||
|
||||
<table class="email-header" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 class="email-header-title">
|
||||
<span class="indicator red"></span>
|
||||
Error Log
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered" width="100%">
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8; width: 120px;">Site</td>
|
||||
<td><a href="{{ frappe.utils.get_url() }}">{{ frappe.utils.get_url() }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Error ID</td>
|
||||
<td>{{ frappe.utils.get_link_to_form("Error Log", doc.name, doc.name) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Title</td>
|
||||
<td>{{ doc.method or "N/A" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Logged At</td>
|
||||
<td>{{ doc.creation }}</td>
|
||||
</tr>
|
||||
{% if doc.reference_doctype and doc.reference_name %}
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Reference</td>
|
||||
<td>{{ frappe.utils.get_link_to_form(doc.reference_doctype, doc.reference_name) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="text-medium text-bold">
|
||||
Error Details
|
||||
{% if needs_truncation %}
|
||||
<span class="text-muted" style="font-weight: normal;"> ({{ max_lines }} of {{ total_lines }} lines)</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="gray-container" style="margin-top: 8px;">
|
||||
<pre class="text-small" style="margin: 0; white-space: pre-wrap; word-wrap: break-word;">{% if needs_truncation %}{{ error_lines[:first_lines] | join('\n') }}
|
||||
|
||||
<span class="text-muted" style="font-style: italic;">... {{ total_lines - max_lines }} lines omitted ...</span>
|
||||
|
||||
{{ error_lines[-last_lines:] | join('\n') }}{% else %}{{ error_lines | join('\n') }}{% endif %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="more-info">
|
||||
<a href="{{ frappe.utils.get_url_to_form('Error Log', doc.name) }}" class="btn btn-primary">View Error Log</a>
|
||||
</div>
|
||||
|
||||
<p class="text-muted text-small" style="margin-top: 20px;">
|
||||
This is an automated notification from {{ frappe.utils.get_host_name() }}.
|
||||
</p>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "Error Log",
|
||||
"document_type": "Error Log",
|
||||
"event": "New",
|
||||
"channel": "Email",
|
||||
"enabled": 0,
|
||||
"subject": "[Error] {{ doc.method }}",
|
||||
"message_type": "HTML",
|
||||
"recipients": [
|
||||
{
|
||||
"receiver_by_role": "System Manager"
|
||||
}
|
||||
],
|
||||
"send_system_notification": 0,
|
||||
"send_to_all_assignees": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
{% set error_lines = (doc.error or "").split('\n') %}
|
||||
{% set output_lines = (doc.output or "").split('\n') %}
|
||||
{% set first_lines = 10 %}
|
||||
{% set last_lines = 15 %}
|
||||
{% set max_lines = first_lines + last_lines %}
|
||||
{% set total_error_lines = error_lines | length %}
|
||||
{% set error_needs_truncation = total_error_lines > max_lines %}
|
||||
{% set total_output_lines = output_lines | length %}
|
||||
{% set output_needs_truncation = total_output_lines > max_lines %}
|
||||
|
||||
<table class="email-header" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 class="email-header-title">
|
||||
<span class="indicator red"></span>
|
||||
Integration Request
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered" style="width: 100%;">
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8; width: 120px">Site</td>
|
||||
<td>{{ frappe.utils.get_url() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Request ID</td>
|
||||
<td>{{ frappe.utils.get_link_to_form("Integration Request", doc.name, doc.request_id or
|
||||
doc.name) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Service</td>
|
||||
<td>{{ doc.integration_request_service or "N/A" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Status</td>
|
||||
<td>
|
||||
<span class="indicator-pill red">
|
||||
{{ doc.status }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% if doc.request_description %}
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Description</td>
|
||||
<td>{{ doc.request_description }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Logged At</td>
|
||||
<td>{{ frappe.utils.format_datetime(doc.creation) }}</td>
|
||||
</tr>
|
||||
{% if doc.url %}
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Endpoint URL</td>
|
||||
<td class="text-small" style="word-break: break-all;">{{ doc.url }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if doc.reference_doctype and doc.reference_docname %}
|
||||
<tr>
|
||||
<td class="text-bold" style="background: #f8f8f8;">Reference</td>
|
||||
<td>
|
||||
<a
|
||||
href="{{ frappe.utils.get_url_to_form(doc.reference_doctype, doc.reference_docname) }}">
|
||||
{{ doc.reference_doctype }}: {{ doc.reference_docname }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
{% if doc.error %}
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="text-medium text-bold">
|
||||
Error Details
|
||||
{% if error_needs_truncation %}
|
||||
<span class="text-muted" style="font-weight: normal;"> ({{ max_lines }} of {{
|
||||
total_error_lines }} lines)</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="gray-container" style="margin-top: 8px;">
|
||||
<pre class="text-small"
|
||||
style="margin: 0; white-space: pre-wrap; word-wrap: break-word;">{% if error_needs_truncation %}{{ error_lines[:first_lines] | join('\n') }}
|
||||
|
||||
<span class="text-muted" style="font-style: italic;">... {{ total_error_lines - max_lines }} lines omitted ...</span>
|
||||
|
||||
{{ error_lines[-last_lines:] | join('\n') }}{% else %}{{ error_lines | join('\n') }}{% endif %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.output %}
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="text-medium text-bold">
|
||||
Response Output
|
||||
{% if output_needs_truncation %}
|
||||
<span class="text-muted" style="font-weight: normal;"> ({{ max_lines }} of {{
|
||||
total_output_lines }} lines)</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="gray-container" style="margin-top: 8px;">
|
||||
<pre class="text-small"
|
||||
style="margin: 0; white-space: pre-wrap; word-wrap: break-word;">{% if output_needs_truncation %}{{ output_lines[:first_lines] | join('\n') }}
|
||||
|
||||
<span class="text-muted" style="font-style: italic;">... {{ total_output_lines - max_lines }} lines omitted ...</span>
|
||||
|
||||
{{ output_lines[-last_lines:] | join('\n') }}{% else %}{{ output_lines | join('\n') }}{% endif %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="more-info">
|
||||
<a class="btn btn-primary"
|
||||
href="{{ frappe.utils.get_url_to_form('Integration Request', doc.name) }}">
|
||||
View Integration Request
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="text-muted text-small" style="margin-top: 20px;">
|
||||
This is an automated notification from {{ frappe.utils.get_host_name() }}.
|
||||
</p>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "Integration Request",
|
||||
"document_type": "Integration Request",
|
||||
"event": "Save",
|
||||
"channel": "Email",
|
||||
"condition": "doc.status==\"Failed\"",
|
||||
"enabled": 0,
|
||||
"subject": "[Error] {{ doc.integration_request_service }}",
|
||||
"message_type": "HTML",
|
||||
"recipients": [
|
||||
{
|
||||
"receiver_by_role": "System Manager"
|
||||
}
|
||||
],
|
||||
"send_system_notification": 0,
|
||||
"send_to_all_assignees": 0
|
||||
}
|
||||
|
|
@ -256,3 +256,4 @@ frappe.patches.v16_0.add_standard_field_in_workspace_sidebar
|
|||
execute:frappe.db.set_single_value("Desktop Settings", "icon_style", "Solid")
|
||||
execute:frappe.delete_doc_if_exists("Workspace Sidebar", "Productivity")
|
||||
frappe.patches.v16_0.unset_standard_field_for_auto_generated_icons
|
||||
execute:frappe.email.doctype.notification.notification.install_notification_templates
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import getpass
|
||||
|
||||
import frappe
|
||||
from frappe.email.doctype.notification.notification import install_notification_templates
|
||||
from frappe.geo.doctype.country.country import import_country_and_currency
|
||||
from frappe.utils import cint
|
||||
from frappe.utils.password import update_password
|
||||
|
|
@ -53,6 +54,9 @@ def after_install():
|
|||
|
||||
add_standard_navbar_items()
|
||||
|
||||
# default templates
|
||||
install_notification_templates()
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue