Exemplo n.º 1
0
def job_closed_flow(job_id: int,
                    command: Optional[dict] = None,
                    db_session=None):
    """Runs the job closed flow."""
    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

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

    # we archive the conversation
    convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG)
    convo_plugin.archive(job.conversation.channel_id)

    # we update the external ticket
    update_external_job_ticket(job, db_session)

    if job.visibility == Visibility.open:
        if INCIDENT_STORAGE_RESTRICTED:
            # we unrestrict the storage
            unrestrict_job_storage(job, db_session)

        # we archive the artifacts in the storage
        archive_job_artifacts(job, db_session)

        # we delete the tactical and notification groups
        delete_participant_groups(job, db_session)

    # we delete the conference
    delete_conference(job, db_session)

    db_session.add(job)
    db_session.commit()
Exemplo n.º 2
0
def job_assign_role_flow(assigner_email: str,
                         job_id: int,
                         assignee_email: str,
                         assignee_role: str,
                         db_session=None):
    """Runs the job participant role assignment flow."""
    # we resolve the assigner and assignee's contact information
    contact_plugin = plugins.get(INCIDENT_PLUGIN_CONTACT_SLUG)
    assigner_contact_info = contact_plugin.get(assigner_email)
    assignee_contact_info = contact_plugin.get(assignee_email)

    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    # we get the participant object for the assignee
    assignee_participant = participant_service.get_by_job_id_and_email(
        db_session=db_session,
        job_id=job.id,
        code=assignee_contact_info["email"])

    if not assignee_participant:
        # The assignee is not a participant. We add them to the job
        job_add_or_reactivate_participant_flow(assignee_email,
                                               job.id,
                                               db_session=db_session)

    # we run the participant assign role flow
    result = participant_role_flows.assign_role_flow(job.id,
                                                     assignee_contact_info,
                                                     assignee_role, db_session)

    if result == "assignee_has_role":
        # NOTE: This is disabled until we can determine the source of the caller
        # we let the assigner know that the assignee already has this role
        # send_job_participant_has_role_ephemeral_message(
        #    assigner_email, assignee_contact_info, assignee_role, job
        # )
        return

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

    if assignee_role != ParticipantRoleType.participant:
        # we send a notification to the job conversation
        send_job_new_role_assigned_notification(assigner_contact_info,
                                                assignee_contact_info,
                                                assignee_role, job)

    if assignee_role == ParticipantRoleType.job_commander:
        # we update the conversation topic
        set_conversation_topic(job)

        # we update the external ticket
        update_external_job_ticket(job, db_session)
Exemplo n.º 3
0
def add_participant_to_conversation(participant_email: str, job_id: int,
                                    db_session: SessionLocal):
    """Adds a participant to the conversation."""
    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG)
    convo_plugin.add(job.conversation.channel_id, [participant_email])
Exemplo n.º 4
0
def job_active_flow(job_id: int,
                    command: Optional[dict] = None,
                    db_session=None):
    """Runs the job active flow."""
    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    # we remind the job commander to write a tactical report
    send_job_report_reminder(job, ReportTypes.tactical_report)

    # we update the status of the external ticket
    update_external_job_ticket(job, db_session)
Exemplo n.º 5
0
def job_remove_participant_flow(user_email: str,
                                job_id: int,
                                event: dict = None,
                                db_session=None):
    """Runs the remove participant flow."""
    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    if user_email == job.commander.code:
        # we add the job commander to the conversation again
        add_participant_to_conversation(user_email, job_id, db_session)

        # we send a notification to the channel
        send_job_commander_readded_notification(job_id, db_session)
    else:
        # we remove the participant from the job
        participant_flows.remove_participant(user_email, job_id, db_session)
Exemplo n.º 6
0
def log(
    db_session,
    source: str,
    description: str,
    job_id: int = None,
    worker_id: int = None,
    started_at: datetime = None,
    ended_at: datetime = None,
    details: dict = None,
) -> Event:
    """
    Logs an event
    """
    uuid = uuid4()

    if not started_at:
        started_at = datetime.datetime.utcnow()

    if not ended_at:
        ended_at = started_at

    event_in = EventCreate(
        uuid=uuid,
        started_at=started_at,
        ended_at=ended_at,
        source=source,
        description=description,
        details=details,
    )
    event = create(db_session=db_session, event_in=event_in)

    if job_id:
        job = job_service.get(db_session=db_session, job_id=job_id)
        job.events.append(event)
        db_session.add(job)

    if worker_id:
        worker = worker_service.get(db_session=db_session, worker_id=worker_id)
        worker.events.append(event)
        db_session.add(worker)

    db_session.commit()

    logger.info(f"{source}: {description}")

    return event
Exemplo n.º 7
0
def job_engage_oncall_flow(user_id: str,
                           user_email: str,
                           job_id: int,
                           action: dict,
                           db_session=None):
    """Runs the job engage oncall flow."""
    oncall_service_id = action["submission"]["oncall_service_id"]
    page = action["submission"]["page"]

    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    # we resolve the oncall service
    oncall_service = service_service.get_by_external_id(
        db_session=db_session, external_id=oncall_service_id)
    oncall_plugin = plugins.get(oncall_service.type)
    oncall_email = oncall_plugin.get(service_id=oncall_service_id)

    # we add the oncall to the job
    job_add_or_reactivate_participant_flow(oncall_email,
                                           job.id,
                                           db_session=db_session)

    event_service.log(
        db_session=db_session,
        source=oncall_plugin.job_code,
        description=
        f"{user_email} engages oncall service {oncall_service.name}",
        job_id=job.id,
    )

    if page == "Yes":
        # we page the oncall
        oncall_plugin.page(oncall_service_id, job.name, job.job_code,
                           job.description)

        event_service.log(
            db_session=db_session,
            source=oncall_plugin.job_code,
            description=f"{oncall_service.name} on-call paged",
            job_id=job.id,
        )
Exemplo n.º 8
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,
        )
Exemplo n.º 9
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()
Exemplo n.º 10
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)