def handle_reaction_added_event(user_email: str, incident_id: int, event: dict = None, db_session=None): """Handles an event where a reaction is added to a message.""" reaction = event.event.reaction if reaction == SLACK_TIMELINE_EVENT_REACTION: conversation_id = event.event.item.channel message_ts = event.event.item.ts message_ts_utc = datetime.datetime.utcfromtimestamp(float(message_ts)) # we fetch the message information response = dispatch_slack_service.list_conversation_messages( slack_client, conversation_id, latest=message_ts, limit=1, inclusive=1) message_text = response["messages"][0]["text"] message_sender_id = response["messages"][0]["user"] # we fetch the individual who sent the message message_sender_email = get_user_email(client=slack_client, user_id=message_sender_id) individual = individual_service.get_by_email( db_session=db_session, email=message_sender_email) # we log the event event_service.log( db_session=db_session, source="Slack Plugin - Conversation Management", description=f'"{message_text}," said {individual.name}', incident_id=incident_id, individual_id=individual.id, started_at=message_ts_utc, )
def save_status_report( user_email: str, conditions: str, actions: str, needs: str, incident_id: int, db_session: SessionLocal, ): """Saves a new status report.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we create a new status report status_report = create( db_session=db_session, conditions=conditions, actions=actions, needs=needs ) # we load the participant participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=user_email ) # we save the status report participant.status_reports.append(status_report) incident.status_reports.append(status_report) db_session.add(participant) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"New status report created by {participant.individual.name}", incident_id=incident_id, )
def incident_active_flow(incident_id: int, command: Optional[dict] = None, db_session=None): """Runs the incident active flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we remind the incident commander to write a status report send_incident_status_report_reminder(incident) # we update the status of the external ticket update_incident_ticket( incident.ticket.resource_id, incident_type=incident.incident_type.name, status=IncidentStatus.active.lower(), ) event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Incident marked as {incident.status}", incident_id=incident.id, )
def incident_update_flow(user_email: str, incident_id: int, previous_incident: IncidentRead, notify=True, db_session=None): """Runs the incident update flow.""" conversation_topic_change = False # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we load the individual individual = individual_service.get_by_email(db_session=db_session, email=user_email) if previous_incident.title != incident.title: event_service.log( db_session=db_session, source="Incident Participant", description= f'{individual.name} changed the incident title to "{incident.title}"', incident_id=incident.id, individual_id=individual.id, ) if previous_incident.description != incident.description: event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident description", details={"description": incident.description}, incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_type.name != incident.incident_type.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description= f"{individual.name} changed the incident type to {incident.incident_type.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_priority.name != incident.incident_priority.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description= f"{individual.name} changed the incident priority to {incident.incident_priority.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.status.value != incident.status: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description= f"{individual.name} marked the incident as {incident.status}", incident_id=incident.id, individual_id=individual.id, ) if conversation_topic_change: # we update the conversation topic set_conversation_topic(incident) if notify: send_incident_update_notifications(incident, previous_incident) # we get the incident document incident_document = get_document( db_session=db_session, incident_id=incident_id, resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT, ) # we update the external ticket update_incident_ticket( db_session, incident.ticket.resource_id, title=incident.title, description=incident.description, incident_type=incident.incident_type.name, priority=incident.incident_priority.name, commander_email=incident.commander.email, conversation_weblink=incident.conversation.weblink, conference_weblink=incident.conference.weblink, document_weblink=incident_document.weblink, storage_weblink=incident.storage.weblink, visibility=incident.visibility, ) log.debug(f"Updated the external ticket {incident.ticket.resource_id}.") # get the incident participants based on incident type and priority individual_participants, team_participants = get_incident_participants( incident, db_session) # lets not attempt to add new participants for non-active incidents (it's confusing) if incident.status == IncidentStatus.active: # we add the individuals as incident participants for individual in individual_participants: incident_add_or_reactivate_participant_flow(individual.email, incident.id, db_session=db_session) # we get the notification group notification_group = group_service.get_by_incident_id_and_resource_type( db_session=db_session, incident_id=incident.id, resource_type=INCIDENT_RESOURCE_NOTIFICATIONS_GROUP, ) team_participant_emails = [x.email for x in team_participants] # we add the team distributions lists to the notifications group group_plugin = plugins.get(INCIDENT_PLUGIN_GROUP_SLUG) group_plugin.add(notification_group.email, team_participant_emails) if previous_incident.status.value != incident.status: if incident.status == IncidentStatus.active: incident_active_flow(incident_id=incident.id, db_session=db_session) elif incident.status == IncidentStatus.stable: incident_stable_flow(incident_id=incident.id, db_session=db_session) elif incident.status == IncidentStatus.closed: if previous_incident.status.value == IncidentStatus.active: incident_stable_flow(incident_id=incident.id, db_session=db_session) incident_closed_flow(incident_id=incident.id, db_session=db_session)
def create(*, db_session, task_in: TaskCreate) -> Task: """Create a new task.""" incident = incident_service.get(db_session=db_session, incident_id=task_in.incident.id) tickets = [ ticket_service.get_or_create_by_weblink(db_session=db_session, weblink=t.weblink, resource_type="task-ticket") for t in task_in.tickets ] assignees = [] for i in task_in.assignees: assignee = incident_flows.incident_add_or_reactivate_participant_flow( db_session=db_session, incident_id=incident.id, user_email=i.individual.email, ) # due to the freeform nature of task assignment, we can sometimes pick up other emails # e.g. a google group that we cannot resolve to an individual assignee if assignee: assignees.append(assignee) creator_email = None if not task_in.creator: creator_email = task_in.owner.individual.email else: creator_email = task_in.creator.individual.email # add creator as a participant if they are not one already creator = incident_flows.incident_add_or_reactivate_participant_flow( db_session=db_session, incident_id=incident.id, user_email=creator_email, ) # if we cannot find any assignees, the creator becomes the default assignee if not assignees: assignees.append(creator) # we add owner as a participant if they are not one already if task_in.owner: owner = incident_flows.incident_add_or_reactivate_participant_flow( db_session=db_session, incident_id=incident.id, user_email=task_in.owner.individual.email, ) else: owner = incident.commander task = Task( **task_in.dict( exclude={"assignees", "owner", "incident", "creator", "tickets"}), creator=creator, owner=owner, assignees=assignees, incident=incident, tickets=tickets, ) event_service.log( db_session=db_session, source="Dispatch Core App", description="New incident task created", details={"weblink": task.weblink}, incident_id=incident.id, ) db_session.add(task) db_session.commit() return task
def create_executive_report( user_email: str, incident_id: int, executive_report_in: ExecutiveReportCreate, organization_slug: str = None, db_session=None, ): """Creates an executive report.""" current_date = date.today().strftime("%B %d, %Y") current_status = executive_report_in.current_status overview = executive_report_in.overview next_steps = executive_report_in.next_steps # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) if not incident.incident_type.executive_template_document: raise ValidationError( [ ErrorWrapper( InvalidConfigurationError( msg="No executive report template defined."), loc="executive_template_document", ) ], model=ExecutiveReportCreate, ) # we fetch all previous executive reports executive_reports = get_all_by_incident_id_and_type( db_session=db_session, incident_id=incident_id, report_type=ReportTypes.executive_report) previous_executive_reports = [] for executive_report in executive_reports: previous_executive_reports.append( f"{executive_report.document.name} - {executive_report.document.weblink}\n" ) # we create a new executive report details = { "current_status": current_status, "overview": overview, "next_steps": next_steps } executive_report_in = ReportCreate( details=details, type=ReportTypes.executive_report, ) executive_report = create(db_session=db_session, report_in=executive_report_in) # we load the participant participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=user_email) # we save the executive report participant.reports.append(executive_report) incident.reports.append(executive_report) db_session.add(participant) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Incident Participant", description= f"{participant.individual.name} created a new executive report", details={ "current_status": current_status, "overview": overview, "next_steps": next_steps }, incident_id=incident_id, individual_id=participant.individual.id, ) # we create a new document for the executive report storage_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="storage") executive_report_document_name = f"{incident.name} - Executive Report - {current_date}" executive_report_document = storage_plugin.instance.copy_file( folder_id=incident.storage.resource_id, file_id=incident.incident_type.executive_template_document.resource_id, name=executive_report_document_name, ) executive_report_document.update({ "name": executive_report_document_name, "resource_type": DocumentResourceTypes.executive, }) storage_plugin.instance.move_file( new_folder_id=incident.storage.resource_id, file_id=executive_report_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.plugin.title, description="Executive report document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=executive_report_document["name"], resource_id=executive_report_document["id"], resource_type=executive_report_document["resource_type"], project=incident.project, weblink=executive_report_document["weblink"], ) executive_report.document = document_service.create( db_session=db_session, document_in=document_in) incident.documents.append(executive_report.document) db_session.add(executive_report) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Executive report document added to incident", incident_id=incident.id, ) # we update the incident update document document_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="document") document_plugin.instance.update( executive_report_document["id"], name=incident.name, title=incident.title, current_date=current_date, current_status=current_status, overview=overview, next_steps=next_steps, previous_reports="\n".join(previous_executive_reports), commander_fullname=incident.commander.individual.name, commander_team=incident.commander.team, commander_weblink=incident.commander.individual.weblink, ) # we send the executive report to the notifications group send_executive_report_to_notifications_group(incident.id, executive_report, db_session) return executive_report
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() # set time immediately 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(db_session=db_session, 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" template = document_service.get_incident_review_template(db_session=db_session) # incident review document is optional if not template: log.warning("No incident review template specificed.") return incident_review_document = storage_plugin.instance.copy_file( folder_id=incident.storage.resource_id, file_id=template.resource_id, name=incident_review_document_name, ) incident_review_document.update( { "name": incident_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, } ) 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.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"], 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(db_session=db_session, 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.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.challendge"), ) 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, db_session, ) db_session.add(incident) db_session.commit()
def create_collaboration_documents(incident: Incident, db_session: SessionLocal): """Create external collaboration document.""" plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage") collab_documents = [] document_name = f"{incident.name} - Incident Document" if plugin: # TODO can we make move and copy in one api call? (kglisson) # NOTE: make template documents optional if incident.incident_type.template_document: document = plugin.instance.copy_file( incident.storage.resource_id, incident.incident_type.template_document.resource_id, document_name, ) plugin.instance.move_file(incident.storage.resource_id, document["id"]) # 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, "resource_type": INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT, "resource_id": document["id"], } ) collab_documents.append(document) event_service.log( db_session=db_session, source=plugin.title, description="Incident investigation document created", incident_id=incident.id, ) sheet = None template = document_service.get_incident_investigation_sheet_template(db_session=db_session) if template: sheet_name = f"{incident.name} - Incident Tracking Sheet" sheet = plugin.instance.copy_file( incident.storage.resource_id, template.resource_id, sheet_name ) plugin.instance.move_file(incident.storage.resource_id, sheet["id"]) sheet.update( { "name": sheet_name, "resource_type": INCIDENT_RESOURCE_INVESTIGATION_SHEET, "resource_id": sheet["id"], } ) collab_documents.append(sheet) event_service.log( db_session=db_session, source=plugin.title, description="Incident investigation sheet created", incident_id=incident.id, ) plugin.instance.create_file(incident.storage.resource_id, "logs") plugin.instance.create_file(incident.storage.resource_id, "screengrabs") return collab_documents
def incident_stable_flow(incident_id: int, command: Optional[dict] = None, db_session=None): """Runs the incident stable flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we set the stable time incident.stable_at = datetime.utcnow() # we remind the incident commander to write a status report send_incident_status_report_reminder(incident) # we update the incident cost incident_cost = incident_service.calculate_cost(incident_id, db_session) # we update the external ticket update_incident_ticket(incident.ticket.resource_id, status=IncidentStatus.stable.lower(), cost=incident_cost) incident_review_document = get_document( db_session=db_session, incident_id=incident.id, resource_type=INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, ) if not incident_review_document: storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) # 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 = storage_plugin.copy_file( team_drive_id=incident.storage.resource_id, file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID, name=incident_review_document_name, ) incident_review_document.update({ "name": incident_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, }) storage_plugin.move_file( new_team_drive_id=incident.storage.resource_id, file_id=incident_review_document["id"]) event_service.log( db_session=db_session, source=storage_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"], 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 get the incident investigation and faq documents incident_document = get_document( db_session=db_session, incident_id=incident_id, resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT, ) # we update the incident review document update_document( incident_review_document["id"], incident.name, incident.incident_priority.name, incident.status, incident.incident_type.name, incident.title, incident.description, incident.commander.name, incident.conversation.weblink, incident_document.weblink, incident.storage.weblink, incident.ticket.weblink, ) # we send a notification about the incident review document to the conversation send_incident_review_document_notification( incident.conversation.channel_id, incident_review_document["weblink"]) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Incident marked as {incident.status}", incident_id=incident.id, )
def create( *, db_session, # job_priority: str = None, # job_type: str, code: str, job_type: str = "visit", org_id: str = None, org_code: str = None, name: str = None, planning_status: str, tags: List[dict] = [], description: str = None, team: TeamCreate, location: LocationCreate, flex_form_data: dict = None, requested_primary_worker: WorkerCreate = None, requested_start_datetime: datetime = None, requested_duration_minutes: float = None, scheduled_primary_worker: WorkerCreate = None, scheduled_secondary_workers: List[WorkerCreate] = [], scheduled_start_datetime: datetime = None, scheduled_duration_minutes: float = None, auto_planning: bool = True, requested_skills: List[str] = [], requested_items: List[str] = [], life_cycle_status: JobLifeCycleUpdate = None ) -> Job: """Creates a new job.""" tag_objs = [] for t in tags: tag_objs.append(tag_service.get_or_create(db_session=db_session, tag_in=TagCreate(**t))) team_obj = team_service.get_by_code(db_session=db_session, code=team["code"]) location_obj = location_service.get_by_location_code( db_session=db_session, location_code=location["location_code"]) if location_obj is None: loc_to_create = LocationCreate(**location) loc_to_create.org_id = org_id if loc_to_create.geo_address_text and not loc_to_create.geo_latitude: try: nid = SHORTUUID.random(length=9) location_config = { "url": config.LOCATION_SERVICE_URL, "token": config.LOCATION_SERVICE_TOKEN, "request_method": config.LOCATION_SERVICE_REQUEST_METHOD, } payload = {"address_id": nid, "input_address": loc_to_create.geo_address_text } # get location service location_plug = service_plugin_service.get_by_service_id_and_type( db_session=db_session, service_id=team_obj.service_id, service_plugin_type=KandboxPlannerPluginType.kandbox_location_service, ).all() if location_plug: location_plugin = plugins.get(location_plug[0].plugin.slug) location_adapter_service = location_plugin(config=location_config) status, _location_ret, msg = location_adapter_service.get_pldt_location(payload) if status: if isinstance(_location_ret['latitude'], float): if _check_location(_location_ret, team_obj.flex_form_data): loc_to_create.geo_latitude = _location_ret['latitude'] loc_to_create.geo_longitude = _location_ret['longitude'] loc_to_create.id = _location_ret['location_id'] loc_to_create.location_code = _location_ret['location_code'] else: logService.create(db_session=db_session, log_in=LogCreate( title='Location Response Data OutSide', category='Location', content=f"location outside,job code:{code},input_address:{payload['input_address']}, msg:{str(_location_ret)}", org_id=int(org_id), team_id=team_obj.id)) log.error( f"Location Response Data OutSide ,{msg} :{payload['input_address']}") else: logService.create(db_session=db_session, log_in=LogCreate( title='Location Response Data NUll', category='Location', content=f"job code:{code},input_address:{payload['input_address']},msg:{str(_location_ret)}", org_id=int(org_id), team_id=team_obj.id)) log.error( f"Location Response Data NUll ,{msg} :{payload['input_address']}") else: logService.create(db_session=db_session, log_in=LogCreate( title=msg['type'], category='Location', content=f"job code:{code},input_address:{payload['input_address']},msg:{str(msg['msg'])}", org_id=int(org_id), team_id=team_obj.id)) log.error( f"Location Response failed ,{msg} :{payload['input_address']}") else: log.error( f"not find location plug,service:{team_obj.service_id},{KandboxPlannerPluginType.kandbox_location_service}") except Exception as e: print(traceback.format_exc()) log.error(f"address request error:{loc_to_create.geo_address_text},{ e} ") location_obj = location_service.get_or_create_by_code( db_session=db_session, location_in=loc_to_create) # if location_obj.geo_longitude < 1: # location_obj.geo_longitude = loc_to_create.geo_longitude # location_obj.geo_latitude = loc_to_create.geo_latitude db_session.add(location_obj) # location_obj = location_service.update( # db_session=db_session, location=location_obj, location_in=LocationUpdate(**location) # ) if requested_primary_worker: requested_primary_worker = worker_service.get_by_code( db_session=db_session, code=requested_primary_worker["code"]) if scheduled_primary_worker: scheduled_primary_worker = worker_service.get_by_code( db_session=db_session, code=scheduled_primary_worker["code"]) scheduled_secondary_workers_list = [] if scheduled_secondary_workers is not None: for w in scheduled_secondary_workers: scheduled_secondary_workers_list.append( worker_service.get_by_code(db_session=db_session, code=w['code'])) # We create the job if requested_skills: flex_form_data['requested_skills'] = requested_skills if requested_items: flex_form_data['requested_items'] = requested_items job = Job( code=code, name=name, org_id=org_id, job_type=job_type, description=description, planning_status=planning_status, tags=tag_objs, flex_form_data=flex_form_data, location=location_obj, team=team_obj, requested_start_datetime=requested_start_datetime, requested_duration_minutes=requested_duration_minutes, requested_primary_worker=requested_primary_worker, scheduled_start_datetime=scheduled_start_datetime, scheduled_duration_minutes=scheduled_duration_minutes, scheduled_primary_worker=scheduled_primary_worker, scheduled_secondary_workers=scheduled_secondary_workers_list, auto_planning=auto_planning, requested_skills=requested_skills, requested_items=requested_items, ) db_session.add(job) if job.job_type == JobType.REPLENISH: depot_code = flex_form_data["depot_code"] depot = depot_service.get_by_code(db_session=db_session, code=depot_code) for item_str in flex_form_data["requested_items"]: item_list = parse_item_str(item_str) item = item_service.get_by_code(db_session=db_session, code=item_list[0]) inv = inventory_service.get_by_item_depot( db_session=db_session, item_id=item.id, depot_id=depot.id, org_id=team_obj.org_id ).one_or_none() inv.curr_qty -= item_list[1] inv.allocated_qty += item_list[1] if inv.curr_qty < 0: log.error( f" Not enough inventory for item: {item_list[0]}, depot: {depot_code}, org.id: {team_obj.org_id}") continue db_session.add(inv) inventory_event_service.log( db_session=db_session, source="Env_Replenish", description=f"Allocated {item_list[1]} {item_list[0]} from depot: {depot_code}", item_code=item_list[0], depot_code=depot_code, item_id=item.id, depot_id=depot.id ) db_session.commit() print(f"\033[37;46m\t1:add job succeed,{code}\033[0m") log.info(f"1:add job succeed,{code}") event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Job ({code}) is created, planning_status={planning_status}, requested_start_datetime={requested_start_datetime}", job_id=job.id, ) post_job_to_kafka(job=job, message_type=KafkaMessageType.CREATE_JOB, db_session=db_session, org_code=org_code) print(f"\033[37;46m\t2:job post kafka in succeed,{code}\033[0m") log.info(f"2:job post kafka in succeed,{code}") # zulip send message if job.planning_status != JobPlanningStatus.UNPLANNED: zulip_dict = get_zulip_client_by_org_id(job.org_id) if zulip_dict: zulip_core = zulip_dict['client'] zulip_core.update_job_send_message( job, [job.scheduled_primary_worker] + job.scheduled_secondary_workers) return job
def create_executive_report(user_id: str, user_email: str, incident_id: int, action: dict, db_session=None): """Creates an executive report.""" report_template = document_service.get_executive_report_template( db_session=db_session) current_date = date.today().strftime("%B %d, %Y") current_status = action["submission"]["current_status"] overview = action["submission"]["overview"] next_steps = action["submission"]["next_steps"] # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) if not report_template: send_feedack_to_user(incident.conversation.channel_id, user_id, "No executive report template defined.") return # we fetch all previous executive reports executive_reports = get_all_by_incident_id_and_type( db_session=db_session, incident_id=incident_id, report_type=ReportTypes.executive_report) previous_executive_reports = [] for executive_report in executive_reports: previous_executive_reports.append( f"{executive_report.document.name} - {executive_report.document.weblink}\n" ) # we create a new executive report details = { "current_status": current_status, "overview": overview, "next_steps": next_steps } executive_report_in = ReportCreate( details=details, type=ReportTypes.executive_report, ) executive_report = create(db_session=db_session, report_in=executive_report_in) # we load the participant participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=user_email) # we save the executive report participant.reports.append(executive_report) incident.reports.append(executive_report) db_session.add(participant) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Incident Participant", description= f"{participant.individual.name} created a new executive report", details={ "current_status": current_status, "overview": overview, "next_steps": next_steps }, incident_id=incident_id, individual_id=participant.individual.id, ) # we create a new document for the executive report storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) executive_report_document_name = f"{incident.name} - Executive Report - {current_date}" executive_report_document = storage_plugin.copy_file( team_drive_id=incident.storage.resource_id, file_id=report_template.resource_id, name=executive_report_document_name, ) executive_report_document.update({ "name": executive_report_document_name, "resource_type": INCIDENT_RESOURCE_EXECUTIVE_REPORT_DOCUMENT, }) storage_plugin.move_file(new_team_drive_id=incident.storage.resource_id, file_id=executive_report_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.title, description="Executive report document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=executive_report_document["name"], resource_id=executive_report_document["id"], resource_type=executive_report_document["resource_type"], weblink=executive_report_document["weblink"], ) executive_report.document = document_service.create( db_session=db_session, document_in=document_in) incident.documents.append(executive_report.document) db_session.add(executive_report) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Executive report document added to incident", incident_id=incident.id, ) # we update the incident update document document_plugin = plugins.get(INCIDENT_PLUGIN_DOCUMENT_SLUG) document_plugin.update( executive_report_document["id"], name=incident.name, title=incident.title, current_date=current_date, current_status=current_status, overview=overview, next_steps=next_steps, previous_reports="\n".join(previous_executive_reports), commander_fullname=incident.commander.name, commander_weblink=incident.commander.weblink, ) # we let the user know that the report has been created send_feedack_to_user( incident.conversation.channel_id, user_id, f"The executive report document has been created and can be found in the incident storage here: {executive_report_document['weblink']}", ) # we send the executive report to the notifications group send_executive_report_to_notifications_group(incident_id, executive_report, db_session) # we let the user know that the report has been sent to the notifications group send_feedack_to_user( incident.conversation.channel_id, user_id, f"The executive report has been emailed to the notifications distribution list ({incident.notifications_group.email}).", ) return executive_report
def job_update_flow(user_email: str, job_id: int, previous_job: JobRead, notify=True, db_session=None): """Runs the job update flow.""" # we load the job instance job = job_service.get(db_session=db_session, job_id=job_id) # we load the worker worker = worker_service.get_by_code(db_session=db_session, code=user_email) conversation_topic_change = False if previous_job.job_code != job.job_code: event_service.log( db_session=db_session, source="Job Participant", description= f'{worker.name} changed the job title to "{job.job_code}"', job_id=job.id, worker_id=worker.id, ) if previous_job.description != job.description: event_service.log( db_session=db_session, source="Job Participant", description=f"{worker.name} changed the job description", details={"description": job.description}, job_id=job.id, worker_id=worker.id, ) if previous_job.job_type.name != job.job_type.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Job Participant", description= f"{worker.name} changed the job type to {job.job_type.name}", job_id=job.id, worker_id=worker.id, ) if previous_job.job_priority.name != job.job_priority.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Job Participant", description= f"{worker.name} changed the job priority to {job.job_priority.name}", job_id=job.id, worker_id=worker.id, ) if previous_job.planning_status.value != job.planning_status: conversation_topic_change = True event_service.log( db_session=db_session, source="Job Participant", description= f"{worker.name} marked the job as {job.planning_status}", job_id=job.id, worker_id=worker.id, )
def job_stable_flow(job_id: int, command: Optional[dict] = None, db_session=None): """Runs the job stable flow.""" # we load the job instance job = job_service.get(db_session=db_session, job_id=job_id) # we set the stable time job.stable_at = datetime.utcnow() # we remind the job commander to write a tactical report send_job_report_reminder(job, ReportTypes.tactical_report) # we update the external ticket update_external_job_ticket(job, db_session) if not job.job_review_document: storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) # we create a copy of the job review document template and we move it to the job storage job_review_document_name = f"{job.name} - Post Job Review Document" job_review_document = storage_plugin.copy_file( team_drive_id=job.storage.resource_id, file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID, name=job_review_document_name, ) job_review_document.update({ "name": job_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, }) storage_plugin.move_file(new_team_drive_id=job.storage.resource_id, file_id=job_review_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.job_code, description="Job review document added to storage", job_id=job.id, ) document_in = DocumentCreate( name=job_review_document["name"], resource_id=job_review_document["id"], resource_type=job_review_document["resource_type"], weblink=job_review_document["weblink"], ) job.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="Job review document added to job", job_id=job.id, ) # we update the job review document update_document( job_review_document["id"], job.name, job.job_priority.name, job.planning_status, job.job_type.name, job.job_code, job.description, job.commander.name, job.conversation.weblink, job.job_document.weblink, job.storage.weblink, job.ticket.weblink, ) # we send a notification about the job review document to the conversation send_job_review_document_notification(job.conversation.channel_id, job_review_document["weblink"]) db_session.add(job) db_session.commit()
def job_create_flow(*, job_id: int, checkpoint: str = None, db_session=None): """Creates all resources required for new jobs.""" job = job_service.get(db_session=db_session, job_id=job_id) # get the job participants based on job type and priority worker_participants, team_participants = get_job_participants( job, db_session) # add workers to job for worker in worker_participants: participant_flows.add_participant(user_code=worker.code, job_id=job.id, db_session=db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Job participants added to job", job_id=job.id, ) # create the job ticket ticket = create_job_ticket(job, db_session) job.ticket = ticket_service.create(db_session=db_session, ticket_in=TicketCreate(**ticket)) event_service.log( db_session=db_session, source="Dispatch Core App", description="External ticket added to job", job_id=job.id, ) # we set the job name name = ticket["resource_id"] job.name = name # we create the participant groups (tactical and notification) try: worker_participants = [x.worker for x in job.participants] tactical_group, notification_group = create_participant_groups( job, worker_participants, team_participants, db_session) for g in [tactical_group, notification_group]: group_in = GroupCreate( name=g["name"], code=g["email"], resource_type=g["resource_type"], resource_id=g["resource_id"], weblink=g["weblink"], ) job.groups.append( group_service.create(db_session=db_session, group_in=group_in)) event_service.log( db_session=db_session, source="Dispatch Core App", description="Tactical and notification groups added to job", job_id=job.id, ) # we create storage resource storage = create_job_storage( job, [tactical_group["email"], notification_group["email"]], db_session) job.storage = storage_service.create( db_session=db_session, resource_id=storage["resource_id"], resource_type=storage["resource_type"], weblink=storage["weblink"], ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Storage added to job", job_id=job.id, ) # we create the job documents job_document, job_sheet = create_collaboration_documents( job, db_session) faq_document = { "name": "Job FAQ", "resource_id": INCIDENT_FAQ_DOCUMENT_ID, "weblink": f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}", "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT, } conversation_commands_reference_document = { "name": "Job Conversation Commands Reference Document", "resource_id": INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID, "weblink": f"https://docs.google.com/document/d/{INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID}", "resource_type": INCIDENT_RESOURCE_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT, } for d in [ job_document, job_sheet, faq_document, conversation_commands_reference_document, ]: document_in = DocumentCreate( name=d["name"], resource_id=d["resource_id"], resource_type=d["resource_type"], weblink=d["weblink"], ) job.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="Documents added to job", job_id=job.id, ) conference = create_conference(job, [tactical_group["email"]], db_session) conference_in = ConferenceCreate( resource_id=conference["resource_id"], resource_type=conference["resource_type"], weblink=conference["weblink"], conference_id=conference["id"], conference_challenge=conference["challenge"], ) job.conference = conference_service.create(db_session=db_session, conference_in=conference_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conference added to job", job_id=job.id, ) # we create the conversation for real-time communications participant_emails = [x.worker.code for x in job.participants] conversation = create_conversation(job, participant_emails, db_session) conversation_in = ConversationCreate( resource_id=conversation["resource_id"], resource_type=conversation["resource_type"], weblink=conversation["weblink"], channel_id=conversation["id"], ) job.conversation = conversation_service.create( db_session=db_session, conversation_in=conversation_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conversation added to job", job_id=job.id, ) except Exception as e: log.warn("failed to create ... , job_id ={}".format(job_id), e) db_session.add(job) db_session.commit() try: # we set the conversation topic set_conversation_topic(job) # we update the job ticket update_external_job_ticket(job, db_session) # we update the investigation document update_document( job_document["id"], job.name, job.job_priority.name, job.planning_status, job.job_type.name, job.job_code, job.description, job.commander.name, job.conversation.weblink, job_document["weblink"], job.storage.weblink, job.ticket.weblink, job.conference.weblink, job.conference.conference_challenge, ) for participant in job.participants: # we announce the participant in the conversation send_job_participant_announcement_message(participant.worker.code, job.id, db_session) # we send the welcome messages to the participant send_job_welcome_participant_messages(participant.worker.code, job.id, db_session) send_job_suggested_reading_messages(participant.worker.code, job.id, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Participants announced and welcome messages sent", job_id=job.id, ) if job.visibility == Visibility.open: send_job_notifications(job, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Job notifications sent", job_id=job.id, ) except Exception as e: log.warn("failed to create more ... , job_id ={}".format(job_id), e)
def create( *, db_session, # job_priority: str = None, # job_type: str, code: str, name: str = None, planning_status: str, tags: List[dict], description: str = None, team: TeamCreate, location: LocationCreate, flex_form_data: dict = None, requested_primary_worker: WorkerCreate = None, requested_start_datetime: datetime = None, requested_duration_minutes: float = None, scheduled_primary_worker: WorkerCreate = None, scheduled_secondary_workers: List[WorkerCreate], scheduled_start_datetime: datetime = None, scheduled_duration_minutes: float = None, auto_planning: bool = True, ) -> Job: """Creates a new job.""" tag_objs = [] for t in tags: tag_objs.append( tag_service.get_or_create(db_session=db_session, tag_in=TagCreate(**t))) team_obj = team_service.get_by_code(db_session=db_session, code=team["code"]) loc_to_create = LocationCreate(**location) location_obj = location_service.get_or_create_by_code( db_session=db_session, location_in=loc_to_create) # if location_obj.geo_longitude < 1: # location_obj.geo_longitude = loc_to_create.geo_longitude # location_obj.geo_latitude = loc_to_create.geo_latitude db_session.add(location_obj) # location_obj = location_service.update( # db_session=db_session, location=location_obj, location_in=LocationUpdate(**location) # ) if requested_primary_worker: requested_primary_worker = worker_service.get_by_code( db_session=db_session, code=requested_primary_worker["code"]) if scheduled_primary_worker: scheduled_primary_worker = worker_service.get_by_code( db_session=db_session, code=scheduled_primary_worker["code"]) if scheduled_secondary_workers is None: scheduled_secondary_workers = [] # We create the job job = Job( code=code, name=name, description=description, planning_status=planning_status, tags=tag_objs, flex_form_data=flex_form_data, location=location_obj, team=team_obj, requested_start_datetime=requested_start_datetime, requested_duration_minutes=requested_duration_minutes, requested_primary_worker=requested_primary_worker, scheduled_start_datetime=scheduled_start_datetime, scheduled_duration_minutes=scheduled_duration_minutes, scheduled_primary_worker=scheduled_primary_worker, scheduled_secondary_workers=scheduled_secondary_workers, auto_planning=auto_planning, ) db_session.add(job) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description= f"Job ({code}) is created, planning_status={planning_status}, scheduled_start_datetime={scheduled_start_datetime}", job_id=job.id, ) post_job_to_kafka(job=job, message_type=KafkaMessageType.CREATE_JOB, db_session=db_session) return job
def incident_create_flow(*, incident_id: int, checkpoint: str = None, db_session=None): """Creates all resources required for new incidents.""" incident = incident_service.get(db_session=db_session, incident_id=incident_id) # get the incident participants based on incident type and priority individual_participants, team_participants = get_incident_participants( incident, db_session) # add individuals to incident for individual in individual_participants: participant_flows.add_participant(user_email=individual.email, incident_id=incident.id, db_session=db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident participants added to incident", incident_id=incident.id, ) # create the incident ticket ticket = create_incident_ticket(incident, db_session) incident.ticket = ticket_service.create(db_session=db_session, ticket_in=TicketCreate(**ticket)) event_service.log( db_session=db_session, source="Dispatch Core App", description="External ticket added to incident", incident_id=incident.id, ) # we set the incident name name = ticket["resource_id"] incident.name = name # we create the participant groups (tactical and notification) individual_participants = [x.individual for x in incident.participants] tactical_group, notification_group = create_participant_groups( incident, individual_participants, team_participants, db_session) for g in [tactical_group, notification_group]: group_in = GroupCreate( name=g["name"], email=g["email"], resource_type=g["resource_type"], resource_id=g["resource_id"], weblink=g["weblink"], ) incident.groups.append( group_service.create(db_session=db_session, group_in=group_in)) event_service.log( db_session=db_session, source="Dispatch Core App", description="Tactical and notification groups added to incident", incident_id=incident.id, ) # we create storage resource storage = create_incident_storage( incident, [tactical_group["email"], notification_group["email"]], db_session) incident.storage = storage_service.create( db_session=db_session, resource_id=storage["resource_id"], resource_type=storage["resource_type"], weblink=storage["weblink"], ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Storage added to incident", incident_id=incident.id, ) # we create the incident documents incident_document, incident_sheet = create_collaboration_documents( incident, db_session) # TODO: we need to delineate between the investigation document and suggested documents # # get any additional documentation based on priority or terms # incident_documents = get_incident_documents( # db_session, incident.incident_type, incident.incident_priority, incident.description # ) # # incident.documents = incident_documents faq_document = { "name": "Incident FAQ", "resource_id": INCIDENT_FAQ_DOCUMENT_ID, "weblink": f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}", "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT, } conversation_commands_reference_document = { "name": "Incident Conversation Commands Reference Document", "resource_id": INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID, "weblink": f"https://docs.google.com/document/d/{INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID}", "resource_type": INCIDENT_RESOURCE_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT, } for d in [ incident_document, incident_sheet, faq_document, conversation_commands_reference_document, ]: document_in = DocumentCreate( name=d["name"], resource_id=d["resource_id"], resource_type=d["resource_type"], weblink=d["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="Documents added to incident", incident_id=incident.id, ) conference = create_conference(incident, [tactical_group["email"]], db_session) conference_in = ConferenceCreate( resource_id=conference["resource_id"], resource_type=conference["resource_type"], weblink=conference["weblink"], conference_id=conference["id"], conference_challenge=conference["challenge"], ) incident.conference = conference_service.create( db_session=db_session, conference_in=conference_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conference added to incident", incident_id=incident.id, ) # we create the conversation for real-time communications participant_emails = [x.individual.email for x in incident.participants] conversation = create_conversation(incident, participant_emails, db_session) conversation_in = ConversationCreate( resource_id=conversation["resource_id"], resource_type=conversation["resource_type"], weblink=conversation["weblink"], channel_id=conversation["id"], ) incident.conversation = conversation_service.create( db_session=db_session, conversation_in=conversation_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conversation added to incident", incident_id=incident.id, ) db_session.add(incident) db_session.commit() # we set the conversation topic set_conversation_topic(incident) # we update the incident ticket update_incident_ticket( incident.ticket.resource_id, title=incident.title, description=incident.description, incident_type=incident.incident_type.name, priority=incident.incident_priority.name, status=incident.status, commander_email=incident.commander.email, reporter_email=incident.reporter.email, conversation_weblink=incident.conversation.weblink, document_weblink=incident_document["weblink"], storage_weblink=incident.storage.weblink, conference_weblink=incident.conference.weblink, visibility=incident.visibility, ) # we update the investigation document update_document( incident_document["id"], incident.name, incident.incident_priority.name, incident.status, incident.incident_type.name, incident.title, incident.description, incident.commander.name, incident.conversation.weblink, incident_document["weblink"], incident.storage.weblink, incident.ticket.weblink, incident.conference.weblink, incident.conference.conference_challenge, ) for participant in incident.participants: # we announce the participant in the conversation send_incident_participant_announcement_message( participant.individual.email, incident.id, db_session) # we send the welcome messages to the participant send_incident_welcome_participant_messages( participant.individual.email, incident.id, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Participants announced and welcome messages sent", incident_id=incident.id, ) if incident.visibility == Visibility.open: send_incident_notifications(incident, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident notifications sent", incident_id=incident.id, )
def update(*, db_session, job: Job, job_in: JobUpdate, org_code: str) -> Job: tags = [] for t in job_in.tags: tags.append(tag_service.get_or_create(db_session=db_session, tag_in=TagUpdate(**t))) scheduled_secondary_workers = [] if job_in.scheduled_secondary_workers: for w in job_in.scheduled_secondary_workers: scheduled_secondary_workers.append( worker_service.get_by_code(db_session=db_session, code=w.code)) if job_in.team and job_in.team.code != job.team.code: team_obj = team_service.get_by_code(db_session=db_session, code=job_in.team.code) job.team = team_obj if job_in.location and job_in.location.location_code and job_in.location.location_code != job.location.location_code: location_obj = location_service.get_or_create_by_code( db_session=db_session, location_in=job_in.location) job.location = location_obj update_data = job_in.dict( skip_defaults=True, exclude={ "tags", "scheduled_secondary_workers", "requested_primary_worker", "scheduled_primary_worker", "team", "location", }, ) for field in update_data.keys(): setattr(job, field, update_data[field]) job.scheduled_secondary_workers = scheduled_secondary_workers job.tags = tags if job_in.scheduled_primary_worker is not None: job.scheduled_primary_worker = worker_service.get_by_code( db_session=db_session, code=job_in.scheduled_primary_worker.code) if job_in.requested_primary_worker is not None: job.requested_primary_worker = worker_service.get_by_code( db_session=db_session, code=job_in.requested_primary_worker.code) db_session.add(job) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Job ({job_in.code}) is updated {job_in.flex_form_data}", job_id=job.id, ) print(f"\033[37;46m\t1: job update succeed {job.code}\033[0m") post_job_to_kafka(job=job, message_type=KafkaMessageType.UPDATE_JOB, db_session=db_session, org_code=org_code) print(f"\033[37;46m\t2: job psot kafka in succeed {job.code}\033[0m") # zulip send message if job.planning_status != JobPlanningStatus.UNPLANNED: zulip_dict = get_zulip_client_by_org_id(job.org_id) if zulip_dict: zulip_core = zulip_dict['client'] zulip_core.update_job_send_message( job, [job.scheduled_primary_worker] + job.scheduled_secondary_workers) return job
def assign_role_flow(incident: "Incident", assignee_email: str, assignee_role: str, db_session: SessionLocal): """Attempts to assign a role to a participant. Returns: str: - "role_assigned", if role assigned. - "role_not_assigned", if not role assigned. - "assignee_has_role", if assignee already has the role. """ # we get the participant that holds the role assigned to the assignee participant_with_assignee_role = participant_service.get_by_incident_id_and_role( db_session=db_session, incident_id=incident.id, role=assignee_role) # we get the participant for the assignee assignee_participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident.id, email=assignee_email) if participant_with_assignee_role is assignee_participant: return "assignee_has_role" if participant_with_assignee_role: # we make the participant renounce to the role that has been given to the assignee participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) for participant_active_role in participant_active_roles: if participant_active_role.role == assignee_role: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we check if the participant has other active roles participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) if participant_active_roles.count() == 0: # we give the participant a new participant role add_role( db_session=db_session, participant_id=participant_with_assignee_role.id, participant_role=ParticipantRoleType.participant, ) log.debug( f"We made {participant_with_assignee_role.individual.name} renounce to their {assignee_role} role." ) if assignee_participant: # we make the assignee renounce to the participant role, if they have it participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=assignee_participant.id) for participant_active_role in participant_active_roles: if participant_active_role.role == ParticipantRoleType.participant: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we give the assignee the new role add_role( db_session=db_session, participant_id=assignee_participant.id, participant_role=assignee_role, ) # we update the commander, reporter, scribe, or liaison foreign key if assignee_role == ParticipantRoleType.incident_commander: incident.commander_id = assignee_participant.id elif assignee_role == ParticipantRoleType.reporter: incident.reporter_id = assignee_participant.id elif assignee_role == ParticipantRoleType.scribe: incident.scribe_id = assignee_participant.id elif assignee_role == ParticipantRoleType.liaison: incident.liaison_id = assignee_participant.id # we add and commit the changes db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description= f"{assignee_participant.individual.name} has been assigned the role of {assignee_role}", incident_id=incident.id, ) return "role_assigned" log.debug( f"We were not able to assign the {assignee_role} role to {assignee_email}." ) return "role_not_assigned"
def incident_update_flow( user_email: str, incident_id: int, previous_incident: IncidentRead, notify=True, db_session=None ): """Runs the incident update flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we load the individual individual = individual_service.get_by_email(db_session=db_session, email=user_email) conversation_topic_change = False if previous_incident.title != incident.title: event_service.log( db_session=db_session, source="Incident Participant", description=f'{individual.name} changed the incident title to "{incident.title}"', incident_id=incident.id, individual_id=individual.id, ) if previous_incident.description != incident.description: event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident description", details={"description": incident.description}, incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_type.name != incident.incident_type.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident type to {incident.incident_type.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_priority.name != incident.incident_priority.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident priority to {incident.incident_priority.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.status.value != incident.status: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} marked the incident as {incident.status}", incident_id=incident.id, individual_id=individual.id, ) if conversation_topic_change: if incident.status != IncidentStatus.closed: set_conversation_topic(incident, db_session) if notify: send_incident_update_notifications(incident, previous_incident, db_session) # we update the external ticket update_external_incident_ticket(incident, db_session) log.debug(f"Updated the external ticket {incident.ticket.resource_id}.") # get the incident participants based on incident type and priority individual_participants, team_participants = get_incident_participants(incident, db_session) # lets not attempt to add new participants for non-active incidents (it's confusing) if incident.status == IncidentStatus.active: # we add the individuals as incident participants for individual in individual_participants: incident_add_or_reactivate_participant_flow( individual.email, incident.id, db_session=db_session ) team_participant_emails = [x.email for x in team_participants] # we add the team distributions lists to the notifications group group_plugin = plugin_service.get_active(db_session=db_session, plugin_type="participant-group") if group_plugin: group_plugin.instance.add(incident.notifications_group.email, team_participant_emails) if previous_incident.status.value != incident.status: if incident.status == IncidentStatus.active: incident_active_flow(incident_id=incident.id, db_session=db_session) elif incident.status == IncidentStatus.stable: incident_stable_flow(incident_id=incident.id, db_session=db_session) elif incident.status == IncidentStatus.closed: if previous_incident.status.value == IncidentStatus.active: incident_stable_flow(incident_id=incident.id, db_session=db_session) incident_closed_flow(incident_id=incident.id, db_session=db_session)
def incident_create_flow(*, incident_id: int, checkpoint: str = None, db_session=None): """Creates all resources required for new incidents.""" incident = incident_service.get(db_session=db_session, incident_id=incident_id) # get the incident participants based on incident type and priority individual_participants, team_participants = get_incident_participants( incident, db_session) # add individuals to incident for individual in individual_participants: participant_flows.add_participant(user_email=individual.email, incident_id=incident.id, db_session=db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident participants added to incident", incident_id=incident.id, ) # create the incident ticket ticket = create_incident_ticket(incident, db_session) incident.ticket = ticket_service.create(db_session=db_session, ticket_in=TicketCreate(**ticket)) event_service.log( db_session=db_session, source="Dispatch Core App", description="External ticket added to incident", incident_id=incident.id, ) # we set the incident name name = ticket["resource_id"] incident.name = name # we create the participant groups (tactical and notification) individual_participants = [x.individual for x in incident.participants] tactical_group, notification_group = create_participant_groups( incident, individual_participants, team_participants, db_session) for g in [tactical_group, notification_group]: group_in = GroupCreate( name=g["name"], email=g["email"], resource_type=g["resource_type"], resource_id=g["resource_id"], weblink=g["weblink"], ) incident.groups.append( group_service.create(db_session=db_session, group_in=group_in)) event_service.log( db_session=db_session, source="Dispatch Core App", description="Tactical and notification groups added to incident", incident_id=incident.id, ) # we create storage resource storage = create_incident_storage( incident, [tactical_group["email"], notification_group["email"]], db_session) incident.storage = storage_service.create( db_session=db_session, resource_id=storage["resource_id"], resource_type=storage["resource_type"], weblink=storage["weblink"], ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Storage added to incident", incident_id=incident.id, ) # we create the incident documents collab_documents = create_collaboration_documents(incident, db_session) for d in collab_documents: document_in = DocumentCreate( name=d["name"], resource_id=d["resource_id"], resource_type=d["resource_type"], weblink=d["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="Documents added to incident", incident_id=incident.id, ) conference = create_conference(incident, [tactical_group["email"]], db_session) conference_in = ConferenceCreate( resource_id=conference["resource_id"], resource_type=conference["resource_type"], weblink=conference["weblink"], conference_id=conference["id"], conference_challenge=conference["challenge"], ) incident.conference = conference_service.create( db_session=db_session, conference_in=conference_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conference added to incident", incident_id=incident.id, ) # we create the conversation for real-time communications participant_emails = [x.individual.email for x in incident.participants] conversation = create_conversation(incident, participant_emails, db_session) conversation_in = ConversationCreate( resource_id=conversation["resource_id"], resource_type=conversation["resource_type"], weblink=conversation["weblink"], channel_id=conversation["id"], ) incident.conversation = conversation_service.create( db_session=db_session, conversation_in=conversation_in) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conversation added to incident", incident_id=incident.id, ) db_session.add(incident) db_session.commit() # we set the conversation topic set_conversation_topic(incident) # we update the incident ticket update_external_incident_ticket(incident, db_session) # we update the investigation document update_document( incident.incident_document.resource_id, incident.name, incident.incident_priority.name, incident.status, incident.incident_type.name, incident.title, incident.description, incident.commander.name, incident.conversation.weblink, incident.incident_document.weblink, incident.storage.weblink, incident.ticket.weblink, incident.conference.weblink, incident.conference.conference_challenge, ) if incident.visibility == Visibility.open: send_incident_notifications(incident, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident notifications sent", incident_id=incident.id, ) suggested_document_items = get_suggested_document_items( incident, db_session) for participant in incident.participants: # we announce the participant in the conversation # should protect ourselves from failures of any one participant try: send_incident_participant_announcement_message( participant.individual.email, incident.id, db_session) # we send the welcome messages to the participant send_incident_welcome_participant_messages( participant.individual.email, incident.id, db_session) send_incident_suggested_reading_messages(incident, suggested_document_items, participant.email) except Exception as e: log.exception(e) sentry_sdk.capture_exception(e) event_service.log( db_session=db_session, source="Dispatch Core App", description="Participants announced and welcome messages sent", incident_id=incident.id, )
def incident_create_flow(*, incident_id: int, checkpoint: str = None, db_session=None): """Creates all resources required for new incidents.""" incident = incident_service.get(db_session=db_session, incident_id=incident_id) # create the incident ticket ticket = create_incident_ticket(incident, db_session) if ticket: incident.ticket = ticket_service.create( db_session=db_session, ticket_in=TicketCreate(**ticket) ) # we set the incident name incident.name = ticket["resource_id"] # get the incident participants based on incident type and priority individual_participants, team_participants = get_incident_participants(incident, db_session) # add individuals to incident for individual in individual_participants: participant_flows.add_participant( user_email=individual.email, incident_id=incident.id, db_session=db_session ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident participants added to incident", incident_id=incident.id, ) # we create the participant groups (tactical and notification) individual_participants = [x.individual for x in incident.participants] participant_emails = [x.individual.email for x in incident.participants] group_plugin = plugin_service.get_active(db_session=db_session, plugin_type="participant-group") tactical_group = None notification_group = None if group_plugin: try: tactical_group, notification_group = create_participant_groups( incident, individual_participants, team_participants, db_session ) for g in [tactical_group, notification_group]: group_in = GroupCreate( name=g["name"], email=g["email"], resource_type=g["resource_type"], resource_id=g["resource_id"], weblink=g["weblink"], ) incident.groups.append( group_service.create(db_session=db_session, group_in=group_in) ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Tactical and notification groups added to incident", incident_id=incident.id, ) except Exception as e: event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Creation of tactical and notification groups failed. Reason: {e}", incident_id=incident.id, ) log.exception(e) storage_plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage") if storage_plugin: # we create storage resource if group_plugin: storage = create_incident_storage( incident, [tactical_group["email"], notification_group["email"]], db_session ) else: # we don't have a group so add participants directly storage = create_incident_storage(incident, participant_emails, db_session) incident.storage = storage_service.create( db_session=db_session, resource_id=storage["resource_id"], resource_type=storage["resource_type"], weblink=storage["weblink"], ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Storage added to incident", incident_id=incident.id, ) # we create collaboration documents, don't fail the whole flow if this fails try: collab_documents = create_collaboration_documents(incident, db_session) for d in collab_documents: document_in = DocumentCreate( name=d["name"], resource_id=d["resource_id"], resource_type=d["resource_type"], weblink=d["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="Documents added to incident", incident_id=incident.id, ) except Exception as e: event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Creation of incident documents failed. Reason: {e}", incident_id=incident.id, ) log.exception(e) conference_plugin = plugin_service.get_active(db_session=db_session, plugin_type="conference") if conference_plugin: try: participants = participant_emails if group_plugin: # we use the tactical group email if the group plugin is enabled participants = [tactical_group["email"]] conference = create_conference(incident, participants, db_session) conference_in = ConferenceCreate( resource_id=conference["resource_id"], resource_type=conference["resource_type"], weblink=conference["weblink"], conference_id=conference["id"], conference_challenge=conference["challenge"], ) incident.conference = conference_service.create( db_session=db_session, conference_in=conference_in ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conference added to incident", incident_id=incident.id, ) except Exception as e: event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Creation of incident conference failed. Reason: {e}", incident_id=incident.id, ) log.exception(e) # we create the conversation for real-time communications conversation_plugin = plugin_service.get_active( db_session=db_session, plugin_type="conversation" ) if conversation_plugin: try: conversation = create_conversation(incident, participant_emails, db_session) conversation_in = ConversationCreate( resource_id=conversation["resource_id"], resource_type=conversation["resource_type"], weblink=conversation["weblink"], channel_id=conversation["id"], ) incident.conversation = conversation_service.create( db_session=db_session, conversation_in=conversation_in ) event_service.log( db_session=db_session, source="Dispatch Core App", description="Conversation added to incident", incident_id=incident.id, ) # we set the conversation topic set_conversation_topic(incident, db_session) except Exception as e: event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Creation of incident conversation failed. Reason: {e}", incident_id=incident.id, ) log.exception(e) db_session.add(incident) db_session.commit() # we update the incident ticket update_external_incident_ticket(incident, db_session) # we update the investigation document document_plugin = plugin_service.get_active(db_session=db_session, plugin_type="document") if document_plugin: if incident.incident_document: try: document_plugin.instance.update( incident.incident_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.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.challendge"), ) except Exception as e: event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Incident documents rendering failed. Reason: {e}", incident_id=incident.id, ) log.exception(e) if incident.visibility == Visibility.open: send_incident_notifications(incident, db_session) event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident notifications sent", incident_id=incident.id, ) suggested_document_items = get_suggested_document_items(incident.id, db_session) for participant in incident.participants: # we announce the participant in the conversation # should protect ourselves from failures of any one participant try: send_incident_participant_announcement_message( participant.individual.email, incident.id, db_session ) # we send the welcome messages to the participant send_incident_welcome_participant_messages( participant.individual.email, incident.id, db_session ) send_incident_suggested_reading_messages( incident.id, suggested_document_items, participant.individual.email, db_session ) except Exception as e: log.exception(e) event_service.log( db_session=db_session, source="Dispatch Core App", description="Participants announced and welcome messages sent", incident_id=incident.id, )
def create( *, db_session, incident_priority: str, incident_type: str, reporter_email: str, title: str, status: str, description: str, tags: List[dict], visibility: str = None, ) -> Incident: """Creates a new incident.""" # We get the incident type by name if not incident_type: incident_type = incident_type_service.get_default( db_session=db_session) if not incident_type: raise Exception( "No incident type specified and no default has been defined.") else: incident_type = incident_type_service.get_by_name( db_session=db_session, name=incident_type["name"]) # We get the incident priority by name if not incident_priority: incident_priority = incident_priority_service.get_default( db_session=db_session) if not incident_priority: raise Exception( "No incident priority specified and no default has been defined." ) else: incident_priority = incident_priority_service.get_by_name( db_session=db_session, name=incident_priority["name"]) if not visibility: visibility = incident_type.visibility tag_objs = [] for t in tags: tag_objs.append( tag_service.get_or_create(db_session=db_session, tag_in=TagCreate(**t))) # We create the incident incident = Incident( title=title, description=description, status=status, incident_type=incident_type, incident_priority=incident_priority, visibility=visibility, tags=tag_objs, ) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident created", incident_id=incident.id, ) # We add the reporter to the incident reporter_participant = participant_flows.add_participant( reporter_email, incident.id, db_session, ParticipantRoleType.reporter) # We resolve the incident commander email incident_commander_email = resolve_incident_commander_email( db_session, reporter_email, incident_type.name, "", title, description, incident_priority.page_commander, ) if reporter_email == incident_commander_email: # We add the role of incident commander the reporter participant_role_service.add_role( participant_id=reporter_participant.id, participant_role=ParticipantRoleType.incident_commander, db_session=db_session, ) else: # We create a new participant for the incident commander and we add it to the incident participant_flows.add_participant( incident_commander_email, incident.id, db_session, ParticipantRoleType.incident_commander, ) return incident
def conversation_topic_dispatcher( incident: Incident, previous_incident: dict, individual: IndividualContact, db_session: SessionLocal, ): """Determines if the conversation topic needs to be updated.""" conversation_topic_change = False if previous_incident.title != incident.title: event_service.log( db_session=db_session, source="Incident Participant", description=f'{individual.name} changed the incident title to "{incident.title}"', incident_id=incident.id, individual_id=individual.id, ) if previous_incident.description != incident.description: event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident description", details={"description": incident.description}, incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_type.name != incident.incident_type.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident type to {incident.incident_type.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.incident_priority.name != incident.incident_priority.name: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} changed the incident priority to {incident.incident_priority.name}", incident_id=incident.id, individual_id=individual.id, ) if previous_incident.status.value != incident.status: conversation_topic_change = True event_service.log( db_session=db_session, source="Incident Participant", description=f"{individual.name} marked the incident as {incident.status}", incident_id=incident.id, individual_id=individual.id, ) if conversation_topic_change: if incident.status != IncidentStatus.closed: set_conversation_topic(incident, db_session)
def assign_role_flow(incident_id: int, assignee_contact_info: dict, assignee_role: str, db_session: SessionLocal): """Attempts to assign a role to a participant. Returns: str: - "role_assigned", if role assigned. - "role_not_assigned", if not role assigned. - "assignee_has_role", if assignee already has the role. """ # we get the participant that holds the role assigned to the assignee participant_with_assignee_role = participant_service.get_by_incident_id_and_role( db_session=db_session, incident_id=incident_id, role=assignee_role) # we get the participant for the assignee assignee_participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=assignee_contact_info["email"]) if participant_with_assignee_role is assignee_participant: return "assignee_has_role" if participant_with_assignee_role: # we make the participant renounce to the role that has been given to the assignee participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) for participant_active_role in participant_active_roles: if participant_active_role.role == assignee_role: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we check if the participant has other active roles participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) if participant_active_roles.count() == 0: # we give the participant a new participant role add_role( db_session=db_session, participant_id=participant_with_assignee_role.id, participant_role=ParticipantRoleType.participant, ) log.debug( f"We made {participant_with_assignee_role.individual.name} renounce to their {assignee_role} role." ) if assignee_participant: # we make the assignee renounce to the participant role, if they have it participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=assignee_participant.id) for participant_active_role in participant_active_roles: if participant_active_role.role == ParticipantRoleType.participant: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we give the assignee the new role add_role( db_session=db_session, participant_id=assignee_participant.id, participant_role=assignee_role, ) event_service.log( db_session=db_session, source="Dispatch Core App", description= f"{assignee_contact_info['fullname']} has been assigned the role of {assignee_role}", incident_id=incident_id, ) return "role_assigned" log.debug( f"We were not able to assign the {assignee_role} role to {assignee_contact_info['fullname']}." ) return "role_not_assigned"
def create( *, db_session, incident_priority: str, incident_type: str, reporter_email: str, title: str, status: str, description: str, visibility: str = None, ) -> Incident: """Creates a new incident.""" # We get the incident type by name incident_type = incident_type_service.get_by_name( db_session=db_session, name=incident_type["name"]) # We get the incident priority by name incident_priority = incident_priority_service.get_by_name( db_session=db_session, name=incident_priority["name"]) if not visibility: visibility = incident_type.visibility # We create the incident incident = Incident( title=title, description=description, status=status, incident_type=incident_type, incident_priority=incident_priority, visibility=visibility, ) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident created", incident_id=incident.id, ) # We add the reporter to the incident reporter_participant = participant_flows.add_participant( reporter_email, incident.id, db_session, ParticipantRoleType.reporter) # We resolve the incident commander email incident_commander_email = resolve_incident_commander_email( db_session, reporter_email, incident_type.name, "", title, description, incident_priority.page_commander, ) if reporter_email == incident_commander_email: # We add the role of incident commander the reporter participant_role_service.add_role( participant_id=reporter_participant.id, participant_role=ParticipantRoleType.incident_commander, db_session=db_session, ) else: # We create a new participant for the incident commander and we add it to the incident participant_flows.add_participant( incident_commander_email, incident.id, db_session, ParticipantRoleType.incident_commander, ) return incident
def create(*, db_session, incident_in: IncidentCreate) -> Incident: """Creates a new incident.""" project = project_service.get_by_name_or_default( db_session=db_session, project_in=incident_in.project) incident_type = incident_type_service.get_by_name_or_default( db_session=db_session, project_id=project.id, incident_type_in=incident_in.incident_type) incident_priority = incident_priority_service.get_by_name_or_default( db_session=db_session, project_id=project.id, incident_priority_in=incident_in.incident_priority, ) if not incident_in.visibility: visibility = incident_type.visibility else: visibility = incident_in.visibility tag_objs = [] for t in incident_in.tags: tag_objs.append( tag_service.get_or_create(db_session=db_session, tag_in=TagCreate(**t))) # We create the incident incident = Incident( title=incident_in.title, description=incident_in.description, status=incident_in.status, incident_type=incident_type, incident_priority=incident_priority, visibility=visibility, tags=tag_objs, project=project, ) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident created", incident_id=incident.id, ) # Add other incident roles (e.g. commander and liaison) assign_incident_role(db_session, incident, incident_in.reporter.individual.email, ParticipantRoleType.reporter) assign_incident_role( db_session, incident, incident_in.reporter.individual.email, ParticipantRoleType.incident_commander, ) assign_incident_role(db_session, incident, incident_in.reporter.individual.email, ParticipantRoleType.liaison) return incident
def create( *, db_session, incident_priority: str, incident_type: str, reporter_email: str, title: str, status: str, description: str, tags: List[dict], visibility: str = None, ) -> Incident: """Creates a new incident.""" # We get the incident type by name if not incident_type: incident_type = incident_type_service.get_default(db_session=db_session) if not incident_type: raise Exception("No incident type specified and no default has been defined.") else: incident_type = incident_type_service.get_by_name( db_session=db_session, name=incident_type["name"] ) # We get the incident priority by name if not incident_priority: incident_priority = incident_priority_service.get_default(db_session=db_session) if not incident_priority: raise Exception("No incident priority specified and no default has been defined.") else: incident_priority = incident_priority_service.get_by_name( db_session=db_session, name=incident_priority["name"] ) if not visibility: visibility = incident_type.visibility tag_objs = [] for t in tags: tag_objs.append(tag_service.get_or_create(db_session=db_session, tag_in=TagCreate(**t))) # We create the incident incident = Incident( title=title, description=description, status=status, incident_type=incident_type, incident_priority=incident_priority, visibility=visibility, tags=tag_objs, ) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident created", incident_id=incident.id, ) # Add other incident roles (e.g. commander and liaison) assign_incident_role(db_session, incident, reporter_email, ParticipantRoleType.reporter) assign_incident_role( db_session, incident, reporter_email, ParticipantRoleType.incident_commander ) assign_incident_role(db_session, incident, reporter_email, ParticipantRoleType.liaison) return incident
def create(*, db_session, incident_in: IncidentCreate) -> Incident: """Creates a new incident.""" project = project_service.get_by_name_or_default( db_session=db_session, project_in=incident_in.project ) incident_type = incident_type_service.get_by_name_or_default( db_session=db_session, project_id=project.id, incident_type_in=incident_in.incident_type ) incident_priority = incident_priority_service.get_by_name_or_default( db_session=db_session, project_id=project.id, incident_priority_in=incident_in.incident_priority, ) if not incident_in.visibility: visibility = incident_type.visibility else: visibility = incident_in.visibility tag_objs = [] for t in incident_in.tags: tag_objs.append(tag_service.get_or_create(db_session=db_session, tag_in=t)) # We create the incident incident = Incident( title=incident_in.title, description=incident_in.description, status=incident_in.status, incident_type=incident_type, incident_priority=incident_priority, visibility=visibility, tags=tag_objs, project=project, ) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Incident created", incident_id=incident.id, ) # add reporter reporter_email = incident_in.reporter.individual.email participant_flows.add_participant( reporter_email, incident, db_session, role=ParticipantRoleType.reporter, ) # add commander commander_email = commander_service_id = None if incident_in.commander: commander_email = incident_in.commander.individual.email else: commander_email, commander_service_id = resolve_and_associate_role( db_session=db_session, incident=incident, role=ParticipantRoleType.incident_commander ) if not commander_email: # we make the reporter the commander if an email for the commander # was not provided or resolved via incident role policies commander_email = reporter_email participant_flows.add_participant( commander_email, incident, db_session, service_id=commander_service_id, role=ParticipantRoleType.incident_commander, ) # add liaison liaison_email, liaison_service_id = resolve_and_associate_role( db_session=db_session, incident=incident, role=ParticipantRoleType.liaison ) if liaison_email: # we only add the liaison if we are able to resolve its email # via incident role policies participant_flows.add_participant( liaison_email, incident, db_session, service_id=liaison_service_id, role=ParticipantRoleType.liaison, ) # add scribe scribe_email, scribe_service_id = resolve_and_associate_role( db_session=db_session, incident=incident, role=ParticipantRoleType.scribe ) if scribe_email: # we only add the scribe if we are able to resolve its email # via incident role policies participant_flows.add_participant( scribe_email, incident, db_session, service_id=scribe_service_id, role=ParticipantRoleType.scribe, ) return incident