Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds new field to capture incident resolution #1992

Merged
merged 7 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Adds resolution column to incident data model

Revision ID: 3097592c0739
Revises: 5f96c909a3c0
Create Date: 2022-02-15 15:48:45.724812

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "3097592c0739"
down_revision = "5f96c909a3c0"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("incident", sa.Column("resolution", sa.String(), nullable=True))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("incident", "resolution")
# ### end Alembic commands ###
269 changes: 149 additions & 120 deletions src/dispatch/incident/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,83 +292,187 @@ def create_incident_storage(
return storage


def create_collaboration_documents(incident: Incident, db_session: SessionLocal):
"""Create external collaboration document."""
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="storage"
)

collab_documents = []
def create_incident_documents(incident: Incident, db_session: SessionLocal):
"""Create incident documents."""
incident_documents = []

if not incident.storage:
return collab_documents
return incident_documents

document_name = f"{incident.name} - Incident Document"
# we get the storage plugin
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="storage"
)

if plugin:
# TODO can we make move and copy in one api call? (kglisson)
incident_document_name = f"{incident.name} - Incident Document"

if incident.incident_type.incident_template_document:
document = plugin.instance.copy_file(
incident.storage.resource_id,
incident.incident_type.incident_template_document.resource_id,
document_name,
incident_document_name,
)
plugin.instance.move_file(incident.storage.resource_id, document["id"])

# create a blank document if no template is defined
else:
# create a blank document if no template is defined
document = plugin.instance.create_file(
incident.storage.resource_id, document_name, file_type="document"
incident.storage.resource_id, incident_document_name, file_type="document"
)

# TODO this logic should probably be pushed down into the plugins i.e. making them return
# the fields we expect instead of re-mapping. (kglisson)
document.update(
{
"name": document_name,
"name": incident_document_name,
"resource_type": DocumentResourceTypes.incident,
"resource_id": document["id"],
}
)

collab_documents.append(document)
incident_documents.append(document)

event_service.log(
db_session=db_session,
source=plugin.plugin.title,
description="Incident investigation document created",
description="Incident document created",
incident_id=incident.id,
)

sheet = None
if incident.incident_type.tracking_template_document:
sheet_name = f"{incident.name} - Incident Tracking Sheet"
incident_sheet_name = f"{incident.name} - Incident Tracking Sheet"
sheet = plugin.instance.copy_file(
incident.storage.resource_id,
incident.incident_type.tracking_template_document.resource_id,
sheet_name,
incident_sheet_name,
)
plugin.instance.move_file(incident.storage.resource_id, sheet["id"])

sheet.update(
{
"name": sheet_name,
"name": incident_sheet_name,
"resource_type": DocumentResourceTypes.tracking,
"resource_id": sheet["id"],
}
)
collab_documents.append(sheet)

incident_documents.append(sheet)

event_service.log(
db_session=db_session,
source=plugin.plugin.title,
description="Incident investigation sheet created",
description="Incident sheet created",
incident_id=incident.id,
)

# we create folders to store logs and screengrabs
plugin.instance.create_file(incident.storage.resource_id, "logs")
plugin.instance.create_file(incident.storage.resource_id, "screengrabs")

return collab_documents
return incident_documents


def create_post_incident_review_document(incident: Incident, db_session: SessionLocal):
"""Create post-incident review document."""
# we get the storage plugin
storage_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="storage"
)
if not storage_plugin:
log.warning("Post-incident review document not created. No storage plugin enabled.")
return

# we create a copy of the incident review document template and we move it to the incident storage
incident_review_document_name = f"{incident.name} - Post-Incident Review Document"

# incident review document is optional
if not incident.incident_type.review_template_document:
log.warning("No template for post-incident review document has been specified.")
return

# we create the document
incident_review_document = storage_plugin.instance.copy_file(
folder_id=incident.storage.resource_id,
file_id=incident.incident_type.review_template_document.resource_id,
name=incident_review_document_name,
)

incident_review_document.update(
{
"name": incident_review_document_name,
"resource_type": DocumentResourceTypes.review,
}
)

# we move the document to the storage
storage_plugin.instance.move_file(
new_folder_id=incident.storage.resource_id,
file_id=incident_review_document["id"],
)

event_service.log(
db_session=db_session,
source=storage_plugin.plugin.title,
description="Post-incident review document added to storage",
incident_id=incident.id,
)

# we add the document to the incident
document_in = DocumentCreate(
name=incident_review_document["name"],
resource_id=incident_review_document["id"],
resource_type=incident_review_document["resource_type"],
project=incident.project,
weblink=incident_review_document["weblink"],
)

incident.documents.append(
document_service.create(db_session=db_session, document_in=document_in)
)

event_service.log(
db_session=db_session,
source="Dispatch Core App",
description="Post-incident review document added to incident",
incident_id=incident.id,
)

# we update the post-incident review document
update_document(incident.incident_review_document.resource_id, incident, db_session)

db_session.add(incident)
db_session.commit()


def update_document(document_resource_id: str, incident: Incident, db_session: SessionLocal):
"""Updates an existing document."""
# we get the document plugin
document_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="document"
)

if not document_plugin:
log.warning("No document plugin enabled. Document not updated.")
return

document_plugin.instance.update(
document_resource_id,
commander_fullname=incident.commander.individual.name,
conference_challenge=resolve_attr(incident, "conference.challenge"),
conference_weblink=resolve_attr(incident, "conference.weblink"),
conversation_weblink=resolve_attr(incident, "conversation.weblink"),
description=incident.description,
document_weblink=resolve_attr(incident, "incident_document.weblink"),
name=incident.name,
priority=incident.incident_priority.name,
resolution=incident.resolution,
status=incident.status,
storage_weblink=resolve_attr(incident, "storage.weblink"),
ticket_weblink=resolve_attr(incident, "ticket.weblink"),
title=incident.title,
type=incident.incident_type.name,
)


def create_conversation(incident: Incident, db_session: SessionLocal):
Expand Down Expand Up @@ -612,9 +716,9 @@ def incident_create_flow(*, organization_slug: str, incident_id: int, db_session

# we create collaboration documents, don't fail the whole flow if this fails
try:
collab_documents = create_collaboration_documents(incident, db_session)
incident_documents = create_incident_documents(incident, db_session)

for d in collab_documents:
for d in incident_documents:
document_in = DocumentCreate(
name=d["name"],
resource_id=d["resource_id"],
Expand Down Expand Up @@ -837,103 +941,30 @@ def incident_stable_status_flow(incident: Incident, db_session=None):
"""Runs the incident stable flow."""
# we set the stable time
incident.stable_at = datetime.utcnow()
db_session.add(incident)
db_session.commit()

if incident.incident_review_document:
log.debug("Incident review document already created... skipping creation.")
return

storage_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="storage"
)
if not storage_plugin:
log.warning("Incident review document not created, no storage plugin enabled.")
return

# we create a copy of the incident review document template and we move it to the incident storage
incident_review_document_name = f"{incident.name} - Post Incident Review Document"
if incident.incident_document:
# we update the incident document
update_document(incident.incident_document.resource_id, incident, db_session)

# incident review document is optional
if not incident.incident_type.review_template_document:
log.warning("No incident review template specified.")
if incident.incident_review_document:
log.debug(
"The post-incident review document has already been created. Skipping creation..."
)
return

incident_review_document = storage_plugin.instance.copy_file(
folder_id=incident.storage.resource_id,
file_id=incident.incident_type.review_template_document.resource_id,
name=incident_review_document_name,
)

incident_review_document.update(
{
"name": incident_review_document_name,
"resource_type": DocumentResourceTypes.review,
}
)
# we create the post-incident review document
create_post_incident_review_document(incident, db_session)

storage_plugin.instance.move_file(
new_folder_id=incident.storage.resource_id,
file_id=incident_review_document["id"],
)

event_service.log(
db_session=db_session,
source=storage_plugin.plugin.title,
description="Incident review document added to storage",
incident_id=incident.id,
)

document_in = DocumentCreate(
name=incident_review_document["name"],
resource_id=incident_review_document["id"],
resource_type=incident_review_document["resource_type"],
project=incident.project,
weblink=incident_review_document["weblink"],
)
incident.documents.append(
document_service.create(db_session=db_session, document_in=document_in)
)

event_service.log(
db_session=db_session,
source="Dispatch Core App",
description="Incident review document added to incident",
incident_id=incident.id,
)

# we update the incident review document
document_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=incident.project.id, plugin_type="document"
)
if document_plugin:
document_plugin.instance.update(
incident.incident_review_document.resource_id,
name=incident.name,
priority=incident.incident_priority.name,
status=incident.status,
type=incident.incident_type.name,
title=incident.title,
description=incident.description,
commander_fullname=incident.commander.individual.name,
conversation_weblink=resolve_attr(incident, "conversation.weblink"),
document_weblink=resolve_attr(incident, "incident_document.weblink"),
storage_weblink=resolve_attr(incident, "storage.weblink"),
ticket_weblink=resolve_attr(incident, "ticket.weblink"),
conference_weblink=resolve_attr(incident, "conference.weblink"),
conference_challenge=resolve_attr(incident, "conference.challenge"),
if incident.incident_review_document:
# we send a notification about the incident review document to the conversation
send_incident_review_document_notification(
incident.conversation.channel_id,
incident.incident_review_document.weblink,
incident,
db_session,
)
else:
log.warning("No document plugin enabled, could not update template.")

# we send a notification about the incident review document to the conversation
send_incident_review_document_notification(
incident.conversation.channel_id,
incident.incident_review_document.weblink,
incident,
db_session,
)

db_session.add(incident)
db_session.commit()


def incident_closed_status_flow(incident: Incident, db_session=None):
Expand All @@ -943,8 +974,6 @@ def incident_closed_status_flow(incident: Incident, db_session=None):

# we set the closed time
incident.closed_at = datetime.utcnow()

# set time immediately
db_session.add(incident)
db_session.commit()

Expand Down Expand Up @@ -1196,15 +1225,15 @@ def incident_assign_role_flow(
# NOTE: This is disabled until we can determine the source of the caller
# we let the assigner know that the assignee already has this role
# send_incident_participant_has_role_ephemeral_message(
# assigner_email, assignee_contact_info, assignee_role, incident
# assigner_email, assignee_contact_info, assignee_role, incident
# )
return

if result == "role_not_assigned":
# NOTE: This is disabled until we can determine the source of the caller
# we let the assigner know that we were not able to assign the role
# send_incident_participant_role_not_assigned_ephemeral_message(
# assigner_email, assignee_contact_info, assignee_role, incident
# assigner_email, assignee_contact_info, assignee_role, incident
# )
return

Expand Down