resolve merge conflict
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
from enum import Enum
|
||||
from sqlalchemy import Column, Date, Enum as SQLAEnum, ForeignKey, Numeric, String
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Date,
|
||||
DateTime,
|
||||
Enum as SQLAEnum,
|
||||
ForeignKey,
|
||||
Numeric,
|
||||
String,
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import date
|
||||
|
||||
@@ -29,6 +37,7 @@ class CLIN(Base, mixins.TimestampsMixin):
|
||||
total_amount = Column(Numeric(scale=2), nullable=False)
|
||||
obligated_amount = Column(Numeric(scale=2), nullable=False)
|
||||
jedi_clin_type = Column(SQLAEnum(JEDICLINType, native_enum=False), nullable=False)
|
||||
last_sent_at = Column(DateTime)
|
||||
|
||||
#
|
||||
# NOTE: For now obligated CLINS are CLIN 1 + CLIN 3
|
||||
|
@@ -61,6 +61,10 @@ class Environment(
|
||||
def portfolio_id(self):
|
||||
return self.application.portfolio_id
|
||||
|
||||
@property
|
||||
def is_pending(self):
|
||||
return self.cloud_id is None
|
||||
|
||||
def __repr__(self):
|
||||
return "<Environment(name='{}', num_users='{}', application='{}', portfolio='{}', id='{}')>".format(
|
||||
self.name,
|
||||
|
@@ -36,7 +36,7 @@ class EnvironmentRole(
|
||||
)
|
||||
application_role = relationship("ApplicationRole")
|
||||
|
||||
csp_user_id = Column(String())
|
||||
cloud_id = Column(String())
|
||||
|
||||
class Status(Enum):
|
||||
PENDING = "pending"
|
||||
|
@@ -24,6 +24,8 @@ class AzureStages(Enum):
|
||||
TENANT_PRINCIPAL_CREDENTIAL = "tenant principal credential"
|
||||
ADMIN_ROLE_DEFINITION = "admin role definition"
|
||||
PRINCIPAL_ADMIN_ROLE = "tenant principal admin"
|
||||
INITIAL_MGMT_GROUP = "initial management group"
|
||||
INITIAL_MGMT_GROUP_VERIFICATION = "initial management group verification"
|
||||
TENANT_ADMIN_OWNERSHIP = "tenant admin ownership"
|
||||
TENANT_PRINCIPAL_OWNERSHIP = "tenant principial ownership"
|
||||
|
||||
|
@@ -1,11 +1,16 @@
|
||||
import re
|
||||
from string import ascii_lowercase, digits
|
||||
from random import choices
|
||||
from itertools import chain
|
||||
|
||||
from sqlalchemy import Column, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.types import ARRAY
|
||||
from itertools import chain
|
||||
|
||||
from atst.models.base import Base
|
||||
import atst.models.types as types
|
||||
import atst.models.mixins as mixins
|
||||
from atst.models.task_order import TaskOrder
|
||||
from atst.models.portfolio_role import PortfolioRole, Status as PortfolioRoleStatus
|
||||
from atst.domain.permission_sets import PermissionSets
|
||||
from atst.utils import first_or_none
|
||||
@@ -89,6 +94,22 @@ class Portfolio(
|
||||
def active_task_orders(self):
|
||||
return [task_order for task_order in self.task_orders if task_order.is_active]
|
||||
|
||||
@property
|
||||
def total_obligated_funds(self):
|
||||
return sum(
|
||||
(task_order.total_obligated_funds for task_order in self.active_task_orders)
|
||||
)
|
||||
|
||||
@property
|
||||
def upcoming_obligated_funds(self):
|
||||
return sum(
|
||||
(
|
||||
task_order.total_obligated_funds
|
||||
for task_order in self.task_orders
|
||||
if task_order.is_upcoming
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def funding_duration(self):
|
||||
"""
|
||||
@@ -153,6 +174,51 @@ class Portfolio(
|
||||
def application_id(self):
|
||||
return None
|
||||
|
||||
def to_dictionary(self):
|
||||
ppoc = self.owner
|
||||
user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower()
|
||||
|
||||
domain_name = re.sub("[^0-9a-zA-Z]+", "", self.name).lower() + "".join(
|
||||
choices(ascii_lowercase + digits, k=4)
|
||||
)
|
||||
portfolio_data = {
|
||||
"user_id": user_id,
|
||||
"password": "",
|
||||
"domain_name": domain_name,
|
||||
"first_name": ppoc.first_name,
|
||||
"last_name": ppoc.last_name,
|
||||
"country_code": "US",
|
||||
"password_recovery_email_address": ppoc.email,
|
||||
"address": { # TODO: TBD if we're sourcing this from data or config
|
||||
"company_name": "",
|
||||
"address_line_1": "",
|
||||
"city": "",
|
||||
"region": "",
|
||||
"country": "",
|
||||
"postal_code": "",
|
||||
},
|
||||
"billing_profile_display_name": "ATAT Billing Profile",
|
||||
}
|
||||
|
||||
try:
|
||||
initial_task_order: TaskOrder = self.task_orders[0]
|
||||
initial_clin = initial_task_order.sorted_clins[0]
|
||||
portfolio_data.update(
|
||||
{
|
||||
"initial_clin_amount": initial_clin.obligated_amount,
|
||||
"initial_clin_start_date": initial_clin.start_date.strftime(
|
||||
"%Y/%m/%d"
|
||||
),
|
||||
"initial_clin_end_date": initial_clin.end_date.strftime("%Y/%m/%d"),
|
||||
"initial_clin_type": initial_clin.number,
|
||||
"initial_task_order_id": initial_task_order.number,
|
||||
}
|
||||
)
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return portfolio_data
|
||||
|
||||
def __repr__(self):
|
||||
return "<Portfolio(name='{}', user_count='{}', id='{}')>".format(
|
||||
self.name, self.user_count, self.id
|
||||
|
@@ -119,6 +119,8 @@ class PortfolioStateMachine(
|
||||
def trigger_next_transition(self, **kwargs):
|
||||
state_obj = self.machine.get_state(self.state)
|
||||
|
||||
kwargs["csp_data"] = kwargs.get("csp_data", {})
|
||||
|
||||
if state_obj.is_system:
|
||||
if self.current_state in (FSMStates.UNSTARTED, FSMStates.STARTING):
|
||||
# call the first trigger availabe for these two system states
|
||||
|
@@ -25,7 +25,6 @@ SORT_ORDERING = [
|
||||
Status.DRAFT,
|
||||
Status.UPCOMING,
|
||||
Status.EXPIRED,
|
||||
Status.UNSIGNED,
|
||||
]
|
||||
|
||||
|
||||
@@ -39,6 +38,7 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
||||
|
||||
pdf_attachment_id = Column(ForeignKey("attachments.id"))
|
||||
_pdf = relationship("Attachment", foreign_keys=[pdf_attachment_id])
|
||||
pdf_last_sent_at = Column(DateTime)
|
||||
number = Column(String, unique=True,) # Task Order Number
|
||||
signer_dod_id = Column(String)
|
||||
signed_at = Column(DateTime)
|
||||
@@ -87,6 +87,10 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
||||
def is_expired(self):
|
||||
return self.status == Status.EXPIRED
|
||||
|
||||
@property
|
||||
def is_upcoming(self):
|
||||
return self.status == Status.UPCOMING
|
||||
|
||||
@property
|
||||
def clins_are_completed(self):
|
||||
return all([len(self.clins), (clin.is_completed for clin in self.clins)])
|
||||
@@ -147,7 +151,10 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
||||
|
||||
@property
|
||||
def display_status(self):
|
||||
return self.status.value
|
||||
if self.status == Status.UNSIGNED:
|
||||
return Status.DRAFT.value
|
||||
else:
|
||||
return self.status.value
|
||||
|
||||
@property
|
||||
def portfolio_name(self):
|
||||
|
Reference in New Issue
Block a user