Merge pull request #543 from dod-ccpo/update-to-form-part-2

Update Task Order Form - Part 2
This commit is contained in:
leigh-mil 2019-01-17 10:53:43 -05:00 committed by GitHub
commit 9af74a8a53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 402 additions and 201 deletions

View File

@ -1,6 +1,6 @@
import re import re
import datetime import datetime
from atst.utils.localization import translate from atst.utils.localization import translate, translate_duration
from flask import current_app as app, render_template from flask import current_app as app, render_template
from jinja2 import contextfilter from jinja2 import contextfilter
from jinja2.exceptions import TemplateNotFound from jinja2.exceptions import TemplateNotFound
@ -97,6 +97,19 @@ def renderAuditEvent(event):
return render_template("audit_log/events/default.html", event=event) return render_template("audit_log/events/default.html", event=event)
def removeHtml(text):
html_tags = re.compile("<.*?>")
return re.sub(html_tags, "", text)
def normalizeOrder(title):
# reorders titles from "Army, Department of the" to "Department of the Army"
text = title.split(", ")
reordered_text = text[0:-1]
reordered_text.insert(0, text[-1])
return " ".join(reordered_text)
def register_filters(app): def register_filters(app):
app.jinja_env.filters["iconSvg"] = iconSvg app.jinja_env.filters["iconSvg"] = iconSvg
app.jinja_env.filters["dollars"] = dollars app.jinja_env.filters["dollars"] = dollars
@ -110,6 +123,9 @@ def register_filters(app):
app.jinja_env.filters["dateFromString"] = dateFromString app.jinja_env.filters["dateFromString"] = dateFromString
app.jinja_env.filters["pageWindow"] = pageWindow app.jinja_env.filters["pageWindow"] = pageWindow
app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent
app.jinja_env.filters["removeHtml"] = removeHtml
app.jinja_env.filters["normalizeOrder"] = normalizeOrder
app.jinja_env.filters["translateDuration"] = translate_duration
@contextfilter @contextfilter
def translateWithoutCache(context, *kwargs): def translateWithoutCache(context, *kwargs):

View File

@ -1,4 +1,6 @@
from atst.domain.roles import PORTFOLIO_ROLES as PORTFOLIO_ROLE_DEFINITIONS from atst.domain.roles import PORTFOLIO_ROLES as PORTFOLIO_ROLE_DEFINITIONS
from atst.utils.localization import translate, translate_duration
SERVICE_BRANCHES = [ SERVICE_BRANCHES = [
("", "Select an option"), ("", "Select an option"),
@ -176,67 +178,41 @@ FUNDING_TYPES = [
TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")] TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")]
APP_MIGRATION = [ APP_MIGRATION = [
("on_premise", "Yes, migrating from an <strong>on-premise data center</strong>"), ("on_premise", translate("forms.task_order.app_migration.on_premise")),
("cloud", "Yes, migrating from <strong>another cloud provider</strong>"), ("cloud", translate("forms.task_order.app_migration.cloud")),
( ("both", translate("forms.task_order.app_migration.both")),
"both", ("none", translate("forms.task_order.app_migration.none")),
"Yes, migrating from an <strong>on-premise data center</strong> and <strong>another cloud provider</strong>", ("not_sure", translate("forms.task_order.app_migration.not_sure")),
),
("none", "Not planning to migrate any applications"),
("not_sure", "Not Sure"),
] ]
APPLICATION_COMPLEXITY = [ APPLICATION_COMPLEXITY = [
("storage", "Storage "), ("storage", translate("forms.task_order.complexity.storage")),
("data_analytics", "Data Analytics "), ("data_analytics", translate("forms.task_order.complexity.data_analytics")),
("conus", "CONUS Access "), ("conus", translate("forms.task_order.complexity.conus")),
("oconus", "OCONUS Access "), ("oconus", translate("forms.task_order.complexity.oconus")),
("tactical_edge", "Tactical Edge Access "), ("tactical_edge", translate("forms.task_order.complexity.tactical_edge")),
("not_sure", "Not Sure "), ("not_sure", translate("forms.task_order.complexity.not_sure")),
("other", "Other"), ("other", translate("forms.task_order.complexity.other")),
] ]
DEV_TEAM = [ DEV_TEAM = [
("government_civilians", "Government Civilians"), (
("military", "Military "), "government_civilians",
("contractor", "Contractor "), translate("forms.task_order.dev_team.government_civilians"),
("other", "Other (E.g. University or other partner)"), ),
("military", translate("forms.task_order.dev_team.military")),
("contractor", translate("forms.task_order.dev_team.contractor")),
("other", translate("forms.task_order.dev_team.other")),
] ]
TEAM_EXPERIENCE = [ TEAM_EXPERIENCE = [
("none", "No previous experience"), ("none", translate("forms.task_order.team_experience.none")),
("planned", "Researched or planned a cloud build or migration"), ("planned", translate("forms.task_order.team_experience.planned")),
("built_1", "Built or Migrated 1-2 applications"), ("built_1", translate("forms.task_order.team_experience.built_1")),
("built_3", "Built or Migrated 3-5 applications"), ("built_3", translate("forms.task_order.team_experience.built_3")),
( ("built_many", translate("forms.task_order.team_experience.built_many")),
"built_many",
"Built or migrated many applications, or consulted on several such applications",
),
] ]
PERIOD_OF_PERFORMANCE_LENGTH = [ PERIOD_OF_PERFORMANCE_LENGTH = [
("1", "1 Month"), (str(x + 1), translate_duration(x + 1)) for x in range(24)
("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

@ -39,8 +39,8 @@ class AppInfoForm(CacheableForm):
translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES
) )
app_migration = RadioField( app_migration = RadioField(
translate("forms.task_order.app_migration_label"), translate("forms.task_order.app_migration.label"),
description=translate("forms.task_order.app_migration_description"), description=translate("forms.task_order.app_migration.description"),
choices=APP_MIGRATION, choices=APP_MIGRATION,
default="", default="",
) )
@ -50,8 +50,8 @@ class AppInfoForm(CacheableForm):
choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")], choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")],
) )
complexity = SelectMultipleField( complexity = SelectMultipleField(
translate("forms.task_order.complexity_label"), translate("forms.task_order.complexity.label"),
description=translate("forms.task_order.complexity_description"), description=translate("forms.task_order.complexity.description"),
choices=APPLICATION_COMPLEXITY, choices=APPLICATION_COMPLEXITY,
default="", default="",
widget=ListWidget(prefix_label=False), widget=ListWidget(prefix_label=False),
@ -59,8 +59,8 @@ class AppInfoForm(CacheableForm):
) )
complexity_other = StringField(translate("forms.task_order.complexity_other_label")) complexity_other = StringField(translate("forms.task_order.complexity_other_label"))
dev_team = SelectMultipleField( dev_team = SelectMultipleField(
translate("forms.task_order.dev_team_label"), translate("forms.task_order.dev_team.label"),
description=translate("forms.task_order.dev_team_description"), description=translate("forms.task_order.dev_team.description"),
choices=DEV_TEAM, choices=DEV_TEAM,
default="", default="",
widget=ListWidget(prefix_label=False), widget=ListWidget(prefix_label=False),
@ -68,8 +68,8 @@ class AppInfoForm(CacheableForm):
) )
dev_team_other = StringField(translate("forms.task_order.dev_team_other_label")) dev_team_other = StringField(translate("forms.task_order.dev_team_other_label"))
team_experience = RadioField( team_experience = RadioField(
translate("forms.task_order.team_experience_label"), translate("forms.task_order.team_experience.label"),
description=translate("forms.task_order.team_experience_description"), description=translate("forms.task_order.team_experience.description"),
choices=TEAM_EXPERIENCE, choices=TEAM_EXPERIENCE,
default="", default="",
) )
@ -77,7 +77,7 @@ class AppInfoForm(CacheableForm):
class FundingForm(CacheableForm): class FundingForm(CacheableForm):
performance_length = SelectField( performance_length = SelectField(
translate("forms.task_order.performance_length_label"), translate("forms.task_order.performance_length.label"),
choices=PERIOD_OF_PERFORMANCE_LENGTH, choices=PERIOD_OF_PERFORMANCE_LENGTH,
) )
start_date = DateField( start_date = DateField(

View File

@ -1,6 +1,8 @@
import yaml import yaml
import os import os
from functools import lru_cache from functools import lru_cache
import math
from gettext import ngettext
from flask import current_app as app from flask import current_app as app
from atst.utils import getattr_path from atst.utils import getattr_path
@ -41,3 +43,14 @@ def translate(key, variables=None):
raise LocalizationInvalidKeyError(key, variables) raise LocalizationInvalidKeyError(key, variables)
return value.format(**variables).replace("\n", "") return value.format(**variables).replace("\n", "")
def translate_duration(duration_in_months):
duration = []
years = math.floor(duration_in_months / 12)
months = duration_in_months % 12
if years > 0:
duration.append("{} {}".format(years, ngettext("year", "years", years)))
if months > 0:
duration.append("{} {}".format(months, ngettext("month", "months", months)))
return (", ").join(duration)

View File

@ -38,6 +38,7 @@
@import 'components/budget_chart'; @import 'components/budget_chart';
@import 'components/audit_log'; @import 'components/audit_log';
@import 'components/usa_banner'; @import 'components/usa_banner';
@import 'components/checklist';
@import 'sections/login'; @import 'sections/login';
@import 'sections/home'; @import 'sections/home';

View File

@ -0,0 +1,9 @@
.checklist {
padding-left: 0;
list-style: none;
margin-top: 5px;
li {
margin-bottom: 0;
}
}

View File

@ -68,4 +68,8 @@
opacity: 0.3; opacity: 0.3;
pointer-events: none; pointer-events: none;
} }
&.icon-link--left {
padding-left: 0;
}
} }

View File

@ -55,7 +55,19 @@
@include icon-size(24); @include icon-size(24);
} }
&.icon--remove { &.icon--remove, &.icon--red {
@include icon-color($color-red); @include icon-color($color-red);
} }
&.icon--green {
@include icon-color($color-green);
}
&.icon--gray {
@include icon-color($color-gray);
}
&.icon--medium {
@include icon-size(12);
}
} }

View File

@ -179,3 +179,40 @@
} }
} }
} }
.task-order-form {
.task-order-form__heading {
margin-bottom: 0;
&.inactive {
color: $color-gray-light;
}
&.subheading {
color: $color-gray;
}
}
.funding-summary__table {
tr td {
border-bottom: 0;
padding: 0.5rem 1.5rem;
.funding-summary__td {
margin-top: 0;
}
}
}
.task-order-invite-message {
&.not-sent {
color: $color-red;
font-weight: $font-bold;
}
&.sent {
color: $color-green;
font-weight: $font-bold;
}
}
}

View File

@ -33,10 +33,10 @@
<li> <li>
{% if choice[0] != 'other' %} {% if choice[0] != 'other' %}
<input type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ loop.index0 }}' value='{{ choice[0] }}' v-model="selections"/> <input type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ loop.index0 }}' value='{{ choice[0] }}' v-model="selections"/>
<label for='{{ field.name }}-{{ loop.index0 }}'>{{ choice[1] }}</label> <label for='{{ field.name }}-{{ loop.index0 }}'>{{ choice[1] | safe }}</label>
{% else %} {% else %}
<input @click="otherToggle" type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ loop.index0 }}' value='other' v-model="selections"/> <input @click="otherToggle" type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ loop.index0 }}' value='other' v-model="selections"/>
<label for='{{ field.name }}-{{ loop.index0 }}'>{{ choice[1] }}</label> <label for='{{ field.name }}-{{ loop.index0 }}'>{{ choice[1] | safe }}</label>
<div v-show="otherChecked"> <div v-show="otherChecked">
<input type='text' name='{{ other_input_field.name}}' id='{{ field.name }}-other' v-model:value="otherText" aria-expanded='false' /> <input type='text' name='{{ other_input_field.name}}' id='{{ field.name }}-other' v-model:value="otherText" aria-expanded='false' />

View File

@ -2,7 +2,7 @@
{% block content %} {% block content %}
<div class="col"> <div class="col task-order-form">
{% include 'task_orders/new/menu.html' %} {% include 'task_orders/new/menu.html' %}
@ -19,8 +19,10 @@
<div class="panel"> <div class="panel">
<div class="panel__heading"> <div class="panel__heading">
<div class="subtitle h2">Task Order Builder</div> <h1 class="task-order-form__heading subheading">
<h1>{% block heading %}{% endblock %}</h1> <div class="h2">Task Order Builder</div>
{% block heading %}{% endblock %}
</h1>
</div> </div>
<div class="panel__content"> <div class="panel__content">

View File

@ -12,7 +12,7 @@
{% block form %} {% block form %}
<!-- App Info Section --> <!-- App Info Section -->
<h3>{{ "task_orders.new.app_info.basic_info_title"| translate }}</h3> <h3 class="task-order-form__heading subheading">{{ "task_orders.new.app_info.basic_info_title"| translate }}</h3>
{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }} {{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }}
{{ TextInput(form.scope, paragraph=True) }} {{ TextInput(form.scope, paragraph=True) }}
<p><i>{{ "task_orders.new.app_info.sample_scope" | translate | safe }}</i></p> <p><i>{{ "task_orders.new.app_info.sample_scope" | translate | safe }}</i></p>
@ -20,20 +20,20 @@
<hr> <hr>
<h3>{{ "task_orders.new.app_info.project_title" | translate }}</h3> <h3 id="reporting" class="subheading">{{ "task_orders.new.app_info.project_title" | translate }}</h3>
{{ OptionsInput(form.app_migration) }} {{ OptionsInput(form.app_migration) }}
{{ OptionsInput(form.native_apps) }} {{ OptionsInput(form.native_apps) }}
{{ MultiCheckboxInput(form.complexity, form.complexity_other) }} {{ MultiCheckboxInput(form.complexity, form.complexity_other) }}
<hr> <hr>
<h3>{{ "task_orders.new.app_info.team_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.app_info.team_title" | translate }}</h3>
{{ MultiCheckboxInput(form.dev_team, form.dev_team_other) }} {{ MultiCheckboxInput(form.dev_team, form.dev_team_other) }}
{{ OptionsInput(form.team_experience) }} {{ OptionsInput(form.team_experience) }}
<hr> <hr>
<h3>{{ "task_orders.new.app_info.market_research_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.app_info.market_research_title" | translate }}</h3>
<p>{{ "task_orders.new.app_info.market_research_paragraph" | translate | safe }}</p> <p>{{ "task_orders.new.app_info.market_research_paragraph" | translate | safe }}</p>
{% endblock %} {% endblock %}

View File

@ -15,14 +15,14 @@
<funding inline-template v-bind:initial-data='{{ form.data|tojson }}'> <funding inline-template v-bind:initial-data='{{ form.data|tojson }}'>
<div> <div>
<!-- Get Funding Section --> <!-- Get Funding Section -->
<h3>{{ "task_orders.new.funding.performance_period_title" | translate }}</h3> <h3 class="subheading">{{ "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_description" | translate }}</p>
<p>{{ "task_orders.new.funding.performance_period_paragraph" | translate }}</p> <p>{{ "task_orders.new.funding.performance_period_paragraph" | translate }}</p>
{{ OptionsInput(form.performance_length) }} {{ OptionsInput(form.performance_length) }}
<hr> <hr>
<h3>{{ "task_orders.new.funding.estimate_usage_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.funding.estimate_usage_title" | translate }}</h3>
<p>{{ "task_orders.new.funding.estimate_usage_description" | translate }}</p> <p>{{ "task_orders.new.funding.estimate_usage_description" | translate }}</p>
<p><a class="icon-link" href="{{ url_for('atst.jedi_csp_calculator') }}"> <p><a class="icon-link" href="{{ url_for('atst.jedi_csp_calculator') }}">
{{ Icon("link")}} Cloud Service Provider's estimate calculator {{ Icon("link")}} Cloud Service Provider's estimate calculator
@ -36,15 +36,15 @@
<hr> <hr>
<h3>{{ "task_orders.new.funding.cloud_calculations_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.funding.cloud_calculations_title" | translate }}</h3>
<p>{{ "task_orders.new.funding.cloud_calculations_paragraph" | translate }}</p> <p>{{ "task_orders.new.funding.cloud_calculations_paragraph" | translate }}</p>
<h4>{{ "task_orders.new.funding.cloud_offerings_title" | translate }}</h4> <h4 class="task-order-form__heading">{{ "task_orders.new.funding.cloud_offerings_title" | translate }}</h4>
<p>{{ "task_orders.new.funding.cloud_offerings_paragraph" | translate }}</p> <p>{{ "task_orders.new.funding.cloud_offerings_paragraph" | translate }}</p>
{{ TextInput(form.clin_01, validation='dollars', placeholder="$0.00") }} {{ TextInput(form.clin_01, validation='dollars', placeholder="$0.00") }}
{{ TextInput(form.clin_02, validation='dollars', disabled=(not config.CLASSIFIED)) }} {{ TextInput(form.clin_02, validation='dollars', disabled=(not config.CLASSIFIED)) }}
<h4>{{ "task_orders.new.funding.support_assistance_title" | translate }}</h4> <h4 class="task-order-form__heading">{{ "task_orders.new.funding.support_assistance_title" | translate }}</h4>
<p>{{ "task_orders.new.funding.support_assistance_paragraph" | translate }}</p> <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_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)) }} {{ 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)) }}
@ -56,7 +56,7 @@
{% block next %} {% block next %}
<div class="row"> <div class="row">
<div class="col col--grow"> <div class="col col--grow">
<p>{{ "task_orders.new.funding.total" | translate }}<br><span id="to-target"></span></p> <p><strong><span class="task-order-form__heading subheading">{{ "task_orders.new.funding.total" | translate }}</span><br><span id="to-target"></span></strong></p>
</div> </div>
<div class="col col--grow"> <div class="col col--grow">
{{ super() }} {{ super() }}

View File

@ -11,7 +11,7 @@
{% block form %} {% block form %}
<!-- Oversight Section --> <!-- Oversight Section -->
<h3>{{ "task_orders.new.oversight.ko_info_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.oversight.ko_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.ko_info_paragraph" | translate }}</p> <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) }} {{ UserInfo(form.ko_first_name, form.ko_last_name, form.ko_email, form.ko_phone_number) }}
{{ CheckboxInput(form.ko_invite) }} {{ CheckboxInput(form.ko_invite) }}
@ -19,7 +19,7 @@
<hr /> <hr />
<h3>{{ "task_orders.new.oversight.cor_info_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.oversight.cor_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.cor_info_paragraph" | translate }}</p> <p>{{ "task_orders.new.oversight.cor_info_paragraph" | translate }}</p>
<div class='usa-input'> <div class='usa-input'>
<fieldset class="usa-input__choices"> <fieldset class="usa-input__choices">
@ -35,7 +35,7 @@
<hr /> <hr />
<h3>{{ "task_orders.new.oversight.so_info_title" | translate }}</h3> <h3 class="subheading">{{ "task_orders.new.oversight.so_info_title" | translate }}</h3>
<p>{{ "task_orders.new.oversight.so_info_paragraph" | translate }}</p> <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) }} {{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_phone_number) }}
{{ CheckboxInput(form.so_invite) }} {{ CheckboxInput(form.so_invite) }}

View File

@ -5,145 +5,219 @@
{% from "components/icon.html" import Icon %} {% from "components/icon.html" import Icon %}
{% block heading %} {% block heading %}
Review & Download {{ "task_orders.new.review.section_title"| translate }}
{% endblock %} {% endblock %}
{% block form %} {% block form %}
{% macro TOEditLink(screen=1) %} {% macro TOEditLink(screen=1, anchor=None) %}
{% if task_order %} {% if task_order %}
{{ EditLink(url_for("task_orders.new", screen=screen, task_order_id=task_order.id)) }} {{ EditLink(url_for("task_orders.new", screen=screen, task_order_id=task_order.id, _anchor=anchor)) }}
{% else %} {% else %}
{{ EditLink(url_for("task_orders.new", screen=screen)) }} {{ EditLink(url_for("task_orders.new", screen=screen, _anchor=anchor)) }}
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
<section> {% if task_order.defense_component %}
<h3>Scope (Statement of Work) {{ TOEditLink() }}</h3> {% set defense_component_description = task_order.defense_component | normalizeOrder %}
<p> {% endif %}
{{ task_order.scope or RequiredLabel() }}
</p>
<div class="row"> {% if task_order.app_migration %}
{% set app_migration_description = "forms.task_order.app_migration.{}".format(task_order.app_migration) | translate | removeHtml %}
{% endif %}
{% if task_order.native_apps %}
{% set native_apps_description = "task_orders.new.review.{}_native".format(task_order.native_apps) | translate %}
{% endif %}
{% if task_order.team_experience %}
{% set team_experience_description = "forms.task_order.team_experience.{}".format(task_order.team_experience) | translate %}
{% endif %}
<h3 class="subheading">{{ "task_orders.new.review.app_info"| translate }} {{ TOEditLink(screen=1) }}</h3>
<div class="row">
<div class="col col--grow"> <div class="col col--grow">
<h3>Period of Performance length {{ TOEditLink(screen=2) }}</h3> <h4 class='task-order-form__heading'>{{ "task_orders.new.review.portfolio"| translate }}</h4>
{{ task_order.performance_length or RequiredLabel() }} <p>{{ task_order.portfolio_name or RequiredLabel() }}</p>
</div> </div>
<div class="col col--grow"> <div class="col col--grow">
<h3>Total funding requested {{ TOEditLink(screen=2) }}</h3> <h4 class='task-order-form__heading'>{{ "task_orders.new.review.dod"| translate }}</h4>
{{ task_order.budget }} <p>{{ defense_component_description or RequiredLabel() }}</p>
</div> </div>
</div> </div>
</section>
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.scope"| translate }}</h4>
<p>
{{ task_order.scope or RequiredLabel() }}
</p>
<hr> <hr>
<section> <h3 class="subheading">{{ "task_orders.new.review.reporting"| translate }} {{ TOEditLink(screen=1, anchor="reporting") }}</h3>
<h2>Generated Documents</h2>
<ul class="usa-unstyled-list"> <div class="row">
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.migration"| translate }}</h4>
<p>{{ app_migration_description or RequiredLabel() }}</p>
</div>
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.native_apps"| translate }}</h4>
<p>{{ native_apps_description or RequiredLabel() }}</p>
</div>
</div>
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.complexity"| translate }}</h4>
{% if task_order.complexity %}
<ul class="checklist">
{% for item in task_order.complexity %}
<li> <li>
<a href="#" download> {{ Icon('ok', classes='icon--gray icon--medium') }}{{ "forms.task_order.complexity.{}".format(item) | translate }}{% if item == 'other' %}: {{ task_order.complexity_other }}{% endif %}
{{ Icon('download') }}
Cover Sheet
</a>
</li>
<li>
<a href="#" download>
{{ Icon('download') }}
Market Research
</a>
</li
>
{% if task_order %}
<li>
<a href="{{ url_for('task_orders.download_summary', task_order_id=task_order.id) }}" download>
{{ Icon('download') }}
Task Order Draft
</a>
</li>
{% endif %}
<li>
<a href="#" download>
{{ Icon('download') }}
DD 254
</a>
</li> </li>
{% endfor %}
</ul> </ul>
</section> {% else %}
<p>{{ RequiredLabel() }}</p>
{% endif %}
<div class="row">
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.team"| translate }}</h4>
{% if task_order.dev_team %}
<ul class="checklist">
{% for item in task_order.dev_team %}
<li>
{% if item == 'other' %}
{{ Icon('ok', classes='icon--gray icon--medium') }}Other: {{ task_order.dev_team_other }}
{% else %}
{{ Icon('ok', classes='icon--gray icon--medium') }}{{ "forms.task_order.dev_team.{}".format(item) | translate }}
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>{{ RequiredLabel() }}</p>
{% endif %}
</div>
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.experience"| translate }}</h4>
<p>{{ team_experience_description or RequiredLabel() }}</p>
</div>
</div>
<hr> <hr>
<section> <h3 class="subheading">{{ "task_orders.new.review.funding"| translate }} {{ TOEditLink(screen=2) }}</h3>
<h2>Invite Signatories/Collaborators</h2>
<div class="form-row"> <div class="row">
<div class="form-col"> <div class="col col--grow">
<div class="usa-input"> <h4 class='task-order-form__heading'>{{ "task_orders.new.review.performance_period"| translate }}</h4>
<fieldset class="usa-input__choices"> {{ task_order.performance_length | translateDuration or RequiredLabel() }}
<legend>Financial Oversight</legend> <p><a href="#" class='icon-link icon-link--left' download>{{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }}</a></p>
<p> </div>
{% if task_order.ko_first_name %}
{{ task_order.ko_first_name }} <div class="col col--grow">
{{ task_order.ko_last_name }} <table class="funding-summary__table">
{% else %} <tbody>
{{ RequiredLabel() }} <tr>
<td><h4>{{ "task_orders.new.review.to_value"| translate }}</h4></td>
<td class="table-cell--align-right">{{ '${:,.2f}'.format(task_order.budget) }}</td>
</tr>
<tr>
<td><h4 class='task-order-form__heading funding-summary__td'>{{ "task_orders.new.review.clin_1"| translate }}</h4></td>
<td class="table-cell--align-right">{{ '${:,.2f}'.format(task_order.clin_01) }}</td>
</tr>
<tr>
<td><h4 class="task-order-form__heading funding-summary__td{% if not config.CLASSIFIED %} inactive{% endif %}">
{{ "task_orders.new.review.clin_2"| translate }}
{% if not config.CLASSIFIED %}
<div>{{ "task_orders.new.review.classified_inactive"| translate }}</div>
{% endif %} {% endif %}
(Contracting Officer) </h4></td>
</p> <td class="table-cell--align-right">
<p> {% if config.CLASSIFIED %}
{% if task_order.ko_first_name %} {{ '${:,.2f}'.format(task_order.clin_02) }}
{{ task_order.cor_first_name }}
{{ task_order.cor_last_name }}
{% else %}
{{ RequiredLabel() }}
{% endif %} {% endif %}
(Contracting Officer Representative) </td>
</p> </tr>
</fieldset> <tr>
</div> <td><h4 class='task-order-form__heading funding-summary__td'>{{ "task_orders.new.review.clin_3"| translate }}</h4></td>
</div> <td class="table-cell--align-right">{{ '${:,.2f}'.format(task_order.clin_03) }}</td>
<div class="form-col"> </tr>
<div class="usa-input"> <tr>
<fieldset class="usa-input__choices"> <td><h4 class="task-order-form__heading funding-summary__td{% if not config.CLASSIFIED %} inactive{% endif %}">
<legend>Invite?</legend> {{ "task_orders.new.review.clin_4"| translate }}
</fieldset> {% if not config.CLASSIFIED %}
</div> <div>{{ "task_orders.new.review.classified_inactive"| translate }}</div>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="usa-input">
<fieldset class="usa-input__choices">
<legend>Security Officer</legend>
<p>
{% if task_order.so_first_name %}
{{ task_order.so_first_name }}
{{ task_order.so_last_name }}
{% else %}
{{ RequiredLabel() }}
{% endif %} {% endif %}
(Security Officer) </h4></td>
</p> <td class="table-cell--align-right">
</fieldset> {% if config.CLASSIFIED %}
{{ '${:,.2f}'.format(task_order.clin_04) }}
{% endif %}
</td>
<tr>
</tbody>
</table>
</div> </div>
</div>
<hr>
<h3 class="subheading">{{ "task_orders.new.review.oversight"| translate }} {{ TOEditLink(screen=3) }}</h3>
<div class="row">
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.ko"| translate }}</h4>
{{ task_order.ko_first_name }} {{ task_order.ko_last_name }}<br>
{{ task_order.ko_email }}<br>
{{ task_order.ko_phone_number | usPhone }}<br>
{{ "task_orders.new.review.dod_id"| translate }} {{ task_order.ko_dod_id}}<br>
{% if task_order.ko_invite %}
{{ Icon('ok', classes='icon--green') }} <span class="task-order-invite-message sent">{{ "task_orders.new.review.invited"| translate }}</<span>
{% else %}
{{ Icon('alert', classes='icon--red') }} <span class="task-order-invite-message not-sent">{{ "task_orders.new.review.not_invited"| translate }}</span>
{% endif %}
</div> </div>
<div class="form-col">
<div class="usa-input"> <div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.cor"| translate }}</h4>
{{ task_order.cor_first_name }} {{ task_order.cor_last_name }}<br>
{{ task_order.cor_email }}<br>
{{ task_order.cor_phone_number | usPhone }}<br>
{{ "task_orders.new.review.dod_id"| translate }} {{ task_order.cor_dod_id}}<br>
{% if task_order.cor_invite %}
{{ Icon('ok', classes='icon--green') }} <span class="task-order-invite-message sent">{{ "task_orders.new.review.invited"| translate }}</<span>
{% else %}
{{ Icon('alert', classes='icon--red') }} <span class="task-order-invite-message not-sent">{{ "task_orders.new.review.not_invited"| translate }}</span>
{% endif %}
</div> </div>
</div>
<div class="row">
<div class="col col--grow">
<h4 class='task-order-form__heading'>{{ "task_orders.new.review.so"| translate }}</h4>
{{ task_order.so_first_name }} {{ task_order.so_last_name }}<br>
{{ task_order.so_email }}<br>
{{ task_order.so_phone_number | usPhone }}<br>
{{ "task_orders.new.review.dod_id"| translate }} {{ task_order.so_dod_id}}<br>
{% if task_order.so_invite %}
{{ Icon('ok', classes='icon--green') }} <span class="task-order-invite-message sent">{{ "task_orders.new.review.invited"| translate }}</<span>
{% else %}
{{ Icon('alert', classes='icon--red') }} <span class="task-order-invite-message not-sent">{{ "task_orders.new.review.not_invited"| translate }}</span>
{% endif %}
</div> </div>
</div> </div>
</section>
{% endblock %} {% endblock %}
{% block next %} {% block next %}
<div class='action-group'> <div class='action-group'>
<input type='submit' class='usa-button usa-button-primary' value='Send Invitations' /> <input type='submit' class='usa-button usa-button-primary' value='Done' />
</div> </div>
{% endblock %} {% endblock %}

View File

@ -388,7 +388,7 @@ class TaskOrderFactory(Base):
defense_component = factory.LazyFunction(random_service_branch) defense_component = factory.LazyFunction(random_service_branch)
app_migration = random_choice(data.APP_MIGRATION) app_migration = random_choice(data.APP_MIGRATION)
native_apps = random.choices(["yes", "no", "not_sure"]) native_apps = random.choice(["yes", "no", "not_sure"])
complexity = [random_choice(data.APPLICATION_COMPLEXITY)] complexity = [random_choice(data.APPLICATION_COMPLEXITY)]
dev_team = [random_choice(data.DEV_TEAM)] dev_team = [random_choice(data.DEV_TEAM)]
team_experience = random_choice(data.TEAM_EXPERIENCE) team_experience = random_choice(data.TEAM_EXPERIENCE)

View File

@ -169,19 +169,45 @@ forms:
scope_label: Cloud Project Scope 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. 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 defense_component_label: Department of Defense Component
app_migration_label: App Migration app_migration:
app_migration_description: Do you plan to migrate existing application(s) to the cloud? label: App Migration
description: Do you plan to migrate existing application(s) to the cloud?
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"
native_apps_label: Native Apps native_apps_label: Native Apps
native_apps_description: Do you plan to develop application(s) natively in the cloud? native_apps_description: Do you plan to develop application(s) natively in the cloud?
complexity_label: Project Complexity complexity:
complexity_description: Which of these describes how complex your team's use of the cloud will be? Select all that apply. label: Project Complexity
description: Which of these describes how complex your team's use of the cloud will be? Select all that apply.
storage: Storage
data_analytics: Data Analytics
conus: CONUS Acess
oconus: OCONUS Access
tactical_edge: Tactical Edge Access
not_sure: Not Sure
other: Other
complexity_other_label: Project Complexity Other complexity_other_label: Project Complexity Other
dev_team_label: Development Team dev_team:
dev_team_description: Which people or teams will be completing the development work for your cloud applications? Select all that apply. label: Development Team
description: Which people or teams will be completing the development work for your cloud applications? Select all that apply.
government_civilians: Government Civilians
military: Military
contractor: Contractor
other: "Other <em>(E.g. University or other partner)</em>"
dev_team_other_label: Development Team Other dev_team_other_label: Development Team Other
team_experience_label: Team Experience team_experience:
team_experience_description: How much experience does your team have with development in the cloud? label: Team Experience
performance_length_label: Period of Performance length description: How much experience does your team have with development in the cloud?
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
performance_length:
label: Period of Performance length
start_date_label: Start Date start_date_label: Start Date
end_date_label: End Date end_date_label: End Date
pdf_label: Upload a copy of your CSP Cost Estimate Research pdf_label: Upload a copy of your CSP Cost Estimate Research
@ -375,11 +401,42 @@ task_orders:
ko_info_title: Contracting Officer (KO) Information 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. 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)" skip_ko_label: "Skip for now (We'll remind you to enter one later)"
cor_info_title: Contractive Officer Representative (COR) Information cor_info_title: Contracting 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. 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 am_cor_label: I am the Contracting Officer Representative (COR) for this Task Order
so_info_title: Security Officer Information 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. so_info_paragraph: Your Security Officer will need to answer some security configuration questions in order to generate a DD-254 document, then electronically sign.
review:
section_title: Review Your Task Order Info
app_info: What are you building
portfolio: Portfolio
dod: DoD Component
scope: Scope (Statement of Work)
reporting: Reporting
migration: App Migration
native_apps: Native Apps
yes_native: Yes, planning to develop natively in the cloud
no_native: No, not planning to develop natively in the cloud
not_sure_native: Not sure, unsure if planning to develop natively in the cloud
complexity: Project Complexity
team: Development Team
experience: Team Experience
funding: Funding
performance_period: Period of Performance length
usage_est_link: View Usage Estimate
to_value: Task Order Value
clin_1: 'CLIN #1: Unclassified Cloud'
clin_2: 'CLIN #2: Classified Cloud'
clin_3: 'CLIN #3: Unclassified Cloud'
clin_4: 'CLIN $4: Classified Cloud'
classified_inactive: (Available Soon)
oversight: Oversight
ko: Contracting Officer (KO)
cor: Contracting Officer Representative (COR)
so: IA Security Officer
dod_id: 'DoD ID:'
invited: Invited
not_invited: Not Yet Invited
testing: testing:
example_string: Hello World example_string: Hello World
example_with_variables: 'Hello, {name}!' example_with_variables: 'Hello, {name}!'