Update to form per design - Part 1 of 2
This commit is contained in:
leigh-mil 2019-01-14 10:12:39 -05:00 committed by GitHub
commit 28b10107e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 381 additions and 164 deletions

View File

@ -0,0 +1,32 @@
"""add funding columns to task order
Revision ID: 4536f50b25bc
Revises: 3d346b5c8f19
Create Date: 2019-01-10 14:24:03.101309
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '4536f50b25bc'
down_revision = '3d346b5c8f19'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('task_orders', sa.Column('attachment_id', postgresql.UUID(as_uuid=True), nullable=True))
op.add_column('task_orders', sa.Column('performance_length', sa.Integer(), nullable=True))
op.create_foreign_key('task_orders_attachments_attachment_id', 'task_orders', 'attachments', ['attachment_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('task_orders_attachments_attachment_id', 'task_orders', type_='foreignkey')
op.drop_column('task_orders', 'performance_length')
op.drop_column('task_orders', 'attachment_id')
# ### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""add oversight columns to task order
Revision ID: 7f2040715b0d
Revises: 4536f50b25bc
Create Date: 2019-01-10 16:34:20.185768
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '7f2040715b0d'
down_revision = '4536f50b25bc'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('task_orders', sa.Column('cor_phone_number', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('ko_phone_number', sa.String(), nullable=True))
op.add_column('task_orders', sa.Column('so_phone_number', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('task_orders', 'so_phone_number')
op.drop_column('task_orders', 'ko_phone_number')
op.drop_column('task_orders', 'cor_phone_number')
# ### end Alembic commands ###

View File

@ -24,8 +24,8 @@ class TaskOrders(object):
"team_experience",
],
"funding": [
"start_date",
"end_date",
"performance_length",
# "pdf",
"clin_01",
"clin_02",
"clin_03",
@ -35,14 +35,17 @@ class TaskOrders(object):
"ko_first_name",
"ko_last_name",
"ko_email",
"ko_phone_number",
"ko_dod_id",
"cor_first_name",
"cor_last_name",
"cor_email",
"cor_phone_number",
"cor_dod_id",
"so_first_name",
"so_last_name",
"so_email",
"so_phone_number",
"so_dod_id",
],
}

View File

@ -176,8 +176,12 @@ FUNDING_TYPES = [
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 "),
("on_premise", "Yes, migrating from an <strong>on-premise data center</strong>"),
("cloud", "Yes, migrating from <strong>another cloud provider</strong>"),
(
"both",
"Yes, migrating from an <strong>on-premise data center</strong> and <strong>another cloud provider</strong>",
),
("none", "Not planning to migrate any applications"),
("not_sure", "Not Sure"),
]
@ -185,7 +189,7 @@ APP_MIGRATION = [
PROJECT_COMPLEXITY = [
("storage", "Storage "),
("data_analytics", "Data Analytics "),
("conus", "CONUS Only Access "),
("conus", "CONUS Access "),
("oconus", "OCONUS Access "),
("tactical_edge", "Tactical Edge Access "),
("not_sure", "Not Sure "),
@ -193,11 +197,10 @@ PROJECT_COMPLEXITY = [
]
DEV_TEAM = [
("government", "Government"),
("civilians", "Civilians"),
("government_civilians", "Government Civilians"),
("military", "Military "),
("contractor", "Contractor "),
("other", "Other"),
("other", "Other (E.g. University or other partner)"),
]
TEAM_EXPERIENCE = [
@ -210,3 +213,30 @@ TEAM_EXPERIENCE = [
"Built or migrated many applications, or consulted on several such projects",
),
]
PERIOD_OF_PERFORMANCE_LENGTH = [
("1", "1 Month"),
("2", "2 Months"),
("3", "3 Months"),
("4", "4 Months"),
("5", "5 Months"),
("6", "6 Months"),
("7", "7 Months"),
("8", "8 Months"),
("9", "9 Months"),
("10", "10 Months"),
("11", "11 Months"),
("12", "1 Year"),
("13", "1 Year, 1 Month"),
("14", "1 Year, 2 Months"),
("15", "1 Year, 3 Months"),
("16", "1 Year, 4 Months"),
("17", "1 Year, 5 Months"),
("18", "1 Year, 6 Months"),
("19", "1 Year, 7 Months"),
("20", "1 Year, 8 Months"),
("21", "1 Year, 9 Months"),
("22", "1 Year, 10 Months"),
("23", "1 Year, 11 Months"),
("24", "2 Years"),
]

View File

@ -6,12 +6,13 @@ from wtforms.fields import (
SelectMultipleField,
StringField,
TextAreaField,
FileField,
)
from wtforms.fields.html5 import DateField
from wtforms.fields.html5 import DateField, TelField
from wtforms.widgets import ListWidget, CheckboxInput
from wtforms.validators import Required, Length
from atst.forms.validators import IsNumber
from atst.forms.validators import IsNumber, PhoneNumber
from .forms import CacheableForm
from .data import (
@ -20,70 +21,84 @@ from .data import (
PROJECT_COMPLEXITY,
DEV_TEAM,
TEAM_EXPERIENCE,
PERIOD_OF_PERFORMANCE_LENGTH,
)
from atst.utils.localization import translate
class AppInfoForm(CacheableForm):
portfolio_name = StringField(
"Organization Portfolio Name",
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",
translate("forms.task_order.portfolio_name_label"),
description=translate("forms.task_order.portfolio_name_description"),
)
scope = TextAreaField(
"Cloud Project Scope",
description="Your team's plan for using the cloud, such as migrating an existing application or creating a prototype.",
translate("forms.task_order.scope_label"),
description=translate("forms.task_order.scope_description"),
)
defense_component = SelectField(
"Department of Defense Component", choices=SERVICE_BRANCHES
translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES
)
app_migration = RadioField(
"App Migration",
description="Do you plan to migrate existing application(s) to the cloud?",
translate("forms.task_order.app_migration_label"),
description=translate("forms.task_order.app_migration_description"),
choices=APP_MIGRATION,
default="",
)
native_apps = RadioField(
"Native Apps",
description="Do you plan to develop application(s) natively in the cloud? ",
translate("forms.task_order.native_apps_label"),
description=translate("forms.task_order.native_apps_description"),
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.)",
translate("forms.task_order.complexity_label"),
description=translate("forms.task_order.complexity_description"),
choices=PROJECT_COMPLEXITY,
default="",
widget=ListWidget(prefix_label=False),
option_widget=CheckboxInput(),
)
complexity_other = StringField("Project Complexity Other")
complexity_other = StringField(translate("forms.task_order.complexity_other_label"))
dev_team = SelectMultipleField(
"Development Team",
description="Which people or teams will be completing the development work for your cloud applications?",
translate("forms.task_order.dev_team_label"),
description=translate("forms.task_order.dev_team_description"),
choices=DEV_TEAM,
default="",
widget=ListWidget(prefix_label=False),
option_widget=CheckboxInput(),
)
dev_team_other = StringField("Development Team Other")
dev_team_other = StringField(translate("forms.task_order.dev_team_other_label"))
team_experience = RadioField(
"Team Experience",
description="How much experience does your team have with development in the cloud?",
translate("forms.task_order.team_experience_label"),
description=translate("forms.task_order.team_experience_description"),
choices=TEAM_EXPERIENCE,
default="",
)
class FundingForm(CacheableForm):
start_date = DateField("Start Date", format="%m/%d/%Y")
end_date = DateField("End Date", format="%m/%d/%Y")
clin_01 = IntegerField("CLIN 01 : Unclassified")
clin_02 = IntegerField("CLIN 02: Classified")
clin_03 = IntegerField("CLIN 03: Unclassified")
clin_04 = IntegerField("CLIN 04: Classified")
performance_length = SelectField(
translate("forms.task_order.performance_length_label"),
choices=PERIOD_OF_PERFORMANCE_LENGTH,
)
start_date = DateField(
translate("forms.task_order.start_date_label"), format="%m/%d/%Y"
)
end_date = DateField(
translate("forms.task_order.end_date_label"), format="%m/%d/%Y"
)
pdf = FileField(
translate("forms.task_order.pdf_label"),
description=translate("forms.task_order.pdf_description"),
)
clin_01 = IntegerField(translate("forms.task_order.clin_01_label"))
clin_02 = IntegerField(translate("forms.task_order.clin_02_label"))
clin_03 = IntegerField(translate("forms.task_order.clin_03_label"))
clin_04 = IntegerField(translate("forms.task_order.clin_04_label"))
class UnclassifiedFundingForm(FundingForm):
clin_02 = IntegerField("CLIN 02: Classified (available soon)")
clin_04 = IntegerField("CLIN 04: Classified (available soon)")
clin_02 = IntegerField(translate("forms.task_order.unclassified_clin_02_label"))
clin_04 = IntegerField(translate("forms.task_order.unclassified_clin_04_label"))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -92,52 +107,56 @@ class UnclassifiedFundingForm(FundingForm):
class OversightForm(CacheableForm):
ko_first_name = StringField("First Name")
ko_last_name = StringField("Last Name")
ko_email = StringField("Email")
ko_first_name = StringField(
translate("forms.task_order.oversight_first_name_label")
)
ko_last_name = StringField(translate("forms.task_order.oversight_last_name_label"))
ko_email = StringField(translate("forms.task_order.oversight_email_label"))
ko_phone_number = TelField(
translate("forms.task_order.oversight_phone_label"), validators=[PhoneNumber()]
)
ko_dod_id = StringField(
"DOD ID", validators=[Required(), Length(min=10), IsNumber()]
translate("forms.task_order.oversight_dod_id_label"),
validators=[Required(), Length(min=10), IsNumber()],
)
cor_first_name = StringField("First Name")
cor_last_name = StringField("Last Name")
cor_email = StringField("Email")
cor_first_name = StringField(
translate("forms.task_order.oversight_first_name_label")
)
cor_last_name = StringField(translate("forms.task_order.oversight_last_name_label"))
cor_email = StringField(translate("forms.task_order.oversight_email_label"))
cor_phone_number = TelField(
translate("forms.task_order.oversight_phone_label"), validators=[PhoneNumber()]
)
cor_dod_id = StringField(
"DOD ID", validators=[Required(), Length(min=10), IsNumber()]
translate("forms.task_order.oversight_dod_id_label"),
validators=[Required(), Length(min=10), IsNumber()],
)
so_first_name = StringField("First Name")
so_last_name = StringField("Last Name")
so_email = StringField("Email")
so_first_name = StringField(
translate("forms.task_order.oversight_first_name_label")
)
so_last_name = StringField(translate("forms.task_order.oversight_last_name_label"))
so_email = StringField(translate("forms.task_order.oversight_email_label"))
so_phone_number = TelField(
translate("forms.task_order.oversight_phone_label"), validators=[PhoneNumber()]
)
so_dod_id = StringField(
"DOD ID", validators=[Required(), Length(min=10), IsNumber()]
translate("forms.task_order.oversight_dod_id_label"),
validators=[Required(), Length(min=10), IsNumber()],
)
ko_invite = BooleanField(
"Invite KO to Task Order Builder",
description="""
Your KO will need to approve funding for this Task Order by logging
into the JEDI Cloud Portal, submitting the Task Order documents
within their official system of record, and electronically signing.
<i>You may choose to skip this for now and invite them later.</i>
""",
translate("forms.task_order.ko_invite_label"),
description=translate("forms.task_order.skip_invite_description"),
)
cor_invite = BooleanField(
"Invite COR to Task Order Builder",
description="""
Your COR may assist with submitting the Task Order documents within
their official system of record. <i>You may choose to skip this for
now and invite them later.</i>
""",
translate("forms.task_order.cor_invite_label"),
description=translate("forms.task_order.skip_invite_description"),
)
so_invite = BooleanField(
"Invite Security Officer to Task Order Builder",
description="""
Your Security Officer will need to answer some security
configuration questions in order to generate a DD-254 document,
then electronically sign. <i>You may choose to skip this for now
and invite them later.</i>
""",
translate("forms.task_order.so_invite_label"),
description=translate("forms.task_order.skip_invite_description"),
)

View File

@ -1,6 +1,14 @@
from enum import Enum
from sqlalchemy import Column, Enum as SQLAEnum, Numeric, String, ForeignKey, Date
from sqlalchemy import (
Column,
Enum as SQLAEnum,
Numeric,
String,
ForeignKey,
Date,
Integer,
)
from sqlalchemy.types import ARRAY
from sqlalchemy.orm import relationship
@ -46,6 +54,9 @@ class TaskOrder(Base, mixins.TimestampsMixin):
team_experience = Column(String) # Team Experience
start_date = Column(Date) # Period of Performance
end_date = Column(Date)
performance_length = Column(Integer)
attachment_id = Column(ForeignKey("attachments.id"))
pdf = relationship("Attachment")
clin_01 = Column(Numeric(scale=2))
clin_02 = Column(Numeric(scale=2))
clin_03 = Column(Numeric(scale=2))
@ -53,14 +64,17 @@ class TaskOrder(Base, mixins.TimestampsMixin):
ko_first_name = Column(String) # First Name
ko_last_name = Column(String) # Last Name
ko_email = Column(String) # Email
ko_phone_number = Column(String) # Phone Number
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_phone_number = Column(String) # Phone Number
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_phone_number = Column(String) # Phone Number
so_dod_id = Column(String) # DOD ID
number = Column(String, unique=True) # Task Order Number
loa = Column(ARRAY(String)) # Line of Accounting (LOA)

View File

@ -20,7 +20,7 @@ from atst.services.invitation import Invitation as InvitationService
TASK_ORDER_SECTIONS = [
{
"section": "app_info",
"title": "What You're Building",
"title": "What You're Making",
"template": "task_orders/new/app_info.html",
"form": task_order_form.AppInfoForm,
},
@ -166,7 +166,13 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
prefix = officer_type["prefix"]
officer_data = {
field: getattr(self.task_order, prefix + "_" + field)
for field in ["first_name", "last_name", "email", "dod_id"]
for field in [
"first_name",
"last_name",
"email",
"phone_number",
"dod_id",
]
}
officer = TaskOrders.add_officer(
self.user, self.task_order, officer_type["role"], officer_data

View File

@ -3,6 +3,7 @@ import { conformToMask } from 'vue-text-mask'
import FormMixin from '../../mixins/form'
import textinput from '../text_input'
import optionsinput from '../options_input'
export default {
name: 'funding',
@ -11,6 +12,7 @@ export default {
components: {
textinput,
optionsinput
},
props: {

View File

@ -6,6 +6,7 @@
label=field.label | striptags,
description=field.description,
tooltip='',
tooltip_title='Help',
placeholder='',
validation='anything',
paragraph=False,
@ -30,7 +31,7 @@
<label for={{field.name}}>
<div class="usa-input__title">
{{ label }}
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
{% if tooltip %}{{ Tooltip(tooltip, tooltip_title) }}{% endif %}
</div>
{% if field.description %}
@ -69,6 +70,7 @@
type='text'
{% if disabled %}
disabled="disabled"
readonly="readonly"
{% endif %}
ref='input'
placeholder='{{ placeholder }}'>

View File

@ -1,6 +1,6 @@
{% from "components/text_input.html" import TextInput %}
{% macro UserInfo(first_name, last_name, email, dod_id) -%}
{% macro UserInfo(first_name, last_name, email, phone) -%}
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(first_name) }}
@ -17,7 +17,7 @@
</div>
<div class='form-col form-col--half'>
{{ TextInput(dod_id, placeholder='1234567890', validation='dodId') }}
{{ TextInput(phone, placeholder='(123) 456-7890', validation='usPhone') }}
</div>
</div>
{% endmacro %}

View File

@ -38,6 +38,7 @@
<div class='action-group'>
<input type='submit' class='usa-button usa-button-primary' value='Save & Continue' />
<input class='usa-button usa-button-secondary' value='Save Draft' />
</div>
{% endblock %}

View File

@ -6,43 +6,34 @@
{% from "components/multi_checkbox_input.html" import MultiCheckboxInput %}
{% block heading %}
What You're Building
{{ "task_orders.new.app_info.section_title"| translate }}
{% endblock %}
{% block form %}
<h3>Basic Information</h3>
<!-- App Info Section -->
<h3>{{ "task_orders.new.app_info.basic_info_title"| translate }}</h3>
{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }}
{{ TextInput(form.scope, paragraph=True) }}
<p>
<i>
Not sure how to describe your scope? <a href="#">Read some Sample Scopes</a> to
get an idea of what is appropriate.
</i>
</p>
<p><i>{{ "task_orders.new.app_info.sample_scope" | translate | safe }}</i></p>
{{ OptionsInput(form.defense_component) }}
<hr>
<h3>About Your Project</h3>
<h3>{{ "task_orders.new.app_info.project_title" | translate }}</h3>
{{ OptionsInput(form.app_migration) }}
{{ OptionsInput(form.native_apps) }}
{{ MultiCheckboxInput(form.complexity, form.complexity_other) }}
<hr>
<h3>About Your Team</h3>
<h3>{{ "task_orders.new.app_info.team_title" | translate }}</h3>
{{ MultiCheckboxInput(form.dev_team, form.dev_team_other) }}
{{ OptionsInput(form.team_experience) }}
<hr>
<h3>Market Research</h3>
<p>
The JEDI Cloud Computing Program Office (CCPO) has completed the market
research requirement for all related task orders. The Department of Defense CIO
has approved this research. <a href="#">View JEDI Cloud Market Research</a>
</p>
<h3>{{ "task_orders.new.app_info.market_research_title" | translate }}</h3>
<p>{{ "task_orders.new.app_info.market_research_paragraph" | translate | safe }}</p>
{% endblock %}

View File

@ -4,8 +4,10 @@
{% from "components/options_input.html" import OptionsInput %}
{% from "components/date_input.html" import DateInput %}
{% from "components/icon.html" import Icon %}
{% block heading %}
Funding
{{ "task_orders.new.funding.section_title" | translate }}
{% endblock %}
{% block form %}
@ -13,65 +15,38 @@
<funding inline-template v-bind:initial-data='{{ form.data|tojson }}'>
<div>
<!-- Get Funding Section -->
<h3>Period of Performance</h3>
<p>Choose the dates your task order will cover.</p>
<p>
Because your funds will be lost if you dont use them, we strongly recommend
submitting small, short-duration task orders, usually a three month period.
Well notify you when your period of performance is nearing the end so you can
request your next set of funds with a new task order.
</p>
{{ DateInput(form.start_date, placeholder='MM / DD / YYYY', validation='date') }}
{{ DateInput(form.end_date, placeholder='MM / DD / YYYY', validation='date') }}
<h3>{{ "task_orders.new.funding.performance_period_title" | translate }}</h3>
<p>{{ "task_orders.new.funding.performance_period_description" | translate }}</p>
<p>{{ "task_orders.new.funding.performance_period_paragraph" | translate }}</p>
{{ OptionsInput(form.performance_length) }}
<hr>
<h3>Cloud Usage Estimate</h3>
<p>
Calculate how much your cloud usage will cost. A technical representative
should help you complete this calculation.
<a href="{{ url_for('atst.jedi_csp_calculator') }}">
Cloud Service Provider's estimate calculator
</a>
</p>
<h4>Upload a copy of your CSP Cost Estimate Research</h4>
<p>
Upload your anticipated cloud usage from the CSP tool linked above. PDFs and
screengrabs of the tool are sufficient.
</p>
<p>
This is only an estimation tool to help you make and informed evaluation of
what you expect to use. While you're tied to the dollar amount you specify in
your task order, you're not obligated by the resources you indicate in the
calculator.
</p>
<input type="file">
<h3>{{ "task_orders.new.funding.estimate_usage_title" | translate }}</h3>
<p>{{ "task_orders.new.funding.estimate_usage_description" | translate }}</p>
<p><a class="icon-link" href="{{ url_for('atst.jedi_csp_calculator') }}">
{{ Icon("link")}} Cloud Service Provider's estimate calculator
</a></p>
<p>{{ "task_orders.new.funding.estimate_usage_paragraph" | translate }}</p>
<div class="usa-input">
{{ form.pdf.label }}
{{ form.pdf.description }}
<input type="file" disabled="disabled" />
</div>
<hr>
<h3>Cloud Usage Calculations</h3>
<p>
Enter the results of your cloud usage calculations. These will correspond with
your task order's period of performance.
</p>
<h4>Cloud Offerings</h4>
<p>
Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings
</p>
{{ TextInput(form.clin_01, validation='dollars') }}
<h3>{{ "task_orders.new.funding.cloud_calculations_title" | translate }}</h3>
<p>{{ "task_orders.new.funding.cloud_calculations_paragraph" | translate }}</p>
<h4>{{ "task_orders.new.funding.cloud_offerings_title" | translate }}</h4>
<p>{{ "task_orders.new.funding.cloud_offerings_paragraph" | translate }}</p>
{{ TextInput(form.clin_01, validation='dollars', placeholder="$0.00") }}
{{ TextInput(form.clin_02, validation='dollars', disabled=(not config.CLASSIFIED)) }}
<h4>Cloud Support and Assistance</h4>
<p>
Technical guidance from the cloud service provider, including architecture,
configuration of IaaS and PaaS, integration, troubleshooting assistance, and
other services.
</p>
{{ TextInput(form.clin_03, validation='dollars', tooltip='The cloud support and assistance packages cannot be used as a primary development resource.') }}
<h4>{{ "task_orders.new.funding.support_assistance_title" | translate }}</h4>
<p>{{ "task_orders.new.funding.support_assistance_paragraph" | translate }}</p>
{{ TextInput(form.clin_03, validation='dollars', tooltip='The cloud support and assistance packages cannot be used as a primary development resource.', placeholder="$0.00") }}
{{ TextInput(form.clin_04, validation='dollars', tooltip='The cloud support and assistance packages cannot be used as a primary development resource.', disabled=(not config.CLASSIFIED)) }}
</div>
</funding>
@ -81,13 +56,10 @@
{% block next %}
<div class="row">
<div class="col col--grow">
<p>Total Task Order Value:<br><span id="to-target"></span></p>
<p>{{ "task_orders.new.funding.total" | translate }}<br><span id="to-target"></span></p>
</div>
<div class="col col--grow">
{{ super() }}
</div>
</div>
{% endblock %}

View File

@ -2,24 +2,43 @@
{% from "components/user_info.html" import UserInfo %}
{% from "components/checkbox_input.html" import CheckboxInput %}
{% from "components/text_input.html" import TextInput %}
{% block heading %}
Oversight
{{ "task_orders.new.oversight.section_title" | translate }}
{% endblock %}
{% block form %}
<!-- Oversight Section -->
<h3>Contracting Officer (KO) Information</h3>
{{ UserInfo(form.ko_first_name, form.ko_last_name, form.ko_email, form.ko_dod_id) }}
<h3>{{ "task_orders.new.oversight.ko_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.ko_info_paragraph" | translate }}</p>
{{ UserInfo(form.ko_first_name, form.ko_last_name, form.ko_email, form.ko_phone_number) }}
{{ CheckboxInput(form.ko_invite) }}
{{ TextInput(form.ko_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}}
<h3>Contractive Officer Representative (COR) Information</h3>
{{ UserInfo(form.cor_first_name, form.cor_last_name, form.cor_email, form.cor_dod_id) }}
<hr />
<h3>{{ "task_orders.new.oversight.cor_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.cor_info_paragraph" | translate }}</p>
<div class='usa-input'>
<fieldset class="usa-input__choices">
<legend>
<input type="checkbox" name="am_cor" id="am_cor" value="y" />
<label for="am_cor">{{ "task_orders.new.oversight.am_cor_label" | translate }}</label>
</legend>
</fieldset>
</div>
{{ UserInfo(form.cor_first_name, form.cor_last_name, form.cor_email, form.cor_phone_number) }}
{{ CheckboxInput(form.cor_invite) }}
{{ TextInput(form.cor_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}}
<h3>Security Officer Information</h3>
{{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_dod_id) }}
<hr />
<h3>{{ "task_orders.new.oversight.so_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.so_info_paragraph" | translate }}</p>
{{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_phone_number) }}
{{ CheckboxInput(form.so_invite) }}
{{ TextInput(form.so_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}}
{% endblock %}

View File

@ -27,7 +27,7 @@
<div class="row">
<div class="col col--grow">
<h3>Period of Performance length {{ TOEditLink(screen=2) }}</h3>
{{ task_order.period or RequiredLabel() }}
{{ task_order.performance_length or RequiredLabel() }}
</div>
<div class="col col--grow">

View File

@ -37,6 +37,10 @@ def random_dod_id():
return "".join(random.choices(string.digits, k=10))
def random_phone_number():
return "".join(random.choices(string.digits, k=10))
def random_future_date(year_min=1, year_max=5):
if year_min == year_max:
inc = year_min
@ -71,9 +75,7 @@ class UserFactory(Base):
last_name = factory.Faker("last_name")
atat_role = factory.LazyFunction(lambda: Roles.get("default"))
dod_id = factory.LazyFunction(random_dod_id)
phone_number = factory.LazyFunction(
lambda: "".join(random.choices(string.digits, k=10))
)
phone_number = factory.LazyFunction(random_phone_number)
service_branch = factory.LazyFunction(random_service_branch)
citizenship = "United States"
designation = "military"
@ -383,16 +385,20 @@ class TaskOrderFactory(Base):
end_date = factory.LazyFunction(
lambda *args: random_future_date(year_min=2, year_max=5)
)
performance_length = random.randint(1, 24)
ko_first_name = factory.Faker("first_name")
ko_last_name = factory.Faker("last_name")
ko_email = factory.Faker("email")
ko_phone_number = factory.LazyFunction(random_phone_number)
ko_dod_id = factory.LazyFunction(random_dod_id)
cor_first_name = factory.Faker("first_name")
cor_last_name = factory.Faker("last_name")
cor_email = factory.Faker("email")
cor_phone_number = factory.LazyFunction(random_phone_number)
cor_dod_id = factory.LazyFunction(random_dod_id)
so_first_name = factory.Faker("first_name")
so_last_name = factory.Faker("last_name")
so_email = factory.Faker("email")
so_phone_number = factory.LazyFunction(random_phone_number)
so_dod_id = factory.LazyFunction(random_dod_id)

View File

@ -224,7 +224,10 @@ def test_task_order_officer_accepts_invite(monkeypatch, client, user_session):
"ko_first_name": user_info["first_name"],
"ko_last_name": user_info["last_name"],
"ko_email": user_info["email"],
"ko_phone_number": user_info["phone_number"],
"ko_dod_id": user_info["dod_id"],
"cor_phone_number": user_info["phone_number"],
"so_phone_number": user_info["phone_number"],
"so_dod_id": task_order.so_dod_id,
"cor_dod_id": task_order.cor_dod_id,
"ko_invite": True,

View File

@ -1,3 +1,13 @@
# How to use text containing html tags in .html files:
# In the template add the `safe` filter when referencing the string
# from the template file. ie:
# login:
# title: A title with <a href="#">a link</a>!
# `{{ "login.title" | translate | safe }}`
audit_log:
events:
default:
@ -153,6 +163,45 @@ forms:
environment_names_required_validation_message: Provide at least one environment name.
environment_names_unique_validation_message: Environment names must be unique.
name_label: Project Name
task_order:
portfolio_name_label: Organization Portfolio Name
portfolio_name_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.
scope_label: Cloud Project Scope
scope_description: Your team's plan for using the cloud, such as migrating an existing application or creating a prototype.
defense_component_label: Department of Defense Component
app_migration_label: App Migration
app_migration_description: Do you plan to migrate existing application(s) to the cloud?
native_apps_label: Native Apps
native_apps_description: Do you plan to develop application(s) natively in the cloud?
complexity_label: Project Complexity
complexity_description: Which of these describes how complex your team's use of the cloud will be? Select all that apply.
complexity_other_label: Project Complexity Other
dev_team_label: Development Team
dev_team_description: Which people or teams will be completing the development work for your cloud applications? Select all that apply.
dev_team_other_label: Development Team Other
team_experience_label: Team Experience
team_experience_description: How much experience does your team have with development in the cloud?
performance_length_label: Period of Performance length
start_date_label: Start Date
end_date_label: End Date
pdf_label: Upload a copy of your CSP Cost Estimate Research
pdf_description: Upload a PDF or screenshot of your usage estimate from the calculator.
clin_01_label: 'CLIN 01 : Unclassified'
clin_02_label: 'CLIN 02: Classified'
clin_03_label: 'CLIN 03: Unclassified'
clin_04_label: 'CLIN 04: Classified'
unclassified_clin_02_label: 'CLIN 02: Classified (available soon)'
unclassified_clin_04_label: 'CLIN 04: Classified (available soon)'
oversight_first_name_label: First Name
oversight_last_name_label: Last Name
oversight_email_label: Email
oversight_phone_label: Phone Number
oversight_dod_id_label: DoD ID
ko_invite_label: Invite KO to Task Order Builder
cor_invite_label: Invite COR to Task Order Builder
so_invite_label: Invite Security Officer to Task Order Builder
skip_invite_description: |
<i>You may choose to skip this for now and invite them later.</i>
validators:
is_number_message: Please enter a valid number.
list_item_required_message: Please provide at least one.
@ -295,6 +344,42 @@ requests:
num_software_systems_tooltip: 'A software system can be any code that you plan to host on cloud infrastructure. For example, it could be a custom-developed web application, or a large ERP system.'
questions_title_text: Questions related to JEDI Cloud migration
rationalization_software_systems_tooltip: Rationalization is the DoD process to determine whether the application should move to the cloud.
task_orders:
new:
app_info:
section_title: "What You're Making"
basic_info_title: Basic information
sample_scope: |
Not sure how to describe your scope? <a href="#">Read some Sample Scopes</a> to get an idea of what is appropriate.
project_title: About your project
team_title: About your team
market_research_title: Market Research
market_research_paragraph: 'The JEDI Cloud Computing Program Office (CCPO) has completed the market research requirements for all related task orders. The Department of Defense CIO has approved this research.<br /><a href="#">View JEDI Market Research Memo</a>'
funding:
section_title: Funding
performance_period_title: Period of Performance
performance_period_description: Choose the length of time your task order will cover.
performance_period_paragraph: Because your funds will be lost if you dont use them, we strongly recommend submitting small, short-duration task orders, usually a three month period. Well notify you when your period of performance is nearing the end so you can request your next set of funds with a new task order.
estimate_usage_title: Estimate Your Cloud Usage
estimate_usage_description: Calculate how much your cloud usage will cost. A technical representative should help you complete this calculation. These calculations will become your CLINs.
estimate_usage_paragraph: This is only an estimation tool to help you make an informed evaluation of what you expect to use. While you're tied to the dollar amount you specify in your task order, you're not obligated by the resources you indicate in the calculator.
cloud_calculations_title: Cloud Usage Calculations
cloud_calculations_paragraph: Enter the results of your cloud usage calculations.
cloud_offerings_title: Cloud Offerings
cloud_offerings_paragraph: Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings
support_assistance_title: Cloud Support and Assistance
support_assistance_paragraph: Technical guidance from the cloud service provider, including architecture, configuration of IaaS and PaaS, integration, troubleshooting assistance, and other services.
total: 'Total Task Order Value:'
oversight:
section_title: Oversight
ko_info_title: Contracting Officer (KO) Information
ko_info_paragraph: Your KO will need to approve funding for this Task Order by logging into the JEDI Cloud Portal, submitting the Task Order documents within their official system of record, and electronically signing. You might want to work with your program Financial Manager to get your TO documents moving in the right dirction.
skip_ko_label: "Skip for now (We'll remind you to enter one later)"
cor_info_title: Contractive Officer Representative (COR) Information
cor_info_paragraph: Your COR may assist in submitting the Task Order documents within thier official system of record. They may also be invited to log in an manage the Task Order entry within the JEDI Cloud portal.
am_cor_label: I am the Contracting Officer Representative (COR) for this Task Order
so_info_title: Security Officer Information
so_info_paragraph: our Security Officer will need to answer some security configuration questions in order to generate a DD-254 document, then electronically sign.
testing:
example_string: Hello World
example_with_variables: 'Hello, {name}!'