Skip to content

Commit

Permalink
Merge branch 'enhancement/direct-link-to-timeline' of https://github.…
Browse files Browse the repository at this point in the history
…com/Netflix/dispatch into enhancement/direct-link-to-timeline
  • Loading branch information
whitdog47 committed Oct 5, 2023
2 parents fc43e5f + 0616ddb commit 139a6ce
Show file tree
Hide file tree
Showing 24 changed files with 582 additions and 134 deletions.
3 changes: 2 additions & 1 deletion requirements-base.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ atlassian-python-api==3.32.0
attrs==22.1.0
bcrypt
blockkit
boto3
cachetools
chardet
click
Expand Down Expand Up @@ -44,8 +45,8 @@ schemathesis
sentry-asgi
sentry-sdk
sh
slack-bolt
slack_sdk
slack-bolt
slowapi
spacy
sqlalchemy-filters
Expand Down
2 changes: 1 addition & 1 deletion requirements-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ rsa==4.9
# google-auth
# oauth2client
# python-jose
schedule==1.2.0
schedule==1.2.1
# via -r requirements-base.in
schemathesis==3.19.7
# via -r requirements-base.in
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ identify==2.5.27
# via pre-commit
iniconfig==2.0.0
# via pytest
ipython==8.16.0
ipython==8.16.1
# via -r requirements-dev.in
jedi==0.19.0
# via ipython
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ def run(self):
"dispatch.plugins": [
"dispatch_atlassian_confluence = dispatch.plugins.dispatch_atlassian_confluence.plugin:ConfluencePagePlugin",
"dispatch_atlassian_confluence_document = dispatch.plugins.dispatch_atlassian_confluence.docs.plugin:ConfluencePageDocPlugin",
"dispatch_aws_sqs = dispatch.plugins.dispatch_aws.plugin:AWSSQSSignalConsumerPlugin",
"dispatch_basic_auth = dispatch.plugins.dispatch_core.plugin:BasicAuthProviderPlugin",
"dispatch_contact = dispatch.plugins.dispatch_core.plugin:DispatchContactPlugin",
"dispatch_document_resolver = dispatch.plugins.dispatch_core.plugin:DispatchDocumentResolverPlugin",
Expand Down
36 changes: 20 additions & 16 deletions src/dispatch/case/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def case_new_create_flow(
conversation_target: str = None,
service_id: int = None,
db_session: Session,
create_resources: bool = True,
create_all_resources: bool = True,
):
"""Runs the case new creation flow."""
# we get the case
Expand All @@ -186,18 +186,21 @@ def case_new_create_flow(
# we create the ticket
ticket_flows.create_case_ticket(case=case, db_session=db_session)

# we resolve participants
individual_participants, team_participants = get_case_participants(
case=case, db_session=db_session
)

if create_resources:
case_create_resources_flow(
db_session=db_session,
case_id=case.id,
individual_participants=individual_participants,
team_participants=team_participants,
conversation_target=conversation_target,
)
# NOTE: we create all external resources for a Case unless it's
# created from a Signal, as it gets expensive when we have lots of them.
case_create_resources_flow(
db_session=db_session,
case_id=case.id,
individual_participants=individual_participants,
team_participants=team_participants,
conversation_target=conversation_target,
create_all_resources=create_all_resources,
)

if case.case_priority.page_assignee:
if not service_id:
Expand Down Expand Up @@ -341,8 +344,9 @@ def case_update_flow(
db_session=db_session,
)

# we send the case updated notification
update_conversation(case, db_session)
if case.conversation:
# we send the case updated notification
update_conversation(case, db_session)


def case_delete_flow(case: Case, db_session: SessionLocal):
Expand Down Expand Up @@ -608,15 +612,15 @@ def case_create_resources_flow(
individual_participants: List[str],
team_participants: List[str],
conversation_target: str = None,
create_resources: bool = True,
create_all_resources: bool = True,
) -> None:
"""Runs the case resource creation flow."""
case = get(db_session=db_session, case_id=case_id)

if case.assignee:
individual_participants.append((case.assignee.individual, None))

if create_resources:
if create_all_resources:
# we create the tactical group
direct_participant_emails = [i.email for i, _ in individual_participants]

Expand Down Expand Up @@ -654,9 +658,6 @@ def case_create_resources_flow(
db_session=db_session,
)

# we update the ticket
ticket_flows.update_case_ticket(case=case, db_session=db_session)

# we update the case document
document_flows.update_document(
document=case.case_document, project_id=case.project.id, db_session=db_session
Expand Down Expand Up @@ -706,3 +707,6 @@ def case_create_resources_flow(
case_id=case.id,
)
log.exception(e)

# we update the ticket
ticket_flows.update_case_ticket(case=case, db_session=db_session)
87 changes: 87 additions & 0 deletions src/dispatch/case/messaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
.. module: dispatch.case.messaging
:platform: Unix
:copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
"""
import logging

from dispatch.database.core import SessionLocal
from dispatch.case.models import Case
from dispatch.messaging.strings import (
CASE_CLOSE_REMINDER,
CASE_TRIAGE_REMINDER,
MessageType,
)
from dispatch.plugin import service as plugin_service


log = logging.getLogger(__name__)


def send_case_close_reminder(case: Case, db_session: SessionLocal):
"""
Sends a direct message to the assignee reminding them to close the case if possible.
"""
message_text = "Case Close Reminder"
message_template = CASE_CLOSE_REMINDER

plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=case.project.id, plugin_type="conversation"
)
if not plugin:
log.warning("Case close reminder message not sent. No conversation plugin enabled.")
return

items = [
{
"name": case.name,
"ticket_weblink": case.ticket.weblink,
"title": case.title,
"status": case.status,
}
]

plugin.instance.send_direct(
case.assignee.individual.email,
message_text,
message_template,
MessageType.case_status_reminder,
items=items,
)

log.debug(f"Case close reminder sent to {case.assignee.individual.email}.")


def send_case_triage_reminder(case: Case, db_session: SessionLocal):
"""
Sends a direct message to the assignee reminding them to triage the case if possible.
"""
message_text = "Case Triage Reminder"
message_template = CASE_TRIAGE_REMINDER

plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=case.project.id, plugin_type="conversation"
)
if not plugin:
log.warning("Case triage reminder message not sent. No conversation plugin enabled.")
return

items = [
{
"name": case.name,
"ticket_weblink": case.ticket.weblink,
"title": case.title,
"status": case.status,
}
]

plugin.instance.send_direct(
case.assignee.individual.email,
message_text,
message_template,
MessageType.case_status_reminder,
items=items,
)

log.debug(f"Case triage reminder sent to {case.assignee.individual.email}.")
47 changes: 47 additions & 0 deletions src/dispatch/case/scheduled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from datetime import datetime, date
from schedule import every

from dispatch.database.core import SessionLocal
from dispatch.decorators import scheduled_project_task, timer
from dispatch.project.models import Project
from dispatch.scheduler import scheduler

from .enums import CaseStatus
from .messaging import send_case_close_reminder, send_case_triage_reminder
from .service import (
get_all_by_status,
)


@scheduler.add(every(1).day.at("18:00"), name="case-close-reminder")
@timer
@scheduled_project_task
def case_close_reminder(db_session: SessionLocal, project: Project):
"""Sends a reminder to the case assignee to close out their case."""
cases = get_all_by_status(
db_session=db_session, project_id=project.id, status=CaseStatus.triage
)

for case in cases:
span = datetime.utcnow() - case.triage_at
q, r = divmod(span.days, 7)
if q >= 1 and date.today().isoweekday() == 1:
# we only send the reminder for cases that have been triaging
# longer than a week and only on Mondays
send_case_close_reminder(case, db_session)


@scheduler.add(every(1).day.at("18:00"), name="case-triage-reminder")
@timer
@scheduled_project_task
def case_triage_reminder(db_session: SessionLocal, project: Project):
"""Sends a reminder to the case assignee to triage their case."""
cases = get_all_by_status(db_session=db_session, project_id=project.id, status=CaseStatus.new)

# if we want more specific SLA reminders, we would need to add additional data model
for case in cases:
span = datetime.utcnow() - case.created_at
q, r = divmod(span.days, 1)
if q >= 1:
# we only send one reminder per case per day
send_case_triage_reminder(case, db_session)
2 changes: 1 addition & 1 deletion src/dispatch/case/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def update(*, db_session, case: Case, case_in: CaseUpdate, current_user: Dispatc
db_session=db_session,
source="Dispatch Core App",
description=(
f"Case status changed to {case_in.status.lower()} " f"by {current_user.email}"
f"Case status changed to {case_in.status.lower()} by {current_user.email}"
),
dispatch_user_id=current_user.id,
case_id=case.id,
Expand Down
Loading

0 comments on commit 139a6ce

Please sign in to comment.