Update atst to atat
This commit is contained in:
166
atat/models/task_order.py
Normal file
166
atat/models/task_order.py
Normal file
@@ -0,0 +1,166 @@
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, String
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.orm import relationship
|
||||
from atat.models.clin import CLIN
|
||||
from atat.models.base import Base
|
||||
import atat.models.types as types
|
||||
import atat.models.mixins as mixins
|
||||
from atat.models.attachment import Attachment
|
||||
from pendulum import today
|
||||
from sqlalchemy import func
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
DRAFT = "Draft"
|
||||
ACTIVE = "Active"
|
||||
UPCOMING = "Upcoming"
|
||||
EXPIRED = "Expired"
|
||||
UNSIGNED = "Unsigned"
|
||||
|
||||
|
||||
SORT_ORDERING = [
|
||||
Status.ACTIVE,
|
||||
Status.DRAFT,
|
||||
Status.UPCOMING,
|
||||
Status.EXPIRED,
|
||||
]
|
||||
|
||||
|
||||
class TaskOrder(Base, mixins.TimestampsMixin):
|
||||
__tablename__ = "task_orders"
|
||||
|
||||
id = types.Id()
|
||||
|
||||
portfolio_id = Column(ForeignKey("portfolios.id"), nullable=False)
|
||||
portfolio = relationship("Portfolio")
|
||||
|
||||
pdf_attachment_id = Column(ForeignKey("attachments.id"))
|
||||
_pdf = relationship("Attachment", foreign_keys=[pdf_attachment_id])
|
||||
pdf_last_sent_at = Column(DateTime)
|
||||
number = Column(String, unique=True,) # Task Order Number
|
||||
signer_dod_id = Column(String)
|
||||
signed_at = Column(DateTime)
|
||||
clins = relationship(
|
||||
"CLIN",
|
||||
back_populates="task_order",
|
||||
cascade="all, delete-orphan",
|
||||
order_by=lambda: [func.substr(CLIN.number, 2), func.substr(CLIN.number, 1, 2)],
|
||||
)
|
||||
|
||||
@hybrid_property
|
||||
def pdf(self):
|
||||
return self._pdf
|
||||
|
||||
@pdf.setter
|
||||
def pdf(self, new_pdf):
|
||||
self._pdf = self._set_attachment(new_pdf, "_pdf")
|
||||
|
||||
def _set_attachment(self, new_attachment, attribute):
|
||||
if isinstance(new_attachment, Attachment):
|
||||
return new_attachment
|
||||
elif isinstance(new_attachment, dict):
|
||||
if new_attachment["filename"] and new_attachment["object_name"]:
|
||||
attachment = Attachment.get_or_create(
|
||||
new_attachment["object_name"], new_attachment
|
||||
)
|
||||
return attachment
|
||||
else:
|
||||
return None
|
||||
elif not new_attachment and hasattr(self, attribute):
|
||||
return None
|
||||
else:
|
||||
raise TypeError("Could not set attachment with invalid type")
|
||||
|
||||
@property
|
||||
def is_draft(self):
|
||||
return self.status == Status.DRAFT or self.status == Status.UNSIGNED
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.status == Status.ACTIVE
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
return self.status == Status.EXPIRED
|
||||
|
||||
@property
|
||||
def is_upcoming(self):
|
||||
return self.status == Status.UPCOMING
|
||||
|
||||
@property
|
||||
def clins_are_completed(self):
|
||||
return all([len(self.clins), (clin.is_completed for clin in self.clins)])
|
||||
|
||||
@property
|
||||
def is_completed(self):
|
||||
return all([self.pdf, self.number, self.clins_are_completed])
|
||||
|
||||
@property
|
||||
def is_signed(self):
|
||||
return self.signed_at is not None
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
todays_date = today(tz="UTC").date()
|
||||
|
||||
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 todays_date < self.start_date:
|
||||
return Status.UPCOMING
|
||||
elif todays_date > self.end_date:
|
||||
return Status.EXPIRED
|
||||
elif self.start_date <= todays_date <= self.end_date:
|
||||
return Status.ACTIVE
|
||||
|
||||
@property
|
||||
def start_date(self):
|
||||
return min((c.start_date for c in self.clins), default=None)
|
||||
|
||||
@property
|
||||
def end_date(self):
|
||||
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 - today(tz="UTC").date()).days
|
||||
|
||||
@property
|
||||
def total_obligated_funds(self):
|
||||
return sum(
|
||||
(clin.obligated_amount for clin in self.clins if clin.obligated_amount)
|
||||
)
|
||||
|
||||
@property
|
||||
def total_contract_amount(self):
|
||||
return sum((clin.total_amount for clin in self.clins if clin.total_amount))
|
||||
|
||||
@property
|
||||
def display_status(self):
|
||||
if self.status == Status.UNSIGNED:
|
||||
return Status.DRAFT.value
|
||||
else:
|
||||
return self.status.value
|
||||
|
||||
@property
|
||||
def portfolio_name(self):
|
||||
return self.portfolio.name
|
||||
|
||||
def to_dictionary(self):
|
||||
return {
|
||||
"portfolio_name": self.portfolio_name,
|
||||
"pdf": self.pdf,
|
||||
"clins": [clin.to_dictionary() for clin in self.clins],
|
||||
**{
|
||||
c.name: getattr(self, c.name)
|
||||
for c in self.__table__.columns
|
||||
if c.name not in ["id"]
|
||||
},
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return "<TaskOrder(number='{}', id='{}')>".format(self.number, self.id)
|
Reference in New Issue
Block a user