Remove request-related models

This commit is contained in:
Patrick Smith
2019-02-20 15:57:30 -05:00
parent 6fb333acb9
commit 2c62f54b83
19 changed files with 42 additions and 745 deletions

View File

@@ -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

View File

@@ -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())

View File

@@ -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

View File

@@ -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
)

View File

@@ -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,
)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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",
]
)

View File

@@ -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
)