diff --git a/src/dispatch/incident/scheduled.py b/src/dispatch/incident/scheduled.py index 0a3a33f6b1aa..404000b75e75 100644 --- a/src/dispatch/incident/scheduled.py +++ b/src/dispatch/incident/scheduled.py @@ -16,6 +16,7 @@ INCIDENT_DAILY_REPORT, INCIDENT_DAILY_REPORT_TITLE, INCIDENT_WEEKLY_REPORT, + INCIDENT_WEEKLY_REPORT_NO_INCIDENTS, INCIDENT_WEEKLY_REPORT_TITLE, INCIDENT_SUMMARY_TEMPLATE, MessageType, @@ -229,7 +230,7 @@ def incident_close_reminder(db_session: Session, project: Project): send_incident_close_reminder(incident, db_session) -@scheduler.add(every(1).day.at("18:00"), name="incident-report-weekly") +@scheduler.add(every().monday.at("18:00"), name="incident-report-weekly") @timer @scheduled_project_task def incident_report_weekly(db_session: Session, project: Project): @@ -255,10 +256,6 @@ def incident_report_weekly(db_session: Session, project: Project): hours=24 * 7, ) - # no incidents closed in the last week - if not incidents: - return - storage_plugin = plugin_service.get_active_instance( db_session=db_session, plugin_type="storage", project_id=project.id ) @@ -319,6 +316,11 @@ def incident_report_weekly(db_session: Session, project: Project): except Exception as e: log.exception(e) + template = INCIDENT_WEEKLY_REPORT + # if no closed incidents or all closed incidents are restricted, send a different template + if not items_grouped: + template = INCIDENT_WEEKLY_REPORT_NO_INCIDENTS + notification_kwargs = { "items_grouped": items_grouped, "items_grouped_template": items_grouped_template, @@ -328,7 +330,7 @@ def incident_report_weekly(db_session: Session, project: Project): notification_params = { "text": notification_title_text, "type": MessageType.incident_weekly_report, - "template": INCIDENT_WEEKLY_REPORT, + "template": template, "kwargs": notification_kwargs, } diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py index 9a93a5ae8f7c..8908b4ad2563 100644 --- a/src/dispatch/messaging/strings.py +++ b/src/dispatch/messaging/strings.py @@ -75,18 +75,31 @@ class MessageType(DispatchEnum): ).strip() INCIDENT_FEEDBACK_DAILY_REPORT_DESCRIPTION = """ -This is a daily report of feedback about incidents handled by you.""".replace("\n", " ").strip() +This is a daily report of feedback about incidents handled by you.""".replace( + "\n", " " +).strip() INCIDENT_WEEKLY_REPORT_TITLE = """ -Incidents Weekly Report""".replace("\n", " ").strip() +Incidents Weekly Report""".replace( + "\n", " " +).strip() INCIDENT_WEEKLY_REPORT_DESCRIPTION = """ This is an AI-generated weekly summary of incidents that have been marked as closed in the last week. NOTE: These summaries may contain errors or inaccuracies. -Please verify the information before relying on it.""".replace("\n", " ").strip() +Please verify the information before relying on it.""".replace( + "\n", " " +).strip() + +INCIDENT_WEEKLY_REPORT_NO_INCIDENTS_DESCRIPTION = """ +No open incidents have been closed in the last week.""".replace( + "\n", " " +).strip() INCIDENT_DAILY_REPORT_TITLE = """ -Incidents Daily Report""".replace("\n", " ").strip() +Incidents Daily Report""".replace( + "\n", " " +).strip() INCIDENT_DAILY_REPORT_DESCRIPTION = """ This is a daily report of incidents that are currently active and incidents that have been marked as stable or closed in the last 24 hours.""".replace( @@ -106,7 +119,9 @@ class MessageType(DispatchEnum): INCIDENT_COMMANDER_DESCRIPTION = """ The Incident Commander (IC) is responsible for knowing the full context of the incident. -Contact them about any questions or concerns.""".replace("\n", " ").strip() +Contact them about any questions or concerns.""".replace( + "\n", " " +).strip() INCIDENT_COMMANDER_READDED_DESCRIPTION = """ {{ commander_fullname }} (Incident Commander) has been re-added to the conversation. @@ -131,40 +146,56 @@ class MessageType(DispatchEnum): INCIDENT_CONVERSATION_DESCRIPTION = """ Private conversation for real-time discussion. All incident participants get added to it. -""".replace("\n", " ").strip() +""".replace( + "\n", " " +).strip() INCIDENT_CONVERSATION_REFERENCE_DOCUMENT_DESCRIPTION = """ Document containing the list of slash commands available to the Incident Commander (IC) -and participants in the incident conversation.""".replace("\n", " ").strip() +and participants in the incident conversation.""".replace( + "\n", " " +).strip() INCIDENT_CONFERENCE_DESCRIPTION = """ Video conference and phone bridge to be used throughout the incident. Password: {{conference_challenge if conference_challenge else 'N/A'}} -""".replace("\n", "").strip() +""".replace( + "\n", "" +).strip() STORAGE_DESCRIPTION = """ Common storage for all artifacts and documents. Add logs, screen captures, or any other data collected during the -investigation to this folder. It is shared with all participants.""".replace("\n", " ").strip() +investigation to this folder. It is shared with all participants.""".replace( + "\n", " " +).strip() INCIDENT_INVESTIGATION_DOCUMENT_DESCRIPTION = """ This is a document for all incident facts and context. All incident participants are expected to contribute to this document. -It is shared with all incident participants.""".replace("\n", " ").strip() +It is shared with all incident participants.""".replace( + "\n", " " +).strip() CASE_INVESTIGATION_DOCUMENT_DESCRIPTION = """ This is a document for all investigation facts and context. All case participants are expected to contribute to this document. -It is shared with all participants.""".replace("\n", " ").strip() +It is shared with all participants.""".replace( + "\n", " " +).strip() INCIDENT_INVESTIGATION_SHEET_DESCRIPTION = """ This is a sheet for tracking impacted assets. All incident participants are expected to contribute to this sheet. -It is shared with all incident participants.""".replace("\n", " ").strip() +It is shared with all incident participants.""".replace( + "\n", " " +).strip() INCIDENT_FAQ_DOCUMENT_DESCRIPTION = """ First time responding to an incident? This document answers common questions encountered when -helping us respond to an incident.""".replace("\n", " ").strip() +helping us respond to an incident.""".replace( + "\n", " " +).strip() INCIDENT_REVIEW_DOCUMENT_DESCRIPTION = """ This document will capture all lessons learned, questions, and action items raised during the incident.""".replace( @@ -188,11 +219,15 @@ class MessageType(DispatchEnum): INCIDENT_RESOLUTION_DEFAULT = """ Description of the actions taken to resolve the incident. -""".replace("\n", " ").strip() +""".replace( + "\n", " " +).strip() CASE_RESOLUTION_DEFAULT = """ Description of the actions taken to resolve the case. -""".replace("\n", " ").strip() +""".replace( + "\n", " " +).strip() INCIDENT_COMPLETED_FORM_DESCRIPTION = """ A new {{form_type}} form related to incident {{name}} has been @@ -200,19 +235,27 @@ class MessageType(DispatchEnum): aspects related to potential legal implications. You can review the detailed report by clicking on the link below. Please note, the information contained in this report is confidential. -""".replace("\n", " ").strip() +""".replace( + "\n", " " +).strip() INCIDENT_PARTICIPANT_WELCOME_DESCRIPTION = """ You\'ve been added to this incident, because we think you may be able to help resolve it. Please review the incident details below and -reach out to the incident commander if you have any questions.""".replace("\n", " ").strip() +reach out to the incident commander if you have any questions.""".replace( + "\n", " " +).strip() INCIDENT_PARTICIPANT_SUGGESTED_READING_DESCRIPTION = """ Dispatch thinks the following documents might be -relevant to this incident.""".replace("\n", " ").strip() +relevant to this incident.""".replace( + "\n", " " +).strip() NOTIFICATION_PURPOSES_FYI = """ -This message is for notification purposes only.""".replace("\n", " ").strip() +This message is for notification purposes only.""".replace( + "\n", " " +).strip() INCIDENT_TACTICAL_REPORT_DESCRIPTION = """ The following conditions, actions, and needs summarize the current status of the incident.""".replace( @@ -241,7 +284,9 @@ class MessageType(DispatchEnum): ).strip() CASE_TRIAGE_REMINDER_DESCRIPTION = """The status of this case hasn't been updated recently. -Please ensure you triage the case based on its priority.""".replace("\n", " ").strip() +Please ensure you triage the case based on its priority.""".replace( + "\n", " " +).strip() CASE_CLOSE_REMINDER_DESCRIPTION = """The status of this case hasn't been updated recently. You can use the case 'Resolve' button if it has been resolved and can be closed.""".replace( @@ -266,7 +311,9 @@ class MessageType(DispatchEnum): INCIDENT_OPEN_TASKS_DESCRIPTION = """ Please resolve or transfer ownership of all the open incident tasks assigned to you in the incident documents or using the <{{dispatch_ui_url}}|Dispatch Web UI>, then wait about 30 seconds for Dispatch to update the tasks before leaving the incident conversation. -""".replace("\n", " ").strip() +""".replace( + "\n", " " +).strip() INCIDENT_TASK_ADD_TO_INCIDENT_DESCRIPTION = """ You have been added to this incident because you were assigned a task related to it. View all tasks for this incident using the <{{dispatch_ui_url}}|Dispatch Web UI> @@ -771,7 +818,9 @@ class MessageType(DispatchEnum): CASE_ASSIGNEE_DESCRIPTION = """ The Case Assignee is responsible for knowing the full context of the case. -Contact them about any questions or concerns.""".replace("\n", " ").strip() +Contact them about any questions or concerns.""".replace( + "\n", " " +).strip() CASE_REPORTER_DESCRIPTION = """ The person who reported the case. Contact them if the report details need clarification.""".replace( @@ -940,6 +989,10 @@ class MessageType(DispatchEnum): "text": INCIDENT_WEEKLY_REPORT_DESCRIPTION, } +INCIDENT_WEEKLY_REPORT_HEADER_NO_INCIDENTS_DESCRIPTION = { + "text": INCIDENT_WEEKLY_REPORT_NO_INCIDENTS_DESCRIPTION, +} + INCIDENT_DAILY_REPORT_HEADER = { "type": "header", "text": INCIDENT_DAILY_REPORT_TITLE, @@ -966,6 +1019,11 @@ class MessageType(DispatchEnum): INCIDENT_DAILY_REPORT_FOOTER, ] +INCIDENT_WEEKLY_REPORT_NO_INCIDENTS = [ + INCIDENT_WEEKLY_REPORT_HEADER, + INCIDENT_WEEKLY_REPORT_HEADER_NO_INCIDENTS_DESCRIPTION, +] + INCIDENT = [ INCIDENT_NAME_WITH_ENGAGEMENT_NO_DESCRIPTION, INCIDENT_TITLE,