fields for the new task order

This commit is contained in:
dandds 2018-12-14 13:19:22 -05:00
parent 6d92755a7f
commit c5580733ba
7 changed files with 308 additions and 51 deletions

View File

@ -0,0 +1,96 @@
"""update task order columns
Revision ID: ea06f5863083
Revises: a4cb6444eb4a
Create Date: 2018-12-14 13:19:11.956511
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ea06f5863083'
down_revision = 'a4cb6444eb4a'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('task_orders', sa.Column('app_migration', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('clin_01', sa.Integer(), nullable=True))
op.add_column('task_orders', sa.Column('clin_02', sa.Integer(), nullable=True))
op.add_column('task_orders', sa.Column('clin_03', sa.Integer(), nullable=True))
op.add_column('task_orders', sa.Column('clin_04', sa.Integer(), nullable=True))
op.add_column('task_orders', sa.Column('complexity', sa.ARRAY(sa.String()), nullable=True))
op.add_column('task_orders', sa.Column('complexity_other', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('cor_dod_id', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('cor_email', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('cor_first_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('cor_last_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('defense_component', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('dev_team', sa.ARRAY(sa.String()), nullable=True))
op.add_column('task_orders', sa.Column('dev_team_other', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('end_date', sa.Date(), nullable=True))
op.add_column('task_orders', sa.Column('ko_dod_id', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('ko_email', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('ko_first_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('ko_last_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('loa', sa.ARRAY(sa.String()), nullable=True))
op.add_column('task_orders', sa.Column('native_apps', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('scope', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('so_dod_id', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('so_email', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('so_first_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('so_last_name', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('start_date', sa.Date(), nullable=True))
op.add_column('task_orders', sa.Column('team_experience', sa.String(), nullable=True))
op.drop_column('task_orders', 'expiration_date')
op.drop_column('task_orders', 'clin_1003')
op.drop_column('task_orders', 'clin_0001')
op.drop_column('task_orders', 'clin_2003')
op.drop_column('task_orders', 'clin_1001')
op.drop_column('task_orders', 'clin_2001')
op.drop_column('task_orders', 'clin_0003')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('task_orders', sa.Column('clin_0003', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('clin_2001', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('clin_1001', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('clin_2003', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('clin_0001', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('clin_1003', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('task_orders', sa.Column('expiration_date', sa.DATE(), autoincrement=False, nullable=True))
op.drop_column('task_orders', 'team_experience')
op.drop_column('task_orders', 'start_date')
op.drop_column('task_orders', 'so_last_name')
op.drop_column('task_orders', 'so_first_name')
op.drop_column('task_orders', 'so_email')
op.drop_column('task_orders', 'so_dod_id')
op.drop_column('task_orders', 'scope')
op.drop_column('task_orders', 'native_apps')
op.drop_column('task_orders', 'loa')
op.drop_column('task_orders', 'ko_last_name')
op.drop_column('task_orders', 'ko_first_name')
op.drop_column('task_orders', 'ko_email')
op.drop_column('task_orders', 'ko_dod_id')
op.drop_column('task_orders', 'end_date')
op.drop_column('task_orders', 'dev_team_other')
op.drop_column('task_orders', 'dev_team')
op.drop_column('task_orders', 'defense_component')
op.drop_column('task_orders', 'cor_last_name')
op.drop_column('task_orders', 'cor_first_name')
op.drop_column('task_orders', 'cor_email')
op.drop_column('task_orders', 'cor_dod_id')
op.drop_column('task_orders', 'complexity_other')
op.drop_column('task_orders', 'complexity')
op.drop_column('task_orders', 'clin_04')
op.drop_column('task_orders', 'clin_03')
op.drop_column('task_orders', 'clin_02')
op.drop_column('task_orders', 'clin_01')
op.drop_column('task_orders', 'app_migration')
# ### end Alembic commands ###

View File

@ -173,3 +173,39 @@ FUNDING_TYPES = [
] ]
TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")] TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")]
APP_MIGRATION = [
("on_premise", "Yes, migrating from an on-premise data center"),
("cloud", "Yes, migrating from another cloud provider "),
("none", "Not planning to migrate any applications "),
("not_sure", "Not Sure"),
]
PROJECT_COMPLEXITY = [
("storage", "Storage "),
("data_analytics", "Data Analytics "),
("conus", "CONUS Only Access "),
("oconus", "OCONUS Access "),
("tactical_edge", "Tactical Edge Access "),
("not_sure", "Not Sure "),
("other", "Other"),
]
DEV_TEAM = [
("government", "Government"),
("civilians", "Civilians"),
("military", "Military "),
("contractor", "Contractor "),
("other", "Other"),
]
TEAM_EXPERIENCE = [
("none", "No previous experience"),
("planned", "Researched or planned a cloud build or migration"),
("built_1", "Built or Migrated 1-2 applications"),
("built_3", "Built or Migrated 3-5 applications"),
(
"built_many",
"Built or migrated many applications, or consulted on several such projects",
),
]

View File

@ -1,12 +1,96 @@
from wtforms.fields import StringField from wtforms.fields import (
DateField,
IntegerField,
RadioField,
SelectField,
SelectMultipleField,
StringField,
TextAreaField,
)
from .forms import CacheableForm from .forms import CacheableForm
from .data import (
SERVICE_BRANCHES,
APP_MIGRATION,
PROJECT_COMPLEXITY,
DEV_TEAM,
TEAM_EXPERIENCE,
)
class TaskOrderForm(CacheableForm): class TaskOrderForm(CacheableForm):
clin_0001 = StringField("CLIN 0001") scope = TextAreaField(
clin_0003 = StringField("CLIN 0003") "Cloud Project Scope",
clin_1001 = StringField("CLIN 1001") description="The name of your office or organization. You can add multiple applications to your portfolio. Your task orders are used to pay for these applications and their environments",
clin_1003 = StringField("CLIN 1003") )
clin_2001 = StringField("CLIN 2001") defense_component = SelectField(
clin_2003 = StringField("CLIN 2003") "Department of Defense Component",
description="Your team's plan for using the cloud, such as migrating an existing application or creating a prototype.",
choices=SERVICE_BRANCHES,
)
app_migration = RadioField(
"App Migration",
description="Do you plan to migrate existing application(s) to the cloud?",
choices=APP_MIGRATION,
default="",
)
native_apps = RadioField(
"Native Apps",
description="Do you plan to develop application(s) natively in the cloud? ",
choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")],
)
complexity = SelectMultipleField(
"Project Complexity",
description="Which of these describes how complex your team's use of the cloud will be? (Select all that apply.)",
choices=PROJECT_COMPLEXITY,
default="",
)
complexity_other = StringField("?")
dev_team = SelectMultipleField(
"Development Team",
description="Which people or teams will be completing the development work for your cloud applications?",
choices=DEV_TEAM,
default="",
)
dev_team_other = StringField("?")
team_experience = RadioField(
"Team Experience",
description="How much experience does your team have with development in the cloud?",
choices=TEAM_EXPERIENCE,
default="",
)
start_date = DateField(
"Period of Performance",
description="Select a start and end date for your Task Order to be active. Please note, this will likely be revised once your Task Order has been approved.",
)
end_date = DateField("Period of Performance")
clin_01 = IntegerField(
"CLIN 01 : Unclassified Cloud Offerings",
description="UNCLASSIFIED Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings. ",
)
clin_02 = IntegerField(
"CLIN 02: Classified Cloud Offerings",
description="CLASSIFIED Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings. ",
)
clin_03 = IntegerField(
"CLIN 03: Unclassified Cloud Support and Assistance",
description="UNCLASSIFIED technical guidance from the cloud service provider, including architecture, configuration of IaaS and PaaS, integration, troubleshooting assistance, and other services.",
)
clin_04 = IntegerField(
"CLIN 04: Classified Cloud Support and Assistance",
description="CLASSIFIED technical guidance from the cloud service provider, including architecture, configuration of IaaS and PaaS, integration, troubleshooting assistance, and other services.",
)
ko_first_name = StringField("First Name")
ko_last_name = StringField("Last Name")
ko_email = StringField("Email")
ko_dod_id = StringField("DOD ID")
cor_first_name = StringField("First Name")
cor_last_name = StringField("Last Name")
cor_email = StringField("Email")
cor_dod_id = StringField("DOD ID")
so_first_name = StringField("First Name")
so_last_name = StringField("Last Name")
so_email = StringField("Email")
so_dod_id = StringField("DOD ID")
number = StringField("Task Order Number")
loa = StringField("Line of Accounting (LOA)")

View File

@ -1,4 +1,5 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Date from sqlalchemy import Column, Integer, String, ForeignKey, Date
from sqlalchemy.types import ARRAY
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from atst.models import Base, types, mixins from atst.models import Base, types, mixins
@ -8,14 +9,6 @@ class TaskOrder(Base, mixins.TimestampsMixin):
__tablename__ = "task_orders" __tablename__ = "task_orders"
id = types.Id() id = types.Id()
number = Column(String, unique=True)
clin_0001 = Column(Integer)
clin_0003 = Column(Integer)
clin_1001 = Column(Integer)
clin_1003 = Column(Integer)
clin_2001 = Column(Integer)
clin_2003 = Column(Integer)
expiration_date = Column(Date)
workspace_id = Column(ForeignKey("workspaces.id")) workspace_id = Column(ForeignKey("workspaces.id"))
workspace = relationship("Workspace") workspace = relationship("Workspace")
@ -23,23 +16,43 @@ class TaskOrder(Base, mixins.TimestampsMixin):
user_id = Column(ForeignKey("users.id")) user_id = Column(ForeignKey("users.id"))
creator = relationship("User") creator = relationship("User")
scope = Column(String) # Cloud Project Scope
defense_component = Column(String) # Department of Defense Component
app_migration = Column(String) # App Migration
native_apps = Column(String) # Native Apps
complexity = Column(ARRAY(String)) # Project Complexity
complexity_other = Column(String)
dev_team = Column(ARRAY(String)) # Development Team
dev_team_other = Column(String)
team_experience = Column(String) # Team Experience
start_date = Column(Date) # Period of Performance
end_date = Column(Date)
clin_01 = Column(Integer) # CLIN 01 : Unclassified Cloud Offerings
clin_02 = Column(Integer) # CLIN 02: Classified Cloud Offerings
clin_03 = Column(Integer) # CLIN 03: Unclassified Cloud Support and Assistance
clin_04 = Column(Integer) # CLIN 04: Classified Cloud Support and Assistance
ko_first_name = Column(String) # First Name
ko_last_name = Column(String) # Last Name
ko_email = Column(String) # Email
ko_dod_id = Column(String) # DOD ID
cor_first_name = Column(String) # First Name
cor_last_name = Column(String) # Last Name
cor_email = Column(String) # Email
cor_dod_id = Column(String) # DOD ID
so_first_name = Column(String) # First Name
so_last_name = Column(String) # Last Name
so_email = Column(String) # Email
so_dod_id = Column(String) # DOD ID
number = Column(String, unique=True) # Task Order Number
loa = Column(ARRAY(String)) # Line of Accounting (LOA)
@property @property
def budget(self): def budget(self):
return sum( return sum(
filter( filter(None, [self.clin_01, self.clin_02, self.clin_03, self.clin_04])
None,
[
self.clin_0001,
self.clin_0003,
self.clin_1001,
self.clin_1003,
self.clin_2001,
self.clin_2003,
],
)
) )
def __repr__(self): def __repr__(self):
return "<TaskOrder(number='{}', budget='{}', expiration_date='{}', id='{}')>".format( return "<TaskOrder(number='{}', budget='{}', end_date='{}', id='{}')>".format(
self.number, self.budget, self.expiration_date, self.id self.number, self.budget, self.end_date, self.id
) )

View File

@ -1,6 +1,8 @@
{% extends "base.html" %} {% extends "base.html" %}
{% from "components/text_input.html" import TextInput %} {% from "components/text_input.html" import TextInput %}
{% from "components/options_input.html" import OptionsInput %}
{% from "components/date_input.html" import DateInput %}
{% block content %} {% block content %}
@ -16,12 +18,38 @@
</div> </div>
<div class="panel__content"> <div class="panel__content">
{{ TextInput(form.clin_0001) }} {{ TextInput(form.scope, paragraph=True) }}
{{ TextInput(form.clin_0003) }} {{ OptionsInput(form.defense_component) }}
{{ TextInput(form.clin_1001) }} {{ OptionsInput(form.app_migration) }}
{{ TextInput(form.clin_1003) }} {{ OptionsInput(form.native_apps) }}
{{ TextInput(form.clin_2001) }} {{ OptionsInput(form.complexity) }}
{{ TextInput(form.clin_2003) }} {{ TextInput(form.complexity_other) }}
{{ OptionsInput(form.dev_team) }}
{{ TextInput(form.dev_team_other) }}
{{ OptionsInput(form.team_experience) }}
{{ DateInput(form.start_date, placeholder='MM / DD / YYYY', validation='date') }}
{{ DateInput(form.end_date, placeholder='MM / DD / YYYY', validation='date') }}
{{ TextInput(form.clin_01, validation='dollars') }}
{{ TextInput(form.clin_02, validation='dollars') }}
{{ TextInput(form.clin_03, validation='dollars') }}
{{ TextInput(form.clin_04, validation='dollars') }}
<h3>Contracting Officer (KO) Information</h3>
{{ TextInput(form.ko_first_name) }}
{{ TextInput(form.ko_last_name) }}
{{ TextInput(form.ko_email) }}
{{ TextInput(form.ko_dod_id) }}
<h3>Contractive Officer Representative (COR) Information</h3>
{{ TextInput(form.cor_first_name) }}
{{ TextInput(form.cor_last_name) }}
{{ TextInput(form.cor_email) }}
{{ TextInput(form.cor_dod_id) }}
<h3>Security Officer Information</h3>
{{ TextInput(form.so_first_name) }}
{{ TextInput(form.so_last_name) }}
{{ TextInput(form.so_email) }}
{{ TextInput(form.so_dod_id) }}
{{ TextInput(form.number) }}
{{ TextInput(form.loa) }}
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ from uuid import uuid4
import datetime import datetime
from faker import Faker as _Faker from faker import Faker as _Faker
from atst.forms.data import SERVICE_BRANCHES from atst.forms import data
from atst.models.environment import Environment from atst.models.environment import Environment
from atst.models.request import Request from atst.models.request import Request
from atst.models.request_revision import RequestRevision from atst.models.request_revision import RequestRevision
@ -25,8 +25,12 @@ from atst.models.invitation import Invitation, Status as InvitationStatus
from atst.domain.invitations import Invitations from atst.domain.invitations import Invitations
def random_choice(choices):
return random.choice([k for k, v in choices if k])
def random_service_branch(): def random_service_branch():
return random.choice([k for k, v in SERVICE_BRANCHES if k]) return random_choice(data.SERVICE_BRANCHES)
class Base(factory.alchemy.SQLAlchemyModelFactory): class Base(factory.alchemy.SQLAlchemyModelFactory):
@ -352,9 +356,12 @@ class TaskOrderFactory(Base):
class Meta: class Meta:
model = TaskOrder model = TaskOrder
clin_0001 = random.randrange(100, 100_000) clin_01 = random.randrange(100, 100_000)
clin_0003 = random.randrange(100, 100_000) clin_03 = random.randrange(100, 100_000)
clin_1001 = random.randrange(100, 100_000)
clin_1003 = random.randrange(100, 100_000) defense_component = random_service_branch()
clin_2001 = random.randrange(100, 100_000) app_migration = random_choice(data.APP_MIGRATION)
clin_2003 = random.randrange(100, 100_000) native_apps = "no"
complexity = random_choice(data.PROJECT_COMPLEXITY)
dev_team = random_choice(data.DEV_TEAM)
team_experience = random_choice(data.TEAM_EXPERIENCE)

View File

@ -26,16 +26,9 @@ def test_create_new_workspace(client, user_session):
response = client.post( response = client.post(
url_for("task_orders.update", task_order_id=task_order.id), url_for("task_orders.update", task_order_id=task_order.id),
data={ data={**TaskOrderFactory.dictionary(), "clin_01": 12345, "clin_03": 12345},
"clin_0001": 12345,
"clin_0003": 12345,
"clin_1001": 12345,
"clin_1003": 12345,
"clin_2001": 12345,
"clin_2003": 12345,
},
follow_redirects=False, follow_redirects=False,
) )
assert response.status_code == 200 assert response.status_code == 200
assert task_order.clin_0001 == 12345 assert task_order.clin_01 == 12345