From 1ae7e617a106691ce1afcfab098ecca5a016cd77 Mon Sep 17 00:00:00 2001 From: Nathan Yee Date: Wed, 6 Aug 2025 14:49:38 -0700 Subject: [PATCH 1/4] Emojify and simplify change notifications --- src/dispatch/case/messaging.py | 16 ++-- src/dispatch/incident/messaging.py | 16 ++-- src/dispatch/messaging/strings.py | 89 ++++--------------- .../plugins/dispatch_slack/messaging.py | 12 ++- 4 files changed, 44 insertions(+), 89 deletions(-) diff --git a/src/dispatch/case/messaging.py b/src/dispatch/case/messaging.py index 1ac8f5c0f942..28ccb3ca091b 100644 --- a/src/dispatch/case/messaging.py +++ b/src/dispatch/case/messaging.py @@ -248,10 +248,10 @@ def send_case_update_notifications(case: Case, previous_case: CaseRead, db_sessi assignee_fullname=case.assignee.individual.name, assignee_team=case.assignee.team, assignee_weblink=case.assignee.individual.weblink, - case_priority_new=case.case_priority.name, - case_priority_old=previous_case.case_priority.name, - case_severity_new=case.case_severity.name, - case_severity_old=previous_case.case_severity.name, + case_priority_new=case.case_priority, + case_priority_old=previous_case.case_priority, + case_severity_new=case.case_severity, + case_severity_old=previous_case.case_severity, case_status_new=case.status, case_status_old=previous_case.status, case_type_new=case.case_type.name, @@ -285,10 +285,10 @@ def send_case_update_notifications(case: Case, previous_case: CaseRead, db_sessi "contact_fullname": case.assignee.individual.name, "contact_weblink": case.assignee.individual.weblink, "case_id": case.id, - "case_priority_new": case.case_priority.name, - "case_priority_old": previous_case.case_priority.name, - "case_severity_new": case.case_severity.name, - "case_severity_old": previous_case.case_severity.name, + "case_priority_new": case.case_priority, + "case_priority_old": previous_case.case_priority, + "case_severity_new": case.case_severity, + "case_severity_old": previous_case.case_severity, "case_status_new": case.status, "case_status_old": previous_case.status, "case_type_new": case.case_type.name, diff --git a/src/dispatch/incident/messaging.py b/src/dispatch/incident/messaging.py index 7fa74e3873ee..1608d2668548 100644 --- a/src/dispatch/incident/messaging.py +++ b/src/dispatch/incident/messaging.py @@ -453,10 +453,10 @@ def send_incident_update_notifications( commander_fullname=incident.commander.individual.name, commander_team=incident.commander.team, commander_weblink=incident.commander.individual.weblink, - incident_priority_new=incident.incident_priority.name, - incident_priority_old=previous_incident.incident_priority.name, - incident_severity_new=incident.incident_severity.name, - incident_severity_old=previous_incident.incident_severity.name, + incident_priority_new=incident.incident_priority, + incident_priority_old=previous_incident.incident_priority, + incident_severity_new=incident.incident_severity, + incident_severity_old=previous_incident.incident_severity, incident_status_new=incident.status, incident_status_old=previous_incident.status, incident_type_new=incident.incident_type.name, @@ -487,10 +487,10 @@ def send_incident_update_notifications( "contact_fullname": incident.commander.individual.name, "contact_weblink": incident.commander.individual.weblink, "incident_id": incident.id, - "incident_priority_new": incident.incident_priority.name, - "incident_priority_old": previous_incident.incident_priority.name, - "incident_severity_new": incident.incident_severity.name, - "incident_severity_old": previous_incident.incident_severity.name, + "incident_priority_new": incident.incident_priority, + "incident_priority_old": previous_incident.incident_priority, + "incident_severity_new": incident.incident_severity, + "incident_severity_old": previous_incident.incident_severity, "incident_status_new": incident.status, "incident_status_old": previous_incident.status, "incident_type_new": incident.incident_type.name, diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py index 9e7e73b74512..b8b7ccabcce2 100644 --- a/src/dispatch/messaging/strings.py +++ b/src/dispatch/messaging/strings.py @@ -431,28 +431,9 @@ class MessageType(DispatchEnum): ONCALL_SHIFT_FEEDBACK_RECEIVED_DESCRIPTION = """ We received your feedback for your shift that ended {{ shift_end_at }} UTC. Thank you!""" -INCIDENT_STATUS_CHANGE_DESCRIPTION = """ -The incident status has been changed from {{ incident_status_old }} to {{ incident_status_new }}.""".replace( - "\n", " " -).strip() - -INCIDENT_TYPE_CHANGE_DESCRIPTION = """ -The incident type has been changed from {{ incident_type_old }} to {{ incident_type_new }}.""".replace( - "\n", " " -).strip() - -INCIDENT_SEVERITY_CHANGE_DESCRIPTION = """ -The incident severity has been changed from {{ incident_severity_old }} to {{ incident_severity_new }}.""".replace( - "\n", " " -).strip() - -INCIDENT_PRIORITY_CHANGE_DESCRIPTION = """ -The incident priority has been changed from {{ incident_priority_old }} to {{ incident_priority_new }}.""".replace( - "\n", " " -).strip() INCIDENT_NAME_WITH_ENGAGEMENT = { - "title": "{{name}} Incident Notification", + "title": ":rotating_light: {{name}} Incident Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, "buttons": [ @@ -488,7 +469,7 @@ class MessageType(DispatchEnum): } INCIDENT_NAME_WITH_ENGAGEMENT_NO_SELF_JOIN = { - "title": "{{name}} Incident Notification", + "title": ":rotating_light: {{name}} Incident Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, "buttons": [ @@ -501,13 +482,13 @@ class MessageType(DispatchEnum): } CASE_NAME = { - "title": "{{name}} Case Notification", + "title": ":briefcase: {{name}} Case Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, } CASE_NAME_WITH_ENGAGEMENT = { - "title": "{{name}} Case Notification", + "title": ":briefcase: {{name}} Case Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, "buttons": [ @@ -533,60 +514,32 @@ class MessageType(DispatchEnum): } CASE_NAME_WITH_ENGAGEMENT_NO_SELF_JOIN = { - "title": "{{name}} Case Notification", + "title": ":briefcase: {{name}} Case Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, } -CASE_STATUS_CHANGE_DESCRIPTION = """ -The case status has been changed from {{ case_status_old }} to {{ case_status_new }}.""".replace( - "\n", " " -).strip() - -CASE_TYPE_CHANGE_DESCRIPTION = """ -The case type has been changed from {{ case_type_old }} to {{ case_type_new }}.""".replace( - "\n", " " -).strip() - -CASE_SEVERITY_CHANGE_DESCRIPTION = """ -The case severity has been changed from {{ case_severity_old }} to {{ case_severity_new }}.""".replace( - "\n", " " -).strip() - -CASE_PRIORITY_CHANGE_DESCRIPTION = """ -The case priority has been changed from {{ case_priority_old }} to {{ case_priority_new }}.""".replace( - "\n", " " -).strip() - -CASE_VISIBILITY_CHANGE_DESCRIPTION = """ -The case visibility has been changed from {{ case_visibility_old }} to {{ case_visibility_new }}.""".replace( - "\n", " " -).strip() CASE_STATUS_CHANGE = { - "title": "Status Change", - "text": CASE_STATUS_CHANGE_DESCRIPTION, + "title": "*{% if case_status_new == 'Closed' %}:white_check_mark:{% elif case_status_new == 'New' %}:new:{% elif case_status_new == 'Triage' %}:mag:{% elif case_status_new == 'Stable' %}:shield:{% elif case_status_new == 'Escalated' %}:arrow_up:{% else %}:arrows_counterclockwise:{% endif %} Status Change:* {{ case_status_old }} → {{ case_status_new }}", } -CASE_TYPE_CHANGE = {"title": "Case Type Change", "text": CASE_TYPE_CHANGE_DESCRIPTION} +CASE_TYPE_CHANGE = {"title": "*:label: Case Type Change:* {{ case_type_old }} → {{ case_type_new }}"} CASE_SEVERITY_CHANGE = { - "title": "Severity Change", - "text": CASE_SEVERITY_CHANGE_DESCRIPTION, + "title": "*{% if case_severity_old.view_order < case_severity_new.view_order %}:arrow_up:{% elif case_severity_old.view_order > case_severity_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Severity Change:* {{ case_severity_old.name }} → {{ case_severity_new.name }}", } CASE_PRIORITY_CHANGE = { - "title": "Priority Change", - "text": CASE_PRIORITY_CHANGE_DESCRIPTION, + "title": "*{% if case_priority_old.view_order < case_priority_new.view_order %}:arrow_up:{% elif case_priority_old.view_order > case_priority_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Priority Change:* {{ case_priority_old.name }} → {{ case_priority_new.name }}", } CASE_VISIBILITY_CHANGE = { - "title": "Visibility Change", - "text": CASE_VISIBILITY_CHANGE_DESCRIPTION, + "title": "*{% if case_visibility_new == 'Open' %}:unlock:{% elif case_visibility_new == 'Restricted' %}:lock:{% else %}:eye:{% endif %} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}", } INCIDENT_NAME = { - "title": "{{name}} Incident Notification", + "title": ":rotating_light: {{name}} Incident Notification", "title_link": "{{ticket_weblink}}", "text": NOTIFICATION_PURPOSES_FYI, } @@ -599,9 +552,9 @@ class MessageType(DispatchEnum): INCIDENT_SUMMARY = {"title": "Summary", "text": "{{summary}}"} -INCIDENT_TITLE = {"title": "Title", "text": "{{title}}"} +INCIDENT_TITLE = {"title": "*:memo: Title:* {{title}}"} -CASE_TITLE = {"title": "Title", "text": "{{title}}"} +CASE_TITLE = {"title": "*:memo: Title:* {{title}}"} CASE_STATUS = { "title": "Status - {{status}}", @@ -662,8 +615,7 @@ class MessageType(DispatchEnum): } INCIDENT_COMMANDER = { - "title": "Commander - {{commander_fullname}}, {{commander_team}}", - "title_link": "{{commander_weblink}}", + "title": ":firefighter: Commander: <{{commander_weblink}}|{{commander_fullname}}, {{commander_team}}>", "text": INCIDENT_COMMANDER_DESCRIPTION, } @@ -710,20 +662,17 @@ class MessageType(DispatchEnum): } INCIDENT_STATUS_CHANGE = { - "title": "Status Change", - "text": INCIDENT_STATUS_CHANGE_DESCRIPTION, + "title": "*{% if incident_status_new == 'Closed' %}:white_check_mark:{% elif incident_status_new == 'Stable' %}:shield:{% elif incident_status_new == 'Active' %}:fire:{% else %}:arrows_counterclockwise:{% endif %} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}", } -INCIDENT_TYPE_CHANGE = {"title": "Incident Type Change", "text": INCIDENT_TYPE_CHANGE_DESCRIPTION} +INCIDENT_TYPE_CHANGE = {"title": "*:label: Incident Type Change:* {{ incident_type_old }} → {{ incident_type_new }}"} INCIDENT_SEVERITY_CHANGE = { - "title": "Severity Change", - "text": INCIDENT_SEVERITY_CHANGE_DESCRIPTION, + "title": "*{% if incident_severity_old.view_order < incident_severity_new.view_order %}:arrow_up:{% elif incident_severity_old.view_order > incident_severity_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Severity Change:* {{ incident_severity_old.name }} → {{ incident_severity_new.name }}", } INCIDENT_PRIORITY_CHANGE = { - "title": "Priority Change", - "text": INCIDENT_PRIORITY_CHANGE_DESCRIPTION, + "title": "*{% if incident_priority_old.view_order < incident_priority_new.view_order %}:arrow_up:{% elif incident_priority_old.view_order > incident_priority_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Priority Change:* {{ incident_priority_old.name }} → {{ incident_priority_new.name }}", } INCIDENT_PARTICIPANT_SUGGESTED_READING_ITEM = { @@ -904,7 +853,7 @@ class MessageType(DispatchEnum): } CASE_ASSIGNEE = { - "title": "Assignee - {{assignee_fullname}}, {{assignee_team}}", + "title": ":female-detective: Assignee - {{assignee_fullname}}, {{assignee_team}}", "title_link": "{{assignee_weblink}}", "text": CASE_ASSIGNEE_DESCRIPTION, } diff --git a/src/dispatch/plugins/dispatch_slack/messaging.py b/src/dispatch/plugins/dispatch_slack/messaging.py index e11662b8ca50..e73f6bc4f308 100644 --- a/src/dispatch/plugins/dispatch_slack/messaging.py +++ b/src/dispatch/plugins/dispatch_slack/messaging.py @@ -190,12 +190,18 @@ def build_unexpected_error_message(guid: str) -> str: def format_default_text(item: dict): """Creates the correct Slack text string based on the item context.""" if item.get("title_link"): - return f"*<{item['title_link']}|{item['title']}>*\n{item['text']}" + text_part = f"\n{item['text']}" if item.get('text') else "" + return f"*<{item['title_link']}|{item['title']}>*{text_part}" if item.get("datetime"): return f"*{item['title']}*\n Date: Wed, 6 Aug 2025 16:38:21 -0700 Subject: [PATCH 2/4] Update formatting for case assignee --- src/dispatch/messaging/strings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py index b8b7ccabcce2..dfd86fbff5f4 100644 --- a/src/dispatch/messaging/strings.py +++ b/src/dispatch/messaging/strings.py @@ -853,8 +853,7 @@ class MessageType(DispatchEnum): } CASE_ASSIGNEE = { - "title": ":female-detective: Assignee - {{assignee_fullname}}, {{assignee_team}}", - "title_link": "{{assignee_weblink}}", + "title": ":female-detective: Assignee: <{{assignee_weblink}}|{{assignee_fullname}}, {{assignee_team}}>", "text": CASE_ASSIGNEE_DESCRIPTION, } From ca4cc7adbb05b507cd25c729d441180f3e461907 Mon Sep 17 00:00:00 2001 From: Nathan Yee Date: Fri, 8 Aug 2025 14:13:22 -0700 Subject: [PATCH 3/4] Update jinja formatting from feedback --- src/dispatch/messaging/strings.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py index dfd86fbff5f4..771830dc26d8 100644 --- a/src/dispatch/messaging/strings.py +++ b/src/dispatch/messaging/strings.py @@ -521,7 +521,13 @@ class MessageType(DispatchEnum): CASE_STATUS_CHANGE = { - "title": "*{% if case_status_new == 'Closed' %}:white_check_mark:{% elif case_status_new == 'New' %}:new:{% elif case_status_new == 'Triage' %}:mag:{% elif case_status_new == 'Stable' %}:shield:{% elif case_status_new == 'Escalated' %}:arrow_up:{% else %}:arrows_counterclockwise:{% endif %} Status Change:* {{ case_status_old }} → {{ case_status_new }}", + "title": "*{% set status_emojis = { + 'Closed': ':white_check_mark:', + 'New': ':new:', + 'Triage': ':mag:', + 'Stable': ':shield:', + 'Escalated': ':arrow_up:' + } %}{{ status_emojis.get(case_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ case_status_old }} → {{ case_status_new }}", } CASE_TYPE_CHANGE = {"title": "*:label: Case Type Change:* {{ case_type_old }} → {{ case_type_new }}"} @@ -535,7 +541,10 @@ class MessageType(DispatchEnum): } CASE_VISIBILITY_CHANGE = { - "title": "*{% if case_visibility_new == 'Open' %}:unlock:{% elif case_visibility_new == 'Restricted' %}:lock:{% else %}:eye:{% endif %} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}", + "title": "*{% set visibility_emojis = { + 'Open': ':unlock:', + 'Restricted': ':lock:' + } %}{{ visibility_emojis.get(case_visibility_new, ':eye:') }} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}", } INCIDENT_NAME = { @@ -662,7 +671,11 @@ class MessageType(DispatchEnum): } INCIDENT_STATUS_CHANGE = { - "title": "*{% if incident_status_new == 'Closed' %}:white_check_mark:{% elif incident_status_new == 'Stable' %}:shield:{% elif incident_status_new == 'Active' %}:fire:{% else %}:arrows_counterclockwise:{% endif %} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}", + "title": "*{% set status_emojis = { + 'Closed': ':white_check_mark:', + 'Stable': ':shield:', + 'Active': ':fire:' + } %}{{ status_emojis.get(incident_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}", } INCIDENT_TYPE_CHANGE = {"title": "*:label: Incident Type Change:* {{ incident_type_old }} → {{ incident_type_new }}"} From 7ae44b90eeec69b66eb32ddca1dae7745c0adb32 Mon Sep 17 00:00:00 2001 From: Nathan Yee Date: Fri, 8 Aug 2025 14:25:47 -0700 Subject: [PATCH 4/4] Set dictionary on a single line for the linter --- src/dispatch/messaging/strings.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py index 771830dc26d8..5c06452aeb4b 100644 --- a/src/dispatch/messaging/strings.py +++ b/src/dispatch/messaging/strings.py @@ -521,13 +521,7 @@ class MessageType(DispatchEnum): CASE_STATUS_CHANGE = { - "title": "*{% set status_emojis = { - 'Closed': ':white_check_mark:', - 'New': ':new:', - 'Triage': ':mag:', - 'Stable': ':shield:', - 'Escalated': ':arrow_up:' - } %}{{ status_emojis.get(case_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ case_status_old }} → {{ case_status_new }}", + "title": "*{% set status_emojis = {'Closed': ':white_check_mark:', 'New': ':new:', 'Triage': ':mag:', 'Stable': ':shield:', 'Escalated': ':arrow_up:'} %}{{ status_emojis.get(case_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ case_status_old }} → {{ case_status_new }}", } CASE_TYPE_CHANGE = {"title": "*:label: Case Type Change:* {{ case_type_old }} → {{ case_type_new }}"} @@ -541,10 +535,7 @@ class MessageType(DispatchEnum): } CASE_VISIBILITY_CHANGE = { - "title": "*{% set visibility_emojis = { - 'Open': ':unlock:', - 'Restricted': ':lock:' - } %}{{ visibility_emojis.get(case_visibility_new, ':eye:') }} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}", + "title": "*{% set visibility_emojis = {'Open': ':unlock:', 'Restricted': ':lock:'} %}{{ visibility_emojis.get(case_visibility_new, ':eye:') }} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}", } INCIDENT_NAME = { @@ -671,11 +662,7 @@ class MessageType(DispatchEnum): } INCIDENT_STATUS_CHANGE = { - "title": "*{% set status_emojis = { - 'Closed': ':white_check_mark:', - 'Stable': ':shield:', - 'Active': ':fire:' - } %}{{ status_emojis.get(incident_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}", + "title": "*{% set status_emojis = {'Closed': ':white_check_mark:', 'Stable': ':shield:', 'Active': ':fire:'} %}{{ status_emojis.get(incident_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}", } INCIDENT_TYPE_CHANGE = {"title": "*:label: Incident Type Change:* {{ incident_type_old }} → {{ incident_type_new }}"}