Merge pull request #412 from dod-ccpo/status-change-emails

Notify PSO or MO of request status change
This commit is contained in:
richard-dds
2018-11-01 10:13:39 -04:00
committed by GitHub
14 changed files with 198 additions and 66 deletions

View File

@@ -29,3 +29,7 @@ class Authorization(object):
@classmethod
def can_view_audit_log(cls, user):
return Authorization.has_atat_permission(user, Permissions.VIEW_AUDIT_LOG)
@classmethod
def is_ccpo(cls, user):
return user.atat_role.name == "ccpo"

View File

@@ -6,9 +6,11 @@ from atst.models.request_status_event import RequestStatusEvent, RequestStatus
from atst.models.request_review import RequestReview
from atst.models.request_internal_comment import RequestInternalComment
from atst.utils import deep_merge
from atst.queue import queue
from .query import RequestsQuery
from .authorization import RequestsAuthorization
from .status_event_handler import RequestStatusEventHandler
def create_revision_from_request_body(body):
@@ -95,11 +97,17 @@ class Requests(object):
@classmethod
def set_status(cls, request, status: RequestStatus):
old_status = request.status
status_event = RequestStatusEvent(
new_status=status, revision=request.latest_revision
)
request.status_events.append(status_event)
return request
updated_request = RequestsQuery.add_and_commit(request)
RequestStatusEventHandler(queue).handle_status_change(
updated_request, old_status, status
)
return updated_request
@classmethod
def should_auto_approve(cls, request):

View File

@@ -0,0 +1,35 @@
from flask import render_template
from atst.models.request_status_event import RequestStatus
class RequestStatusEventHandler(object):
STATUS_TRANSITIONS = set(
[
(
RequestStatus.PENDING_CCPO_ACCEPTANCE,
RequestStatus.PENDING_FINANCIAL_VERIFICATION,
),
(RequestStatus.PENDING_CCPO_ACCEPTANCE, RequestStatus.CHANGES_REQUESTED),
(
RequestStatus.PENDING_CCPO_APPROVAL,
RequestStatus.CHANGES_REQUESTED_TO_FINVER,
),
(RequestStatus.PENDING_CCPO_APPROVAL, RequestStatus.APPROVED),
]
)
def __init__(self, queue):
self.queue = queue
def handle_status_change(self, request, old_status, new_status):
if (old_status, new_status) in self.STATUS_TRANSITIONS:
self._send_email(request)
def _send_email(self, request):
email_body = render_template(
"emails/request_status_change.txt", request=request
)
self.queue.send_mail(
[request.creator.email], "Your JEDI request status has changed", email_body
)

View File

@@ -119,11 +119,11 @@ class Request(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
@property
def latest_status(self):
return self.status_events[-1]
return self.status_events[-1] if self.status_events else None
@property
def status(self):
return self.latest_status.new_status
return self.latest_status.new_status if self.latest_status else None
@property
def status_displayname(self):

View File

@@ -27,8 +27,8 @@ class ATSTQueue(RQ):
# pylint: disable=pointless-string-statement
"""Instance methods to queue up application-specific jobs."""
def send_mail(self, to, subject, body):
self._queue_job(ATSTQueue._send_mail, to, subject, body)
def send_mail(self, recipients, subject, body):
self._queue_job(ATSTQueue._send_mail, recipients, subject, body)
# pylint: disable=pointless-string-statement
"""Class methods to actually perform the work.
@@ -38,8 +38,8 @@ class ATSTQueue(RQ):
"""
@classmethod
def _send_mail(self, to, subject, body):
app.mailer.send(to, subject, body)
def _send_mail(self, recipients, subject, body):
app.mailer.send(recipients, subject, body)
queue = ATSTQueue()

View File

@@ -55,25 +55,6 @@ class RequestsIndex(object):
"extended_view": False,
}
def _edit_link_for_request(self, viewing_role, request):
if request.creator == g.current_user:
if request.is_pending_financial_verification:
return url_for("requests.financial_verification", request_id=request.id)
elif request.is_pending_financial_verification_changes:
return url_for(
"requests.financial_verification",
request_id=request.id,
extended=True,
)
elif request.is_approved:
return url_for("requests.view_request_details", request_id=request.id)
else:
return url_for(
"requests.requests_form_update", screen=1, request_id=request.id
)
elif viewing_role == "ccpo":
return url_for("requests.approval", request_id=request.id)
def _map_request(self, request, viewing_role):
time_created = pendulum.instance(request.time_created)
is_new = time_created.add(days=1) > pendulum.now()
@@ -92,7 +73,7 @@ class RequestsIndex(object):
"last_edited_timestamp": request.latest_revision.time_updated,
"full_name": request.creator.full_name,
"annual_usage": annual_usage,
"edit_link": self._edit_link_for_request(viewing_role, request),
"edit_link": url_for("requests.edit", request_id=request.id),
"action_required": request.action_required_by == viewing_role,
"dod_component": request.latest_revision.dod_component,
}

View File

@@ -2,6 +2,7 @@ from flask import g, redirect, render_template, url_for, request as http_request
from . import requests_bp
from atst.domain.requests import Requests
from atst.domain.authz import Authorization
from atst.routes.requests.jedi_request_flow import JEDIRequestFlow
from atst.models.request_status_event import RequestStatus
from atst.forms.data import (
@@ -144,3 +145,34 @@ def view_request_details(request_id=None):
jedi_request=request,
requires_fv_action=requires_fv_action,
)
@requests_bp.route("/requests/edit/<string:request_id>")
def edit(request_id):
user = g.current_user
request = Requests.get(user, request_id)
is_ccpo = Authorization.is_ccpo(user)
redirect_url = ""
if request.creator == user:
if request.is_pending_financial_verification:
redirect_url = url_for(
"requests.financial_verification", request_id=request.id
)
elif request.is_pending_financial_verification_changes:
redirect_url = url_for(
"requests.financial_verification", request_id=request.id, extended=True
)
elif request.is_approved:
redirect_url = url_for(
"requests.view_request_details", request_id=request.id
)
else:
redirect_url = url_for(
"requests.requests_form_update", screen=1, request_id=request.id
)
elif is_ccpo:
redirect_url = url_for("requests.approval", request_id=request.id)
return redirect(redirect_url)