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 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 create( *, db_session, incident_priority: str, incident_type: str, reporter_email: str, title: str, status: str, description: str, ) -> Incident: participants = [] # TODO should some of this logic be in the incident_create_flow_instead? (kglisson) incident_priority = incident_priority_service.get_by_name( db_session=db_session, name=incident_priority["name"]) incident_type = incident_type_service.get_by_name( db_session=db_session, name=incident_type["name"]) commander_email = resolve_incident_commander_email( db_session, reporter_email, incident_type.name, incident_priority.name, "", title, description, ) commander_info = individual_service.resolve_user_by_email(commander_email) incident_commander_role = participant_role_service.create( db_session=db_session, role=ParticipantRoleType.incident_commander) commander_participant = participant_service.create( db_session=db_session, participant_role=[incident_commander_role]) commander = individual_service.get_or_create( db_session=db_session, email=commander_info["email"], name=commander_info["fullname"], weblink=commander_info["weblink"], ) incident_reporter_role = participant_role_service.create( db_session=db_session, role=ParticipantRoleType.reporter) if reporter_email == commander_email: commander_participant.participant_role.append(incident_reporter_role) else: reporter_participant = participant_service.create( db_session=db_session, participant_role=[incident_reporter_role]) reporter_info = individual_service.resolve_user_by_email( reporter_email) reporter = individual_service.get_or_create( db_session=db_session, email=reporter_info["email"], name=reporter_info["fullname"], weblink=commander_info["weblink"], ) reporter.participant.append(reporter_participant) db_session.add(reporter) participants.append(reporter_participant) participants.append(commander_participant) incident = Incident( title=title, description=description, status=status, incident_priority=incident_priority, incident_type=incident_type, participants=participants, ) commander.participant.append(commander_participant) db_session.add(commander) db_session.add(incident) db_session.commit() return incident
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 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)