Remove request-related models
This commit is contained in:
parent
6fb333acb9
commit
2c62f54b83
@ -225,8 +225,6 @@ class MockReportingProvider(ReportingInterface):
|
||||
def get_budget(self, portfolio):
|
||||
if portfolio.name in self.REPORT_FIXTURE_MAP:
|
||||
return self.REPORT_FIXTURE_MAP[portfolio.name]["budget"]
|
||||
elif portfolio.request and portfolio.legacy_task_order:
|
||||
return portfolio.legacy_task_order.budget
|
||||
return 0
|
||||
|
||||
def get_total_spending(self, portfolio):
|
||||
|
@ -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
|
||||
)
|
@ -6,7 +6,6 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Portfolio Name</th>
|
||||
<th>Task Order</th>
|
||||
<th>Users</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -16,9 +15,6 @@
|
||||
<td>
|
||||
<a class='icon-link icon-link--large' href="/portfolios/{{ portfolio.id }}/applications">{{ portfolio.name }}</a><br>
|
||||
</td>
|
||||
<td>
|
||||
#{{ portfolio.legacy_task_order.number }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="label">{{ portfolio.user_count }}</span><span class='h6'>Users</span>
|
||||
</td>
|
||||
|
@ -1,11 +1,10 @@
|
||||
from atst.domain.applications import Applications
|
||||
from tests.factories import RequestFactory, UserFactory, PortfolioFactory
|
||||
from tests.factories import UserFactory, PortfolioFactory
|
||||
from atst.domain.portfolios import Portfolios
|
||||
|
||||
|
||||
def test_create_application_with_multiple_environments():
|
||||
request = RequestFactory.create()
|
||||
portfolio = Portfolios.create_from_request(request)
|
||||
portfolio = PortfolioFactory.create()
|
||||
application = Applications.create(
|
||||
portfolio.owner, portfolio, "My Test Application", "Test", ["dev", "prod"]
|
||||
)
|
||||
|
@ -8,12 +8,7 @@ from atst.domain.applications import Applications
|
||||
from atst.domain.environments import Environments
|
||||
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
||||
|
||||
from tests.factories import (
|
||||
RequestFactory,
|
||||
UserFactory,
|
||||
PortfolioRoleFactory,
|
||||
PortfolioFactory,
|
||||
)
|
||||
from tests.factories import UserFactory, PortfolioRoleFactory, PortfolioFactory
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@ -22,39 +17,21 @@ def portfolio_owner():
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def request_(portfolio_owner):
|
||||
return RequestFactory.create(creator=portfolio_owner)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def portfolio(request_):
|
||||
portfolio = Portfolios.create_from_request(request_)
|
||||
def portfolio(portfolio_owner):
|
||||
portfolio = PortfolioFactory.create(owner=portfolio_owner)
|
||||
return portfolio
|
||||
|
||||
|
||||
def test_can_create_portfolio(request_):
|
||||
portfolio = Portfolios.create_from_request(request_, name="frugal-whale")
|
||||
def test_can_create_portfolio():
|
||||
portfolio = PortfolioFactory.create(name="frugal-whale")
|
||||
assert portfolio.name == "frugal-whale"
|
||||
|
||||
|
||||
def test_request_is_associated_with_portfolio(portfolio, request_):
|
||||
assert portfolio.request == request_
|
||||
|
||||
|
||||
def test_default_portfolio_name_is_request_name(portfolio, request_):
|
||||
assert portfolio.name == str(request_.displayname)
|
||||
|
||||
|
||||
def test_get_nonexistent_portfolio_raises():
|
||||
with pytest.raises(NotFoundError):
|
||||
Portfolios.get(UserFactory.build(), uuid4())
|
||||
|
||||
|
||||
def test_can_get_portfolio_by_request(portfolio):
|
||||
found = Portfolios.get_by_request(portfolio.request)
|
||||
assert portfolio == found
|
||||
|
||||
|
||||
def test_creating_portfolio_adds_owner(portfolio, portfolio_owner):
|
||||
assert portfolio.roles[0].user == portfolio_owner
|
||||
|
||||
@ -162,10 +139,6 @@ def test_need_permission_to_update_portfolio_role_role(portfolio, portfolio_owne
|
||||
|
||||
|
||||
def test_owner_can_view_portfolio_members(portfolio, portfolio_owner):
|
||||
portfolio_owner = UserFactory.create()
|
||||
portfolio = Portfolios.create_from_request(
|
||||
RequestFactory.create(creator=portfolio_owner)
|
||||
)
|
||||
portfolio = Portfolios.get_with_members(portfolio_owner, portfolio.id)
|
||||
|
||||
assert portfolio
|
||||
@ -258,7 +231,7 @@ def test_for_user_returns_active_portfolios_for_user(portfolio, portfolio_owner)
|
||||
PortfolioRoleFactory.create(
|
||||
user=bob, portfolio=portfolio, status=PortfolioRoleStatus.ACTIVE
|
||||
)
|
||||
Portfolios.create_from_request(RequestFactory.create())
|
||||
PortfolioFactory.create()
|
||||
|
||||
bobs_portfolios = Portfolios.for_user(bob)
|
||||
|
||||
@ -268,7 +241,7 @@ def test_for_user_returns_active_portfolios_for_user(portfolio, portfolio_owner)
|
||||
def test_for_user_does_not_return_inactive_portfolios(portfolio, portfolio_owner):
|
||||
bob = UserFactory.from_atat_role("default")
|
||||
Portfolios.add_member(portfolio, bob, "developer")
|
||||
Portfolios.create_from_request(RequestFactory.create())
|
||||
PortfolioFactory.create()
|
||||
bobs_portfolios = Portfolios.for_user(bob)
|
||||
|
||||
assert len(bobs_portfolios) == 0
|
||||
@ -276,17 +249,13 @@ def test_for_user_does_not_return_inactive_portfolios(portfolio, portfolio_owner
|
||||
|
||||
def test_for_user_returns_all_portfolios_for_ccpo(portfolio, portfolio_owner):
|
||||
sam = UserFactory.from_atat_role("ccpo")
|
||||
Portfolios.create_from_request(RequestFactory.create())
|
||||
PortfolioFactory.create()
|
||||
|
||||
sams_portfolios = Portfolios.for_user(sam)
|
||||
assert len(sams_portfolios) == 2
|
||||
|
||||
|
||||
def test_get_for_update_information():
|
||||
portfolio_owner = UserFactory.create()
|
||||
portfolio = Portfolios.create_from_request(
|
||||
RequestFactory.create(creator=portfolio_owner)
|
||||
)
|
||||
def test_get_for_update_information(portfolio, portfolio_owner):
|
||||
owner_ws = Portfolios.get_for_update_information(portfolio_owner, portfolio.id)
|
||||
assert portfolio == owner_ws
|
||||
|
||||
@ -307,8 +276,8 @@ def test_get_for_update_information():
|
||||
|
||||
def test_can_create_portfolios_with_matching_names():
|
||||
portfolio_name = "Great Portfolio"
|
||||
Portfolios.create_from_request(RequestFactory.create(), name=portfolio_name)
|
||||
Portfolios.create_from_request(RequestFactory.create(), name=portfolio_name)
|
||||
PortfolioFactory.create(name=portfolio_name)
|
||||
PortfolioFactory.create(name=portfolio_name)
|
||||
|
||||
|
||||
def test_able_to_revoke_portfolio_access_for_active_member():
|
||||
|
@ -1,27 +1,17 @@
|
||||
from atst.domain.reports import Reports
|
||||
|
||||
from tests.factories import RequestFactory, LegacyTaskOrderFactory, PortfolioFactory
|
||||
|
||||
CLIN_NUMS = ["0001", "0003", "1001", "1003", "2001", "2003"]
|
||||
from tests.factories import PortfolioFactory
|
||||
|
||||
|
||||
def test_portfolio_totals():
|
||||
legacy_task_order = LegacyTaskOrderFactory.create()
|
||||
|
||||
for num in CLIN_NUMS:
|
||||
setattr(legacy_task_order, "clin_{}".format(num), 200)
|
||||
|
||||
request = RequestFactory.create(legacy_task_order=legacy_task_order)
|
||||
portfolio = PortfolioFactory.create(request=request)
|
||||
portfolio = PortfolioFactory.create()
|
||||
report = Reports.portfolio_totals(portfolio)
|
||||
total = 200 * len(CLIN_NUMS)
|
||||
assert report == {"budget": total, "spent": 0}
|
||||
assert report == {"budget": 0, "spent": 0}
|
||||
|
||||
|
||||
# this is sketched in until we do real reporting
|
||||
def test_monthly_totals():
|
||||
request = RequestFactory.create()
|
||||
portfolio = PortfolioFactory.create(request=request)
|
||||
portfolio = PortfolioFactory.create()
|
||||
monthly = Reports.monthly_totals(portfolio)
|
||||
|
||||
assert not monthly["environments"]
|
||||
@ -31,8 +21,7 @@ def test_monthly_totals():
|
||||
|
||||
# this is sketched in until we do real reporting
|
||||
def test_cumulative_budget():
|
||||
request = RequestFactory.create()
|
||||
portfolio = PortfolioFactory.create(request=request)
|
||||
portfolio = PortfolioFactory.create()
|
||||
months = Reports.cumulative_budget(portfolio)
|
||||
|
||||
assert len(months["months"]) >= 12
|
||||
|
@ -9,10 +9,6 @@ from faker import Faker as _Faker
|
||||
from atst.forms import data
|
||||
from atst.models.attachment import Attachment
|
||||
from atst.models.environment import Environment
|
||||
from atst.models.request import Request
|
||||
from atst.models.request_revision import RequestRevision
|
||||
from atst.models.request_review import RequestReview
|
||||
from atst.models.request_status_event import RequestStatusEvent, RequestStatus
|
||||
from atst.models.pe_number import PENumber
|
||||
from atst.models.application import Application
|
||||
from atst.models.legacy_task_order import LegacyTaskOrder, Source, FundingType
|
||||
@ -105,142 +101,6 @@ class UserFactory(Base):
|
||||
return cls.create(atat_role=role, **kwargs)
|
||||
|
||||
|
||||
class RequestStatusEventFactory(Base):
|
||||
class Meta:
|
||||
model = RequestStatusEvent
|
||||
|
||||
id = factory.Sequence(lambda x: uuid4())
|
||||
sequence = 1
|
||||
|
||||
|
||||
class RequestRevisionFactory(Base):
|
||||
class Meta:
|
||||
model = RequestRevision
|
||||
|
||||
id = factory.Sequence(lambda x: uuid4())
|
||||
|
||||
|
||||
class RequestReviewFactory(Base):
|
||||
class Meta:
|
||||
model = RequestReview
|
||||
|
||||
comment = factory.Faker("sentence")
|
||||
fname_mao = factory.Faker("first_name")
|
||||
lname_mao = factory.Faker("last_name")
|
||||
email_mao = factory.Faker("email")
|
||||
phone_mao = factory.LazyFunction(
|
||||
lambda: "".join(random.choices(string.digits, k=10))
|
||||
)
|
||||
fname_ccpo = factory.Faker("first_name")
|
||||
lname_ccpo = factory.Faker("last_name")
|
||||
|
||||
|
||||
class RequestFactory(Base):
|
||||
class Meta:
|
||||
model = Request
|
||||
|
||||
id = factory.Sequence(lambda x: uuid4())
|
||||
creator = factory.SubFactory(UserFactory)
|
||||
revisions = factory.LazyAttribute(
|
||||
lambda r: [RequestFactory.create_initial_revision(r)]
|
||||
)
|
||||
status_events = factory.RelatedFactory(
|
||||
RequestStatusEventFactory,
|
||||
"request",
|
||||
new_status=RequestStatus.STARTED,
|
||||
revision=factory.LazyAttribute(lambda se: se.factory_parent.revisions[-1]),
|
||||
)
|
||||
|
||||
class Params:
|
||||
initial_revision = None
|
||||
|
||||
@classmethod
|
||||
def _adjust_kwargs(cls, **kwargs):
|
||||
if kwargs.pop("with_task_order", False) and "legacy_task_order" not in kwargs:
|
||||
kwargs["legacy_task_order"] = LegacyTaskOrderFactory.build()
|
||||
return kwargs
|
||||
|
||||
@classmethod
|
||||
def create_initial_status_event(cls, request):
|
||||
return RequestStatusEventFactory(
|
||||
request=request,
|
||||
new_status=RequestStatus.STARTED,
|
||||
revision=request.revisions,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_initial_revision(cls, request, dollar_value=1_000_000):
|
||||
user = request.creator
|
||||
default_data = dict(
|
||||
name=factory.Faker("domain_word"),
|
||||
am_poc=False,
|
||||
dodid_poc=user.dod_id,
|
||||
email_poc=user.email,
|
||||
fname_poc=user.first_name,
|
||||
lname_poc=user.last_name,
|
||||
jedi_usage="adf",
|
||||
start_date=datetime.date(2050, 1, 1),
|
||||
cloud_native="yes",
|
||||
dollar_value=dollar_value,
|
||||
dod_component=random_service_branch(),
|
||||
data_transfers="Less than 100GB",
|
||||
expected_completion_date="Less than 1 month",
|
||||
jedi_migration="yes",
|
||||
num_software_systems=1,
|
||||
number_user_sessions=2,
|
||||
average_daily_traffic=1,
|
||||
engineering_assessment="yes",
|
||||
technical_support_team="yes",
|
||||
estimated_monthly_spend=100,
|
||||
average_daily_traffic_gb=4,
|
||||
rationalization_software_systems="yes",
|
||||
organization_providing_assistance="In-house staff",
|
||||
citizenship="United States",
|
||||
designation="military",
|
||||
phone_number="1234567890",
|
||||
phone_ext="123",
|
||||
email_request=user.email,
|
||||
fname_request=user.first_name,
|
||||
lname_request=user.last_name,
|
||||
service_branch=random_service_branch(),
|
||||
date_latest_training=datetime.date(2018, 8, 6),
|
||||
)
|
||||
|
||||
data = (
|
||||
request.initial_revision
|
||||
if request.initial_revision is not None
|
||||
else default_data
|
||||
)
|
||||
|
||||
return RequestRevisionFactory.build(**data)
|
||||
|
||||
@classmethod
|
||||
def create_with_status(cls, status=RequestStatus.STARTED, **kwargs):
|
||||
request = RequestFactory(**kwargs)
|
||||
RequestStatusEventFactory.create(
|
||||
request=request, revision=request.latest_revision, new_status=status
|
||||
)
|
||||
return request
|
||||
|
||||
@classmethod
|
||||
def mock_financial_data(cls):
|
||||
fake = _Faker()
|
||||
return {
|
||||
"pe_id": "0101110F",
|
||||
"fname_co": fake.first_name(),
|
||||
"lname_co": fake.last_name(),
|
||||
"email_co": fake.email(),
|
||||
"office_co": fake.phone_number(),
|
||||
"fname_cor": fake.first_name(),
|
||||
"lname_cor": fake.last_name(),
|
||||
"email_cor": fake.email(),
|
||||
"office_cor": fake.phone_number(),
|
||||
"uii_ids": "123abc",
|
||||
"treasury_code": "00123456",
|
||||
"ba_code": "02A",
|
||||
}
|
||||
|
||||
|
||||
class PENumberFactory(Base):
|
||||
class Meta:
|
||||
model = PENumber
|
||||
@ -269,9 +129,7 @@ class PortfolioFactory(Base):
|
||||
class Meta:
|
||||
model = Portfolio
|
||||
|
||||
request = factory.SubFactory(RequestFactory, with_task_order=True)
|
||||
# name it the same as the request ID by default
|
||||
name = factory.LazyAttribute(lambda w: w.request.id)
|
||||
name = factory.Faker("name")
|
||||
|
||||
@classmethod
|
||||
def _create(cls, model_class, *args, **kwargs):
|
||||
@ -286,7 +144,6 @@ class PortfolioFactory(Base):
|
||||
for p in with_applications
|
||||
]
|
||||
|
||||
portfolio.request.creator = owner
|
||||
PortfolioRoleFactory.create(
|
||||
portfolio=portfolio,
|
||||
role=Roles.get("owner"),
|
||||
|
@ -1,4 +1,4 @@
|
||||
from tests.factories import RequestFactory, UserFactory
|
||||
from tests.factories import UserFactory
|
||||
|
||||
|
||||
DOD_SDN_INFO = {"first_name": "ART", "last_name": "GARFUNKEL", "dod_id": "5892460358"}
|
||||
|
@ -1,14 +1,13 @@
|
||||
from atst.domain.environments import Environments
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.applications import Applications
|
||||
from tests.factories import RequestFactory, UserFactory
|
||||
from tests.factories import PortfolioFactory, UserFactory
|
||||
|
||||
|
||||
def test_add_user_to_environment():
|
||||
owner = UserFactory.create()
|
||||
developer = UserFactory.from_atat_role("developer")
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
application = Applications.create(
|
||||
owner,
|
||||
portfolio,
|
||||
|
@ -9,7 +9,6 @@ from atst.models.invitation import Status as InvitationStatus
|
||||
from atst.models.audit_event import AuditEvent
|
||||
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
||||
from tests.factories import (
|
||||
RequestFactory,
|
||||
UserFactory,
|
||||
InvitationFactory,
|
||||
PortfolioRoleFactory,
|
||||
@ -25,7 +24,7 @@ def test_has_no_ws_role_history(session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = PortfolioRoles.add(user, portfolio.id, "developer")
|
||||
create_event = (
|
||||
session.query(AuditEvent)
|
||||
@ -42,7 +41,7 @@ def test_has_ws_role_history(session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
role = session.query(Role).filter(Role.name == "developer").one()
|
||||
# in order to get the history, we don't want the PortfolioRoleFactory
|
||||
# to commit after create()
|
||||
@ -67,7 +66,7 @@ def test_has_ws_status_history(session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
# in order to get the history, we don't want the PortfolioRoleFactory
|
||||
# to commit after create()
|
||||
PortfolioRoleFactory._meta.sqlalchemy_session_persistence = "flush"
|
||||
@ -89,7 +88,7 @@ def test_has_ws_status_history(session):
|
||||
def test_has_no_env_role_history(session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
application = ApplicationFactory.create(portfolio=portfolio)
|
||||
environment = EnvironmentFactory.create(
|
||||
application=application, name="new environment!"
|
||||
@ -110,7 +109,7 @@ def test_has_no_env_role_history(session):
|
||||
def test_has_env_role_history(session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = PortfolioRoleFactory.create(portfolio=portfolio, user=user)
|
||||
application = ApplicationFactory.create(portfolio=portfolio)
|
||||
environment = EnvironmentFactory.create(
|
||||
@ -137,7 +136,7 @@ def test_event_details():
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = PortfolioRoles.add(user, portfolio.id, "developer")
|
||||
|
||||
assert portfolio_role.event_details["updated_user_name"] == user.displayname
|
||||
@ -154,7 +153,7 @@ def test_has_no_environment_roles():
|
||||
"portfolio_role": "developer",
|
||||
}
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = Portfolios.create_member(owner, portfolio, developer_data)
|
||||
|
||||
assert not portfolio_role.has_environment_roles
|
||||
@ -170,7 +169,7 @@ def test_has_environment_roles():
|
||||
"portfolio_role": "developer",
|
||||
}
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = Portfolios.create_member(owner, portfolio, developer_data)
|
||||
application = Applications.create(
|
||||
owner,
|
||||
@ -195,7 +194,7 @@ def test_role_displayname():
|
||||
"portfolio_role": "developer",
|
||||
}
|
||||
|
||||
portfolio = Portfolios.create_from_request(RequestFactory.create(creator=owner))
|
||||
portfolio = PortfolioFactory.create(owner=owner)
|
||||
portfolio_role = Portfolios.create_member(owner, portfolio, developer_data)
|
||||
|
||||
assert portfolio_role.role_displayname == "Developer"
|
||||
|
@ -1,28 +1,27 @@
|
||||
import pytest
|
||||
|
||||
from tests.factories import UserFactory, PortfolioFactory, RequestFactory
|
||||
from tests.factories import UserFactory, PortfolioFactory
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
||||
|
||||
|
||||
def test_request_owner_with_one_portfolio_redirected_to_reports(client, user_session):
|
||||
request = RequestFactory.create()
|
||||
portfolio = Portfolios.create_from_request(request)
|
||||
def test_portfolio_owner_with_one_portfolio_redirected_to_reports(client, user_session):
|
||||
portfolio = PortfolioFactory.create()
|
||||
|
||||
user_session(request.creator)
|
||||
user_session(portfolio.owner)
|
||||
response = client.get("/home", follow_redirects=False)
|
||||
|
||||
assert "/portfolios/{}/reports".format(portfolio.id) in response.location
|
||||
|
||||
|
||||
def test_request_owner_with_more_than_one_portfolio_redirected_to_portfolios(
|
||||
def test_portfolio_owner_with_more_than_one_portfolio_redirected_to_portfolios(
|
||||
client, user_session
|
||||
):
|
||||
request_creator = UserFactory.create()
|
||||
Portfolios.create_from_request(RequestFactory.create(creator=request_creator))
|
||||
Portfolios.create_from_request(RequestFactory.create(creator=request_creator))
|
||||
owner = UserFactory.create()
|
||||
PortfolioFactory.create(owner=owner)
|
||||
PortfolioFactory.create(owner=owner)
|
||||
|
||||
user_session(request_creator)
|
||||
user_session(owner)
|
||||
response = client.get("/home", follow_redirects=False)
|
||||
|
||||
assert "/portfolios" in response.location
|
||||
|
Loading…
x
Reference in New Issue
Block a user