예제 #1
0
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,
        )
예제 #2
0
파일: flows.py 프로젝트: z1nkum/dispatch
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,
    )
예제 #3
0
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,
    )
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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()
예제 #8
0
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
예제 #9
0
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,
    )
예제 #10
0
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
예제 #11
0
파일: flows.py 프로젝트: kylie-sre/dispatch
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
예제 #12
0
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,
        )
예제 #13
0
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()
예제 #14
0
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)
예제 #15
0
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
예제 #16
0
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,
        )
예제 #17
0
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
예제 #18
0
파일: flows.py 프로젝트: Netflix/dispatch
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"
예제 #19
0
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)
예제 #20
0
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,
    )
예제 #21
0
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,
    )
예제 #22
0
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
예제 #23
0
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)
예제 #24
0
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"
예제 #25
0
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
예제 #26
0
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
예제 #27
0
파일: service.py 프로젝트: reddit/dispatch
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
예제 #28
0
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