def create_task_reminders(db_session=None): """Creates multiple task reminders.""" tasks = task_service.get_overdue_tasks(db_session=db_session) log.debug(f"New tasks that need reminders. NumTasks: {len(tasks)}") # lets only remind for active incidents for now tasks = [t for t in tasks if t.incident.status == IncidentStatus.active] if tasks: contact_fullname = contact_weblink = DISPATCH_HELP_EMAIL # NOTE INCIDENT_ONCALL_SERVICE_ID is optional if INCIDENT_ONCALL_SERVICE_ID: oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID) oncall_plugin = plugin_service.get_by_slug( db_session=db_session, slug=oncall_service.type) if oncall_plugin.enabled: log.warning( f"Unable to resolve oncall, INCIDENT_ONCALL_SERVICE_ID configured but associated plugin ({oncall_plugin.name}) is not enabled." ) oncall_email = oncall_plugin.instance.get( service_id=INCIDENT_ONCALL_SERVICE_ID) oncall_individual = individual_service.resolve_user_by_email( oncall_email, db_session) contact_fullname = oncall_individual["fullname"] contact_weblink = oncall_individual["weblink"] grouped_tasks = group_tasks_by_assignee(tasks) for assignee, tasks in grouped_tasks.items(): create_reminder(db_session, assignee, tasks, contact_fullname, contact_weblink)
def incident_engage_oncall_flow(user_email: str, incident_id: int, action: dict, db_session=None): """Runs the incident engage oncall flow.""" oncall_service_id = action["submission"]["oncall_service_id"] page = action["submission"]["page"] # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we resolve the oncall service oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=oncall_service_id) oncall_plugin = plugins.get(oncall_service.type) oncall_email = oncall_plugin.get(service_id=oncall_service_id) # we add the oncall to the incident incident_add_or_reactivate_participant_flow(oncall_email, incident.id, db_session=db_session) if page == "Yes": # we page the oncall oncall_plugin.page(oncall_service_id, incident.name, incident.title, incident.description) log.debug(f"{user_email} has engaged oncall service {oncall_service.name}")
def create_task_reminders(db_session: SessionLocal, project: Project): """Creates multiple task reminders.""" tasks = task_service.get_overdue_tasks(db_session=db_session, project_id=project.id) log.debug(f"New tasks that need reminders. NumTasks: {len(tasks)}") # let's only remind for active incidents for now tasks = [t for t in tasks if t.incident.status == IncidentStatus.active] if tasks: contact_fullname = contact_weblink = DISPATCH_HELP_EMAIL # NOTE INCIDENT_ONCALL_SERVICE_ID is optional if INCIDENT_ONCALL_SERVICE_ID: oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID) if not oncall_service: log.warning( "INCIDENT_ONCALL_SERVICE_ID configured in the .env file, but not found in the database. Did you create the oncall service in the UI?" ) return oncall_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=project.id, plugin_type="oncall") if not oncall_plugin: log.warning( f"Unable to resolve oncall. No oncall plugin is enabled. Project: {project.name}" ) if oncall_plugin.plugin.slug != oncall_service.type: log.warning( f"Unable to resolve the oncall. Oncall plugin enabled not of type {oncall_plugin.plugin.slug}." ) return if not oncall_plugin: log.warning( f"Unable to resolve the oncall, INCIDENT_ONCALL_SERVICE_ID configured, but associated plugin ({oncall_plugin.plugin.slug}) is not enabled." ) contact_fullname = "Unknown" contact_weblink = None else: oncall_email = oncall_plugin.instance.get( service_id=INCIDENT_ONCALL_SERVICE_ID) oncall_individual = individual_service.resolve_user_by_email( oncall_email, db_session) contact_fullname = oncall_individual["fullname"] contact_weblink = oncall_individual["weblink"] grouped_tasks = group_tasks_by_assignee(tasks) for assignee, tasks in grouped_tasks.items(): create_reminder(db_session, assignee, tasks, contact_fullname, contact_weblink)
def incident_engage_oncall_flow( user_email: str, incident_id: int, oncall_service_id: str, page=None, db_session=None ): """Runs the incident engage oncall flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we resolve the oncall service oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=oncall_service_id ) # we get the active oncall plugin oncall_plugin = plugin_service.get_active(db_session=db_session, plugin_type="oncall") if oncall_plugin: if oncall_plugin.slug != oncall_service.type: log.warning( f"Unable to engage the oncall. Oncall plugin enabled not of type {oncall_plugin.slug}." ) return None, None else: log.warning("Unable to engage the oncall. No oncall plugins enabled.") return None, None oncall_email = oncall_plugin.instance.get(service_id=oncall_service_id) # we add the oncall to the incident incident_add_or_reactivate_participant_flow(oncall_email, incident.id, db_session=db_session) # we load the individual individual = individual_service.get_by_email(db_session=db_session, email=oncall_email) event_service.log( db_session=db_session, source=oncall_plugin.title, description=f"{individual.name} engages oncall service {oncall_service.name}", incident_id=incident.id, ) if page == "Yes": # we page the oncall oncall_plugin.instance.page( oncall_service_id, incident.name, incident.title, incident.description ) event_service.log( db_session=db_session, source=oncall_plugin.title, description=f"{oncall_service.name} on-call paged", incident_id=incident.id, ) return individual, oncall_service
def incident_engage_oncall_flow(user_id: str, user_email: str, incident_id: int, action: dict, db_session=None): """Runs the incident engage oncall flow.""" oncall_service_id = action["submission"]["oncall_service_id"] page = action["submission"]["page"] # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we resolve the oncall service oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=oncall_service_id) oncall_plugin = plugins.get(oncall_service.type) oncall_email = oncall_plugin.get(service_id=oncall_service_id) # we add the oncall to the incident incident_add_or_reactivate_participant_flow(oncall_email, incident.id, db_session=db_session) # we load the individual individual = individual_service.get_by_email(db_session=db_session, email=user_email) event_service.log( db_session=db_session, source=oncall_plugin.title, description= f"{individual.name} engages oncall service {oncall_service.name}", incident_id=incident.id, ) if page == "Yes": # we page the oncall oncall_plugin.page(oncall_service_id, incident.name, incident.title, incident.description) event_service.log( db_session=db_session, source=oncall_plugin.title, description=f"{oncall_service.name} on-call paged", incident_id=incident.id, )
def job_engage_oncall_flow(user_id: str, user_email: str, job_id: int, action: dict, db_session=None): """Runs the job engage oncall flow.""" oncall_service_id = action["submission"]["oncall_service_id"] page = action["submission"]["page"] # we load the job instance job = job_service.get(db_session=db_session, job_id=job_id) # we resolve the oncall service oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=oncall_service_id) oncall_plugin = plugins.get(oncall_service.type) oncall_email = oncall_plugin.get(service_id=oncall_service_id) # we add the oncall to the job job_add_or_reactivate_participant_flow(oncall_email, job.id, db_session=db_session) event_service.log( db_session=db_session, source=oncall_plugin.job_code, description= f"{user_email} engages oncall service {oncall_service.name}", job_id=job.id, ) if page == "Yes": # we page the oncall oncall_plugin.page(oncall_service_id, job.name, job.job_code, job.description) event_service.log( db_session=db_session, source=oncall_plugin.job_code, description=f"{oncall_service.name} on-call paged", job_id=job.id, )
def daily_summary(db_session=None): """Fetches all open incidents and provides a daily summary.""" blocks = [] blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": f"*{INCIDENT_DAILY_SUMMARY_DESCRIPTION}*" }, }) active_incidents = get_all_by_status(db_session=db_session, status=IncidentStatus.active) if active_incidents: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": f"*{INCIDENT_DAILY_SUMMARY_ACTIVE_INCIDENTS_DESCRIPTION}*", }, }) for idx, incident in enumerate(active_incidents): if incident.visibility == Visibility.open: try: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": (f"*<{incident.ticket.weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>" ), }, "block_id": f"{ConversationButtonActions.invite_user}-active-{idx}", "accessory": { "type": "button", "text": { "type": "plain_text", "text": "Join Incident" }, "value": f"{incident.id}", }, }) except Exception as e: sentry_sdk.capture_exception(e) else: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": INCIDENT_DAILY_SUMMARY_NO_ACTIVE_INCIDENTS_DESCRIPTION, }, }) blocks.append({"type": "divider"}) blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": f"*{INCIDENT_DAILY_SUMMARY_STABLE_CLOSED_INCIDENTS_DESCRIPTION}*", }, }) hours = 24 stable_incidents = get_all_last_x_hours_by_status( db_session=db_session, status=IncidentStatus.stable, hours=hours) closed_incidents = get_all_last_x_hours_by_status( db_session=db_session, status=IncidentStatus.closed, hours=hours) if stable_incidents or closed_incidents: for idx, incident in enumerate(stable_incidents): if incident.visibility == Visibility.open: try: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": (f"*<{incident.ticket.weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n" f"*Status*: {incident.status}"), }, "block_id": f"{ConversationButtonActions.invite_user}-{idx}", "accessory": { "type": "button", "text": { "type": "plain_text", "text": "Join Incident" }, "value": f"{incident.id}", }, }) except Exception as e: sentry_sdk.capture_exception(e) for incident in closed_incidents: if incident.visibility == Visibility.open: try: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": (f"*<{incident.ticket.weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n" f"*Status*: {incident.status}"), }, }) except Exception as e: sentry_sdk.capture_exception(e) else: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": INCIDENT_DAILY_SUMMARY_NO_STABLE_CLOSED_INCIDENTS_DESCRIPTION, }, }) # NOTE INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID is optional if INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID: oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID) oncall_plugin = plugins.get(oncall_service.type) oncall_email = oncall_plugin.get( service_id=INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID) oncall_individual = individual_service.resolve_user_by_email( oncall_email) blocks.append({ "type": "context", "elements": [{ "type": "mrkdwn", "text": f"For questions about this notification, reach out to <{oncall_individual['weblink']}|{oncall_individual['fullname']}> (current on-call)", }], }) convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG) for c in INCIDENT_NOTIFICATION_CONVERSATIONS: convo_plugin.send(c, "Incident Daily Summary", {}, "", blocks=blocks)
def test_get_by_external_id(session, service): from dispatch.service.service import get_by_external_id t_service = get_by_external_id(db_session=session, external_id=service.external_id) assert t_service.external_id == service.external_id
def daily_summary(db_session=None): """ Fetches all active, and stable and closed incidents in the last 24 hours and sends a daily summary to all incident notification conversations. """ blocks = [] blocks.append( { "type": "header", "text": { "type": "plain_text", "text": f"{INCIDENT_DAILY_SUMMARY_DESCRIPTION}", }, } ) active_incidents = get_all_by_status(db_session=db_session, status=IncidentStatus.active) if active_incidents: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": f"*{INCIDENT_DAILY_SUMMARY_ACTIVE_INCIDENTS_DESCRIPTION}*", }, } ) for idx, incident in enumerate(active_incidents): ticket_weblink = resolve_attr(incident, "ticket.weblink") if incident.visibility == Visibility.open: try: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>" ), }, "block_id": f"{ConversationButtonActions.invite_user}-active-{idx}", "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Join Incident"}, "value": f"{incident.id}", }, } ) except Exception as e: log.exception(e) blocks.append( { "type": "context", "elements": [ { "type": "mrkdwn", "text": f"For more information about active incidents, please visit the active incidents status <{DISPATCH_UI_URL}/incidents/status|page>.", } ], } ) else: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": INCIDENT_DAILY_SUMMARY_NO_ACTIVE_INCIDENTS_DESCRIPTION, }, } ) blocks.append({"type": "divider"}) blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": f"*{INCIDENT_DAILY_SUMMARY_STABLE_CLOSED_INCIDENTS_DESCRIPTION}*", }, } ) hours = 24 stable_incidents = get_all_last_x_hours_by_status( db_session=db_session, status=IncidentStatus.stable, hours=hours ) closed_incidents = get_all_last_x_hours_by_status( db_session=db_session, status=IncidentStatus.closed, hours=hours ) if stable_incidents or closed_incidents: for idx, incident in enumerate(stable_incidents): ticket_weblink = resolve_attr(incident, "ticket.weblink") if incident.visibility == Visibility.open: try: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n" f"*Status*: {incident.status}" ), }, "block_id": f"{ConversationButtonActions.invite_user}-stable-{idx}", "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Join Incident"}, "value": f"{incident.id}", }, } ) except Exception as e: log.exception(e) for incident in closed_incidents: ticket_weblink = resolve_attr(incident, "ticket.weblink") if incident.visibility == Visibility.open: try: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n" f"*Status*: {incident.status}" ), }, } ) except Exception as e: log.exception(e) else: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": INCIDENT_DAILY_SUMMARY_NO_STABLE_CLOSED_INCIDENTS_DESCRIPTION, }, } ) # NOTE INCIDENT_ONCALL_SERVICE_ID is optional if INCIDENT_ONCALL_SERVICE_ID: oncall_service = service_service.get_by_external_id( db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID ) if not oncall_service: log.warning( "INCIDENT_ONCALL_SERVICE_ID configured in the .env file, but not found in the database. Did you create the oncall service in the UI?" ) return oncall_plugin = plugin_service.get_active(db_session=db_session, plugin_type="oncall") if not oncall_plugin: log.warning( f"Unable to resolve the oncall, INCIDENT_ONCALL_SERVICE_ID configured, but associated plugin ({oncall_plugin.slug}) is not enabled." ) return if oncall_plugin.slug != oncall_service.type: log.warning( f"Unable to resolve the oncall. Oncall plugin enabled not of type {oncall_plugin.slug}." ) return oncall_email = oncall_plugin.instance.get(service_id=INCIDENT_ONCALL_SERVICE_ID) oncall_individual = individual_service.resolve_user_by_email(oncall_email, db_session) blocks.append( { "type": "context", "elements": [ { "type": "mrkdwn", "text": f"For any questions about this notification, please reach out to <{oncall_individual['weblink']}|{oncall_individual['fullname']}> (current on-call)", } ], } ) plugin = plugin_service.get_active(db_session=db_session, plugin_type="conversation") for c in INCIDENT_NOTIFICATION_CONVERSATIONS: plugin.instance.send(c, "Incident Daily Summary", {}, "", blocks=blocks)