Merge pull request #884 from dod-ccpo/to-funding-statuses

Funding page Task Order statuses
This commit is contained in:
richard-dds
2019-06-10 15:40:25 -04:00
committed by GitHub
14 changed files with 280 additions and 92 deletions

View File

@@ -2,7 +2,7 @@ from flask import current_app as app
from atst.database import db
from atst.models.clin import CLIN
from atst.models.task_order import TaskOrder
from atst.models.task_order import TaskOrder, SORT_ORDERING
from . import BaseDomainClass
@@ -98,3 +98,10 @@ class TaskOrders(BaseDomainClass):
if not app.config.get("CLASSIFIED"):
section_list["funding"] = TaskOrders.UNCLASSIFIED_FUNDING
return section_list
@classmethod
def sort(cls, task_orders: [TaskOrder]) -> [TaskOrder]:
# Sorts a list of task orders on two keys: status (primary) and time_created (secondary)
by_time_created = sorted(task_orders, key=lambda to: to.time_created)
by_status = sorted(by_time_created, key=lambda to: SORT_ORDERING.get(to.status))
return by_status

View File

@@ -1,5 +1,4 @@
from enum import Enum
from datetime import date
from sqlalchemy import Column, DateTime, ForeignKey, String
from sqlalchemy.ext.hybrid import hybrid_property
@@ -8,13 +7,23 @@ from werkzeug.datastructures import FileStorage
from atst.models import Attachment, Base, mixins, types
from atst.models.clin import JEDICLINType
from atst.utils.clock import Clock
class Status(Enum):
STARTED = "Started"
PENDING = "Pending"
DRAFT = "Draft"
ACTIVE = "Active"
UPCOMING = "Upcoming"
EXPIRED = "Expired"
UNSIGNED = "Not signed"
SORT_ORDERING = {
status: order
for (order, status) in enumerate(
[Status.DRAFT, Status.ACTIVE, Status.UPCOMING, Status.EXPIRED, Status.UNSIGNED]
)
}
class TaskOrder(Base, mixins.TimestampsMixin):
@@ -62,28 +71,41 @@ class TaskOrder(Base, mixins.TimestampsMixin):
def is_expired(self):
return self.status == Status.EXPIRED
@property
def is_completed(self):
return all([self.pdf, self.number, len(self.clins)])
@property
def is_signed(self):
return self.signed_at is not None
@property
def status(self):
# TODO: fix task order -- implement correctly using CLINs
# Faked for display purposes
return Status.ACTIVE
today = Clock.today()
if not self.is_completed and not self.is_signed:
return Status.DRAFT
elif self.is_completed and not self.is_signed:
return Status.UNSIGNED
elif today < self.start_date:
return Status.UPCOMING
elif today >= self.end_date:
return Status.EXPIRED
elif self.start_date <= today < self.end_date:
return Status.ACTIVE
@property
def start_date(self):
# TODO: fix task order -- reimplement using CLINs
# Faked for display purposes
return date.today()
return min((c.start_date for c in self.clins), default=self.time_created.date())
@property
def end_date(self):
# TODO: fix task order -- reimplement using CLINs
# Faked for display purposes
return date.today()
return max((c.end_date for c in self.clins), default=None)
@property
def days_to_expiration(self):
if self.end_date:
return (self.end_date - date.today()).days
return (self.end_date - Clock.today()).days
@property
def total_obligated_funds(self):

View File

@@ -1,13 +1,11 @@
from collections import defaultdict
from flask import g, render_template
from . import task_orders_bp
from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.domain.portfolios import Portfolios
from atst.domain.task_orders import TaskOrders
from atst.models.task_order import Status
from atst.models import Permissions
from atst.models.task_order import Status as TaskOrderStatus
@task_orders_bp.route("/task_orders/<task_order_id>")
@@ -34,19 +32,16 @@ def review_task_order(task_order_id):
@user_can(Permissions.VIEW_PORTFOLIO_FUNDING, message="view portfolio funding")
def portfolio_funding(portfolio_id):
portfolio = Portfolios.get(g.current_user, portfolio_id)
task_orders_by_status = defaultdict(list)
for task_order in portfolio.task_orders:
task_orders_by_status[task_order.status].append(task_order)
active_task_orders = task_orders_by_status.get(TaskOrderStatus.ACTIVE, [])
task_orders = TaskOrders.sort(portfolio.task_orders)
label_colors = {
Status.DRAFT: "warning",
Status.ACTIVE: "success",
Status.UPCOMING: "info",
Status.EXPIRED: "error",
Status.UNSIGNED: "purple",
}
return render_template(
"portfolios/task_orders/index.html",
pending_task_orders=(
task_orders_by_status.get(TaskOrderStatus.STARTED, [])
+ task_orders_by_status.get(TaskOrderStatus.PENDING, [])
),
active_task_orders=active_task_orders,
expired_task_orders=task_orders_by_status.get(TaskOrderStatus.EXPIRED, []),
task_orders=task_orders,
label_colors=label_colors,
)

11
atst/utils/clock.py Normal file
View File

@@ -0,0 +1,11 @@
import pendulum
class Clock(object):
@classmethod
def today(cls, tz="UTC"):
return pendulum.today(tz=tz).date()
@classmethod
def now(cls, tz="UTC"):
return pendulum.now(tz=tz)