Merge pull request #178 from dod-ccpo/kpi-159705475

Add counts for KPI (159705475)
This commit is contained in:
patricksmithdds 2018-08-15 12:04:28 -04:00 committed by GitHub
commit 46aa95248c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 19 deletions

View File

@ -1,4 +1,6 @@
from enum import Enum
from sqlalchemy import exists, and_, exc
from sqlalchemy.sql import text
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm.attributes import flag_modified
@ -154,3 +156,43 @@ class Requests(object):
@classmethod
def is_pending_ccpo_approval(cls, request):
return request.status == RequestStatus.PENDING_CCPO_APPROVAL
@classmethod
def status_count(cls, status, creator=None):
if isinstance(status, Enum):
status = status.name
bindings = {"status": status}
raw = """
SELECT count(requests_with_status.id)
FROM (
SELECT DISTINCT ON (rse.request_id) r.*, rse.new_status as status
FROM request_status_events rse JOIN requests r ON r.id = rse.request_id
ORDER BY rse.request_id, rse.sequence DESC
) as requests_with_status
WHERE requests_with_status.status = :status
"""
if creator:
raw += " AND requests_with_status.user_id = :user_id"
bindings["user_id"] = creator.id
results = db.session.execute(text(raw), bindings).fetchone()
(count,) = results
return count
@classmethod
def in_progress_count(cls):
return sum([
Requests.status_count(RequestStatus.STARTED),
Requests.status_count(RequestStatus.PENDING_FINANCIAL_VERIFICATION),
Requests.status_count(RequestStatus.CHANGES_REQUESTED),
])
@classmethod
def pending_ccpo_count(cls):
return Requests.status_count(RequestStatus.PENDING_CCPO_APPROVAL)
@classmethod
def completed_count(cls):
return Requests.status_count(RequestStatus.APPROVED)

View File

@ -12,9 +12,10 @@ class RequestStatus(Enum):
STARTED = "Started"
PENDING_FINANCIAL_VERIFICATION = "Pending Financial Verification"
PENDING_CCPO_APPROVAL = "Pending CCPO Approval"
CHANGES_REQUESTED = "Changes Requested"
APPROVED = "Approved"
EXPIRED = "Expired"
DELETED = "Deleted"
CANCELED = "Canceled"
class RequestStatusEvent(Base):

View File

@ -32,22 +32,40 @@ def map_request(request):
@requests_bp.route("/requests", methods=["GET"])
def requests_index():
requests = []
is_ccpo = Permissions.REVIEW_AND_APPROVE_JEDI_WORKSPACE_REQUEST in g.current_user.atat_permissions
if is_ccpo:
requests = Requests.get_many()
else:
requests = Requests.get_many(creator=g.current_user)
if Permissions.REVIEW_AND_APPROVE_JEDI_WORKSPACE_REQUEST in g.current_user.atat_permissions:
return _ccpo_view()
else:
return _non_ccpo_view()
def _ccpo_view():
requests = Requests.get_many()
mapped_requests = [map_request(r) for r in requests]
pending_fv = not is_ccpo and any(Requests.is_pending_financial_verification(r) for r in requests)
pending_ccpo = not is_ccpo and any(Requests.is_pending_ccpo_approval(r) for r in requests)
return render_template(
"requests.html",
requests=mapped_requests,
pending_financial_verification=False,
pending_ccpo_approval=False,
extended_view=True,
kpi_inprogress=Requests.in_progress_count(),
kpi_pending=Requests.pending_ccpo_count(),
kpi_completed=Requests.completed_count(),
)
def _non_ccpo_view():
requests = Requests.get_many(creator=g.current_user)
mapped_requests = [map_request(r) for r in requests]
pending_fv = any(Requests.is_pending_financial_verification(r) for r in requests)
pending_ccpo = any(Requests.is_pending_ccpo_approval(r) for r in requests)
return render_template(
"requests.html",
requests=mapped_requests,
pending_financial_verification=pending_fv,
pending_ccpo_approval=pending_ccpo,
extended_view=is_ccpo
extended_view=False,
)

View File

@ -51,16 +51,16 @@
{% if extended_view %}
<div class="row kpi">
<div class="kpi__item col col--grow">
<div class="kpi__item__value">3</div>
<div class="kpi__item__description">Pending Requests</div>
<div class="kpi__item__value">{{ kpi_inprogress }}</div>
<div class="kpi__item__description">In Progress</div>
</div>
<div class="kpi__item col col--grow">
<div class="kpi__item__value">2,456</div>
<div class="kpi__item__description">Completed Requests This Year</div>
<div class="kpi__item__value">{{ kpi_pending }}</div>
<div class="kpi__item__description">Pending CCPO Action</div>
</div>
<div class="kpi__item col col--grow">
<div class="kpi__item__value">234</div>
<div class="kpi__item__description">Denied Requests</div>
<div class="kpi__item__value">{{ kpi_completed }}</div>
<div class="kpi__item__description">Completed (Overall)</div>
</div>
</div>
{% endif %}

View File

@ -69,6 +69,7 @@ def session(db, request):
]
for factory in factory_list:
factory._meta.sqlalchemy_session = session
factory._meta.sqlalchemy_session_persistence = "commit"
yield session

View File

@ -3,9 +3,10 @@ from uuid import uuid4
from atst.domain.exceptions import NotFoundError
from atst.domain.requests import Requests
from atst.models.request import Request
from atst.models.request_status_event import RequestStatus
from tests.factories import RequestFactory, UserFactory
from tests.factories import RequestFactory, UserFactory, RequestStatusEventFactory
@pytest.fixture(scope="function")
@ -63,3 +64,27 @@ def test_exists(session):
request = RequestFactory.create(creator=user_allowed)
assert Requests.exists(request.id, user_allowed)
assert not Requests.exists(request.id, user_denied)
def test_status_count(session):
# make sure table is empty
session.query(Request).delete()
request1 = RequestFactory.create()
request2 = RequestFactory.create()
RequestStatusEventFactory.create(sequence=2, request_id=request2.id, new_status=RequestStatus.PENDING_FINANCIAL_VERIFICATION)
assert Requests.status_count(RequestStatus.PENDING_FINANCIAL_VERIFICATION) == 1
assert Requests.status_count(RequestStatus.STARTED) == 1
assert Requests.in_progress_count() == 2
def test_status_count_scoped_to_creator(session):
# make sure table is empty
session.query(Request).delete()
user = UserFactory.create()
request1 = RequestFactory.create()
request2 = RequestFactory.create(creator=user)
assert Requests.status_count(RequestStatus.STARTED) == 2
assert Requests.status_count(RequestStatus.STARTED, creator=user) == 1

View File

@ -39,6 +39,7 @@ class RequestStatusEventFactory(factory.alchemy.SQLAlchemyModelFactory):
model = RequestStatusEvent
id = factory.Sequence(lambda x: uuid4())
sequence = 1
class RequestFactory(factory.alchemy.SQLAlchemyModelFactory):

View File

@ -65,6 +65,6 @@ def test_request_status_pending_expired_displayname():
def test_request_status_pending_deleted_displayname():
request = RequestFactory.create()
request = Requests.set_status(request, RequestStatus.DELETED)
request = Requests.set_status(request, RequestStatus.CANCELED)
assert request.status_displayname == "Deleted"
assert request.status_displayname == "Canceled"