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

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

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
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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