Merge pull request #178 from dod-ccpo/kpi-159705475
Add counts for KPI (159705475)
This commit is contained in:
commit
46aa95248c
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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 %}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -39,6 +39,7 @@ class RequestStatusEventFactory(factory.alchemy.SQLAlchemyModelFactory):
|
||||
model = RequestStatusEvent
|
||||
|
||||
id = factory.Sequence(lambda x: uuid4())
|
||||
sequence = 1
|
||||
|
||||
|
||||
class RequestFactory(factory.alchemy.SQLAlchemyModelFactory):
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user