Remove request-related models
This commit is contained in:
@@ -2,8 +2,6 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
from .request import Request
|
||||
from .request_status_event import RequestStatusEvent
|
||||
from .permissions import Permissions
|
||||
from .role import Role
|
||||
from .user import User
|
||||
@@ -14,9 +12,6 @@ from .portfolio import Portfolio
|
||||
from .application import Application
|
||||
from .environment import Environment
|
||||
from .attachment import Attachment
|
||||
from .request_revision import RequestRevision
|
||||
from .request_review import RequestReview
|
||||
from .request_internal_comment import RequestInternalComment
|
||||
from .audit_event import AuditEvent
|
||||
from .invitation import Invitation
|
||||
from .task_order import TaskOrder
|
||||
|
@@ -17,9 +17,6 @@ class AuditEvent(Base, TimestampsMixin):
|
||||
portfolio_id = Column(UUID(as_uuid=True), ForeignKey("portfolios.id"), index=True)
|
||||
portfolio = relationship("Portfolio", backref="audit_events")
|
||||
|
||||
request_id = Column(UUID(as_uuid=True), ForeignKey("requests.id"), index=True)
|
||||
request = relationship("Request", backref="audit_events")
|
||||
|
||||
changed_state = Column(JSONB())
|
||||
event_details = Column(JSONB())
|
||||
|
||||
|
@@ -14,7 +14,6 @@ class AuditableMixin(object):
|
||||
def create_audit_event(connection, resource, action):
|
||||
user_id = getattr_path(g, "current_user.id")
|
||||
portfolio_id = resource.portfolio_id
|
||||
request_id = resource.request_id
|
||||
resource_type = resource.resource_type
|
||||
display_name = resource.displayname
|
||||
event_details = resource.event_details
|
||||
@@ -24,7 +23,6 @@ class AuditableMixin(object):
|
||||
audit_event = AuditEvent(
|
||||
user_id=user_id,
|
||||
portfolio_id=portfolio_id,
|
||||
request_id=request_id,
|
||||
resource_type=resource_type,
|
||||
resource_id=resource.id,
|
||||
display_name=display_name,
|
||||
@@ -91,10 +89,6 @@ class AuditableMixin(object):
|
||||
def portfolio_id(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def request_id(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def displayname(self):
|
||||
return None
|
||||
|
@@ -13,7 +13,6 @@ class Portfolio(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
|
||||
id = types.Id()
|
||||
name = Column(String)
|
||||
request_id = Column(ForeignKey("requests.id"), nullable=True)
|
||||
applications = relationship("Application", back_populates="portfolio")
|
||||
roles = relationship("PortfolioRole")
|
||||
|
||||
@@ -35,10 +34,6 @@ class Portfolio(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
def user_count(self):
|
||||
return len(self.members)
|
||||
|
||||
@property
|
||||
def legacy_task_order(self):
|
||||
return self.request.legacy_task_order if self.request else None
|
||||
|
||||
@property
|
||||
def members(self):
|
||||
return (
|
||||
@@ -60,6 +55,6 @@ class Portfolio(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
return "<Portfolio(name='{}', request='{}', user_count='{}', id='{}')>".format(
|
||||
self.name, self.request_id, self.user_count, self.id
|
||||
return "<Portfolio(name='{}', user_count='{}', id='{}')>".format(
|
||||
self.name, self.user_count, self.id
|
||||
)
|
||||
|
@@ -1,256 +0,0 @@
|
||||
from sqlalchemy import Column, func, ForeignKey
|
||||
from sqlalchemy.types import DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from atst.models import Base, types, mixins
|
||||
from atst.models.request_status_event import RequestStatus
|
||||
from atst.utils import first_or_none
|
||||
from atst.models.request_revision import RequestRevision
|
||||
from atst.models.legacy_task_order import Source as TaskOrderSource
|
||||
|
||||
|
||||
def map_properties_to_dict(properties, instance):
|
||||
return {
|
||||
field: getattr(instance, field)
|
||||
for field in properties
|
||||
if getattr(instance, field) is not None
|
||||
}
|
||||
|
||||
|
||||
def update_dict_with_properties(instance, body, top_level_key, properties):
|
||||
new_properties = map_properties_to_dict(properties, instance)
|
||||
if new_properties:
|
||||
body[top_level_key] = new_properties
|
||||
|
||||
return body
|
||||
|
||||
|
||||
class Request(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
__tablename__ = "requests"
|
||||
|
||||
id = types.Id()
|
||||
time_created = Column(DateTime(timezone=True), server_default=func.now())
|
||||
status_events = relationship(
|
||||
"RequestStatusEvent", backref="request", order_by="RequestStatusEvent.sequence"
|
||||
)
|
||||
|
||||
portfolio = relationship("Portfolio", uselist=False, backref="request")
|
||||
|
||||
user_id = Column(ForeignKey("users.id"), nullable=False)
|
||||
creator = relationship("User", backref="owned_requests")
|
||||
|
||||
legacy_task_order_id = Column(ForeignKey("legacy_task_orders.id"))
|
||||
legacy_task_order = relationship("LegacyTaskOrder")
|
||||
|
||||
revisions = relationship(
|
||||
"RequestRevision", back_populates="request", order_by="RequestRevision.sequence"
|
||||
)
|
||||
|
||||
internal_comments = relationship("RequestInternalComment")
|
||||
|
||||
@property
|
||||
def latest_revision(self):
|
||||
if self.revisions:
|
||||
return self.revisions[-1]
|
||||
|
||||
else:
|
||||
return RequestRevision(request=self)
|
||||
|
||||
PRIMARY_POC_FIELDS = ["am_poc", "dodid_poc", "email_poc", "fname_poc", "lname_poc"]
|
||||
DETAILS_OF_USE_FIELDS = [
|
||||
"jedi_usage",
|
||||
"start_date",
|
||||
"cloud_native",
|
||||
"dollar_value",
|
||||
"dod_component",
|
||||
"data_transfers",
|
||||
"expected_completion_date",
|
||||
"jedi_migration",
|
||||
"num_software_systems",
|
||||
"number_user_sessions",
|
||||
"average_daily_traffic",
|
||||
"engineering_assessment",
|
||||
"technical_support_team",
|
||||
"estimated_monthly_spend",
|
||||
"average_daily_traffic_gb",
|
||||
"rationalization_software_systems",
|
||||
"organization_providing_assistance",
|
||||
"name",
|
||||
]
|
||||
INFORMATION_ABOUT_YOU_FIELDS = [
|
||||
"citizenship",
|
||||
"designation",
|
||||
"phone_number",
|
||||
"phone_ext",
|
||||
"email_request",
|
||||
"fname_request",
|
||||
"lname_request",
|
||||
"service_branch",
|
||||
"date_latest_training",
|
||||
]
|
||||
FINANCIAL_VERIFICATION_FIELDS = [
|
||||
"pe_id",
|
||||
"task_order_number",
|
||||
"fname_co",
|
||||
"lname_co",
|
||||
"email_co",
|
||||
"office_co",
|
||||
"fname_cor",
|
||||
"lname_cor",
|
||||
"email_cor",
|
||||
"office_cor",
|
||||
"uii_ids",
|
||||
"treasury_code",
|
||||
"ba_code",
|
||||
]
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
current = self.latest_revision
|
||||
body = {}
|
||||
for top_level_key, properties in [
|
||||
("primary_poc", Request.PRIMARY_POC_FIELDS),
|
||||
("details_of_use", Request.DETAILS_OF_USE_FIELDS),
|
||||
("information_about_you", Request.INFORMATION_ABOUT_YOU_FIELDS),
|
||||
("financial_verification", Request.FINANCIAL_VERIFICATION_FIELDS),
|
||||
]:
|
||||
body = update_dict_with_properties(current, body, top_level_key, properties)
|
||||
|
||||
return body
|
||||
|
||||
@property
|
||||
def latest_status(self):
|
||||
return self.status_events[-1] if self.status_events else None
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self.latest_status.new_status if self.latest_status else None
|
||||
|
||||
@property
|
||||
def status_displayname(self):
|
||||
return self.latest_status.displayname
|
||||
|
||||
@property
|
||||
def annual_spend(self):
|
||||
monthly = self.latest_revision.estimated_monthly_spend or 0
|
||||
return monthly * 12
|
||||
|
||||
@property
|
||||
def financial_verification(self):
|
||||
return self.body.get("financial_verification", {})
|
||||
|
||||
@property
|
||||
def is_financially_verified(self):
|
||||
if self.legacy_task_order:
|
||||
return self.legacy_task_order.verified
|
||||
return False
|
||||
|
||||
@property
|
||||
def last_submission_timestamp(self):
|
||||
def _is_submission(status_event):
|
||||
return status_event.new_status == RequestStatus.SUBMITTED
|
||||
|
||||
last_submission = first_or_none(_is_submission, reversed(self.status_events))
|
||||
if last_submission:
|
||||
return last_submission.time_created
|
||||
return None
|
||||
|
||||
@property
|
||||
def action_required_by(self):
|
||||
return {
|
||||
RequestStatus.PENDING_FINANCIAL_VERIFICATION: "mission_owner",
|
||||
RequestStatus.CHANGES_REQUESTED: "mission_owner",
|
||||
RequestStatus.CHANGES_REQUESTED_TO_FINVER: "mission_owner",
|
||||
RequestStatus.PENDING_CCPO_APPROVAL: "ccpo",
|
||||
RequestStatus.PENDING_CCPO_ACCEPTANCE: "ccpo",
|
||||
}.get(self.status)
|
||||
|
||||
@property
|
||||
def reviews(self):
|
||||
return [status.review for status in self.status_events if status.review]
|
||||
|
||||
@property
|
||||
def is_pending_financial_verification(self):
|
||||
return self.status == RequestStatus.PENDING_FINANCIAL_VERIFICATION
|
||||
|
||||
@property
|
||||
def is_pending_financial_verification_changes(self):
|
||||
return self.status == RequestStatus.CHANGES_REQUESTED_TO_FINVER
|
||||
|
||||
@property
|
||||
def is_pending_ccpo_acceptance(self):
|
||||
return self.status == RequestStatus.PENDING_CCPO_ACCEPTANCE
|
||||
|
||||
@property
|
||||
def is_pending_ccpo_approval(self):
|
||||
return self.status == RequestStatus.PENDING_CCPO_APPROVAL
|
||||
|
||||
@property
|
||||
def is_pending_ccpo_action(self):
|
||||
return self.is_pending_ccpo_acceptance or self.is_pending_ccpo_approval
|
||||
|
||||
@property
|
||||
def is_approved(self):
|
||||
return self.status == RequestStatus.APPROVED
|
||||
|
||||
@property
|
||||
def review_comment(self):
|
||||
if (
|
||||
self.status == RequestStatus.CHANGES_REQUESTED
|
||||
or self.status == RequestStatus.CHANGES_REQUESTED_TO_FINVER
|
||||
):
|
||||
review = self.latest_status.review
|
||||
if review: # pragma: no branch
|
||||
return review.comment
|
||||
|
||||
@property
|
||||
def has_financial_data(self):
|
||||
return (
|
||||
self.is_pending_ccpo_approval
|
||||
or self.is_pending_financial_verification_changes
|
||||
or self.is_approved
|
||||
) and self.legacy_task_order
|
||||
|
||||
@property
|
||||
def displayname(self):
|
||||
return self.latest_revision.name or self.id
|
||||
|
||||
@property
|
||||
def contracting_officer_full_name(self):
|
||||
if self.latest_revision.fname_co:
|
||||
return "{} {}".format(
|
||||
self.latest_revision.fname_co, self.latest_revision.lname_co
|
||||
)
|
||||
|
||||
@property
|
||||
def contracting_officer_email(self):
|
||||
return self.latest_revision.email_co
|
||||
|
||||
@property
|
||||
def pe_number(self):
|
||||
return self.body.get("financial_verification", {}).get("pe_id")
|
||||
|
||||
@property
|
||||
def has_manual_task_order(self):
|
||||
return (
|
||||
self.legacy_task_order.source == TaskOrderSource.MANUAL
|
||||
if self.legacy_task_order is not None
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def last_finver_draft_saved_at(self):
|
||||
if self.latest_revision.any_finver_fields_saved:
|
||||
return self.latest_revision.time_updated
|
||||
else:
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return "<Request(status='{}', name='{}', creator='{}', is_approved='{}', time_created='{}', id='{}')>".format(
|
||||
self.status_displayname,
|
||||
self.displayname,
|
||||
self.creator.full_name,
|
||||
self.is_approved,
|
||||
self.time_created,
|
||||
self.id,
|
||||
)
|
@@ -1,22 +0,0 @@
|
||||
from sqlalchemy import Column, String, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from atst.models import Base, types, mixins
|
||||
|
||||
|
||||
class RequestInternalComment(Base, mixins.TimestampsMixin):
|
||||
__tablename__ = "request_internal_comments"
|
||||
|
||||
id = types.Id()
|
||||
text = Column(String(), nullable=False)
|
||||
|
||||
user_id = Column(ForeignKey("users.id"), nullable=False)
|
||||
user = relationship("User")
|
||||
|
||||
request_id = Column(ForeignKey("requests.id", ondelete="CASCADE"), nullable=False)
|
||||
request = relationship("Request")
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return "<RequestInternalComment(text='{}', user='{}', request='{}', id='{}')>".format(
|
||||
self.text, self.user.full_name, self.request_id, self.id
|
||||
)
|
@@ -1,43 +0,0 @@
|
||||
from sqlalchemy import Column, String, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from atst.models import Base, mixins, types
|
||||
|
||||
|
||||
class RequestReview(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
__tablename__ = "request_reviews"
|
||||
|
||||
id = types.Id()
|
||||
status = relationship("RequestStatusEvent", uselist=False, back_populates="review")
|
||||
|
||||
user_id = Column(ForeignKey("users.id"))
|
||||
reviewer = relationship("User")
|
||||
|
||||
comment = Column(String)
|
||||
fname_mao = Column(String)
|
||||
lname_mao = Column(String)
|
||||
email_mao = Column(String)
|
||||
phone_mao = Column(String)
|
||||
phone_ext_mao = Column(String)
|
||||
fname_ccpo = Column(String)
|
||||
lname_ccpo = Column(String)
|
||||
|
||||
@property
|
||||
def full_name_reviewer(self):
|
||||
if self.reviewer:
|
||||
return self.reviewer.full_name
|
||||
else:
|
||||
return "System"
|
||||
|
||||
@property
|
||||
def full_name_mao(self):
|
||||
return "{} {}".format(self.fname_mao, self.lname_mao)
|
||||
|
||||
@property
|
||||
def full_name_ccpo(self):
|
||||
return "{} {}".format(self.fname_ccpo, self.lname_ccpo)
|
||||
|
||||
def __repr__(self):
|
||||
return "<RequestReview(status='{}', comment='{}', reviewer='{}', id='{}')>".format(
|
||||
self.status.log_name, self.comment, self.full_name_reviewer, self.id
|
||||
)
|
@@ -1,106 +0,0 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
ForeignKey,
|
||||
String,
|
||||
Boolean,
|
||||
Integer,
|
||||
Date,
|
||||
BigInteger,
|
||||
Sequence,
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import ARRAY
|
||||
|
||||
from atst.models import Base
|
||||
from atst.models import mixins
|
||||
from atst.models.types import Id
|
||||
|
||||
|
||||
class RequestRevision(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
__tablename__ = "request_revisions"
|
||||
|
||||
id = Id()
|
||||
request_id = Column(ForeignKey("requests.id"), nullable=False)
|
||||
request = relationship("Request", back_populates="revisions")
|
||||
sequence = Column(
|
||||
BigInteger, Sequence("request_revisions_sequence_seq"), nullable=False
|
||||
)
|
||||
|
||||
# primary_poc
|
||||
am_poc = Column(Boolean)
|
||||
dodid_poc = Column(String)
|
||||
email_poc = Column(String)
|
||||
fname_poc = Column(String)
|
||||
lname_poc = Column(String)
|
||||
|
||||
# details_of_use
|
||||
jedi_usage = Column(String)
|
||||
start_date = Column(Date)
|
||||
cloud_native = Column(String)
|
||||
dollar_value = Column(Integer)
|
||||
dod_component = Column(String)
|
||||
data_transfers = Column(String)
|
||||
expected_completion_date = Column(String)
|
||||
jedi_migration = Column(String)
|
||||
num_software_systems = Column(Integer)
|
||||
number_user_sessions = Column(Integer)
|
||||
average_daily_traffic = Column(Integer)
|
||||
engineering_assessment = Column(String)
|
||||
technical_support_team = Column(String)
|
||||
estimated_monthly_spend = Column(Integer)
|
||||
average_daily_traffic_gb = Column(Integer)
|
||||
rationalization_software_systems = Column(String)
|
||||
organization_providing_assistance = Column(String)
|
||||
name = Column(String)
|
||||
|
||||
# information_about_you
|
||||
citizenship = Column(String)
|
||||
designation = Column(String)
|
||||
phone_number = Column(String)
|
||||
phone_ext = Column(String)
|
||||
email_request = Column(String)
|
||||
fname_request = Column(String)
|
||||
lname_request = Column(String)
|
||||
service_branch = Column(String)
|
||||
date_latest_training = Column(Date)
|
||||
|
||||
# financial_verification
|
||||
pe_id = Column(String)
|
||||
task_order_number = Column(String)
|
||||
fname_co = Column(String)
|
||||
lname_co = Column(String)
|
||||
email_co = Column(String)
|
||||
office_co = Column(String)
|
||||
fname_cor = Column(String)
|
||||
lname_cor = Column(String)
|
||||
email_cor = Column(String)
|
||||
office_cor = Column(String)
|
||||
uii_ids = Column(ARRAY(String))
|
||||
treasury_code = Column(String)
|
||||
ba_code = Column(String)
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return "<RequestRevision(request='{}', id='{}')>".format(
|
||||
self.request_id, self.id
|
||||
)
|
||||
|
||||
@property
|
||||
def any_finver_fields_saved(self):
|
||||
return any(
|
||||
getattr(self, n, None)
|
||||
for n in [
|
||||
"pe_id",
|
||||
"task_order_number",
|
||||
"fname_co",
|
||||
"lname_co",
|
||||
"email_co",
|
||||
"office_co",
|
||||
"fname_cor",
|
||||
"lname_cor",
|
||||
"email_cor",
|
||||
"office_cor",
|
||||
"uii_ids",
|
||||
"treasury_code",
|
||||
"ba_code",
|
||||
]
|
||||
)
|
@@ -1,62 +0,0 @@
|
||||
from enum import Enum
|
||||
from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.types import BigInteger
|
||||
from sqlalchemy.schema import Sequence
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
from atst.models import Base, mixins
|
||||
from atst.models.types import Id
|
||||
|
||||
|
||||
class RequestStatus(Enum):
|
||||
STARTED = "Started"
|
||||
SUBMITTED = "Submitted"
|
||||
PENDING_FINANCIAL_VERIFICATION = "Pending Financial Verification"
|
||||
PENDING_CCPO_ACCEPTANCE = "Pending CCPO Acceptance"
|
||||
PENDING_CCPO_APPROVAL = "Pending CCPO Approval"
|
||||
CHANGES_REQUESTED = "Changes Requested"
|
||||
CHANGES_REQUESTED_TO_FINVER = "Change Requested to Financial Verification"
|
||||
APPROVED = "Approved"
|
||||
EXPIRED = "Expired"
|
||||
DELETED = "Deleted"
|
||||
|
||||
|
||||
class RequestStatusEvent(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
||||
__tablename__ = "request_status_events"
|
||||
|
||||
id = Id()
|
||||
new_status = Column(SQLAEnum(RequestStatus, native_enum=False))
|
||||
request_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("requests.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
sequence = Column(
|
||||
BigInteger, Sequence("request_status_events_sequence_seq"), nullable=False
|
||||
)
|
||||
request_revision_id = Column(ForeignKey("request_revisions.id"), nullable=False)
|
||||
revision = relationship("RequestRevision")
|
||||
|
||||
request_review_id = Column(ForeignKey("request_reviews.id"), nullable=True)
|
||||
review = relationship("RequestReview", back_populates="status")
|
||||
|
||||
@property
|
||||
def displayname(self):
|
||||
return self.new_status.value if self.new_status else None
|
||||
|
||||
@property
|
||||
def log_name(self):
|
||||
if self.new_status == RequestStatus.CHANGES_REQUESTED:
|
||||
return "Denied"
|
||||
if self.new_status == RequestStatus.CHANGES_REQUESTED_TO_FINVER:
|
||||
return "Denied"
|
||||
elif self.new_status == RequestStatus.PENDING_FINANCIAL_VERIFICATION:
|
||||
return "Accepted"
|
||||
else:
|
||||
return self.displayname
|
||||
|
||||
def __repr__(self):
|
||||
return "<RequestStatusEvent(log_name='{}', request='{}', id='{}')>".format(
|
||||
self.log_name, self.request_id, self.id
|
||||
)
|
Reference in New Issue
Block a user