diff --git a/atst/domain/task_orders.py b/atst/domain/task_orders.py index e3b2c342..e5ef82f6 100644 --- a/atst/domain/task_orders.py +++ b/atst/domain/task_orders.py @@ -76,7 +76,7 @@ class TaskOrders(object): def is_section_complete(cls, task_order, section): if section in TaskOrders.SECTIONS: for attr in TaskOrders.SECTIONS[section]: - if not getattr(task_order, attr): + if getattr(task_order, attr) is None: return False return True diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index 86a7efb8..03854c95 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -1,5 +1,4 @@ from wtforms.fields import ( - DateField, IntegerField, RadioField, SelectField, @@ -7,6 +6,7 @@ from wtforms.fields import ( StringField, TextAreaField, ) +from wtforms.fields.html5 import DateField from .forms import CacheableForm from .data import ( @@ -28,9 +28,7 @@ class AppInfoForm(CacheableForm): 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", ) defense_component = SelectField( - "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, + "Department of Defense Component", choices=SERVICE_BRANCHES ) app_migration = RadioField( "App Migration", @@ -66,27 +64,12 @@ class AppInfoForm(CacheableForm): class FundingForm(CacheableForm): - 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.", - ) + 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") class OversightForm(CacheableForm): diff --git a/atst/routes/task_orders/new.py b/atst/routes/task_orders/new.py index 507f2471..059ebc1b 100644 --- a/atst/routes/task_orders/new.py +++ b/atst/routes/task_orders/new.py @@ -76,9 +76,13 @@ class ShowTaskOrderWorkflow: return screen_info +from flask import current_app as app + + class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow): def __init__(self, form_data, user, screen=1, task_order_id=None): self.form_data = form_data + app.logger.info(form_data) self.user = user self.screen = screen self.task_order_id = task_order_id diff --git a/templates/components/user_info.html b/templates/components/user_info.html new file mode 100644 index 00000000..7c6378ca --- /dev/null +++ b/templates/components/user_info.html @@ -0,0 +1,23 @@ +{% from "components/text_input.html" import TextInput %} + +{% macro UserInfo(first_name, last_name, email, dod_id) -%} +
+
+ {{ TextInput(first_name) }} +
+ +
+ {{ TextInput(last_name) }} +
+
+ +
+
+ {{ TextInput(email, placeholder='name@mail.mil') }} +
+ +
+ {{ TextInput(dod_id, placeholder='1234567890') }} +
+
+{% endmacro %} diff --git a/templates/navigation/global_navigation.html b/templates/navigation/global_navigation.html index 093b86bd..a87c329d 100644 --- a/templates/navigation/global_navigation.html +++ b/templates/navigation/global_navigation.html @@ -11,6 +11,12 @@ ] ) }} + {{ SidenavItem("New Task Order", + href=url_for("task_orders.new", screen=1), + icon="plus", + active=g.matchesPath('/task_orders/new'), + ) }} + {% if g.current_user.has_workspaces %} {{ SidenavItem("Workspaces", href="/workspaces", icon="cloud", active=g.matchesPath('/workspaces')) }} {% endif %} diff --git a/templates/task_orders/new/_user_fields.html b/templates/task_orders/new/_user_fields.html new file mode 100644 index 00000000..51028005 --- /dev/null +++ b/templates/task_orders/new/_user_fields.html @@ -0,0 +1,19 @@ +
+
+ {{ TextInput(first_name) }} +
+ +
+ {{ TextInput(last_name) }} +
+
+ +
+
+ {{ TextInput(email, placeholder='name@mail.mil') }} +
+ +
+ {{ TextInput(dod_id, placeholder='1234567890') }} +
+
diff --git a/templates/task_orders/new/app_info.html b/templates/task_orders/new/app_info.html index 86acc4e7..09a088a7 100644 --- a/templates/task_orders/new/app_info.html +++ b/templates/task_orders/new/app_info.html @@ -13,8 +13,14 @@ {% include "fragments/flash.html" %}

Basic Information

-{{ TextInput(form.portfolio_name) }} +{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }} {{ TextInput(form.scope, paragraph=True) }} +

+ + Not sure how to describe your scope? Read some Sample Scopes to + get an idea of what is appropriate. + +

{{ OptionsInput(form.defense_component) }}
@@ -35,7 +41,11 @@

Market Research

-

View JEDI Market Research Memo

+

+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. View JEDI Cloud Market Research +

{% endblock %} diff --git a/templates/task_orders/new/funding.html b/templates/task_orders/new/funding.html index a5189b4b..4cc4b57c 100644 --- a/templates/task_orders/new/funding.html +++ b/templates/task_orders/new/funding.html @@ -13,14 +13,64 @@ {% include "fragments/flash.html" %} +

Period of Performance

+ +

Choose the dates your task order will cover.

+ +

+Because your funds will be lost if you don’t use them, we strongly recommend +submitting small, short-duration task orders, usually a three month period. +We’ll 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. +

+ {{ DateInput(form.start_date, placeholder='MM / DD / YYYY', validation='date') }} {{ DateInput(form.end_date, placeholder='MM / DD / YYYY', validation='date') }} -

Cloud Usage Estimate

-

Upload a copy of your CSP Cost Estimate Research

+
+

Cloud Usage Estimate

+ +

+ Calculate how much your cloud usage will cost. A technical representative + should help you complete this calculation. + + Cloud Service Provider's estimate calculator + +

+

Upload a copy of your CSP Cost Estimate Research

+ +

+Upload your anticipated cloud usage from the CSP tool linked above. PDFs and +screengrabs of the tool are sufficient. +

+

+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. +

+ + +

Cloud Usage Calculations

+

+Enter the results of your cloud usage calculations. These will correspond with +your task order's period of performance. +

+

Cloud Offerings

+

+Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) offerings +

+ {{ TextInput(form.clin_01, validation='dollars') }} {{ TextInput(form.clin_02, validation='dollars') }} + +

Cloud Support and Assistance

+

+Technical guidance from the cloud service provider, including architecture, +configuration of IaaS and PaaS, integration, troubleshooting assistance, and +other services. +

{{ TextInput(form.clin_03, validation='dollars', tooltip='The cloud support and assistance packages cannot be used as a primary development resource.') }} {{ TextInput(form.clin_04, validation='dollars', tooltip='The cloud support and assistance packages cannot be used as a primary development resource.') }}

Total Task Order Value

diff --git a/templates/task_orders/new/oversight.html b/templates/task_orders/new/oversight.html index f48c3d2c..42c316e9 100644 --- a/templates/task_orders/new/oversight.html +++ b/templates/task_orders/new/oversight.html @@ -1,11 +1,9 @@ {% extends 'task_orders/_new.html' %} -{% from "components/text_input.html" import TextInput %} -{% from "components/options_input.html" import OptionsInput %} -{% from "components/date_input.html" import DateInput %} +{% from "components/user_info.html" import UserInfo %} {% block heading %} - Funding + Oversight {% endblock %} {% block form %} @@ -14,19 +12,13 @@

Contracting Officer (KO) Information

-{{ TextInput(form.ko_first_name) }} -{{ TextInput(form.ko_last_name) }} -{{ TextInput(form.ko_email) }} -{{ TextInput(form.ko_dod_id) }} + +{{ UserInfo(form.ko_first_name, form.ko_last_name, form.ko_email, form.ko_dod_id) }} +

Contractive Officer Representative (COR) Information

-{{ TextInput(form.cor_first_name) }} -{{ TextInput(form.cor_last_name) }} -{{ TextInput(form.cor_email) }} -{{ TextInput(form.cor_dod_id) }} +{{ UserInfo(form.cor_first_name, form.cor_last_name, form.cor_email, form.cor_dod_id) }} +

Security Officer Information

-{{ TextInput(form.so_first_name) }} -{{ TextInput(form.so_last_name) }} -{{ TextInput(form.so_email) }} -{{ TextInput(form.so_dod_id) }} +{{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_dod_id) }} {% endblock %} diff --git a/templates/task_orders/new/review.html b/templates/task_orders/new/review.html new file mode 100644 index 00000000..6296fe91 --- /dev/null +++ b/templates/task_orders/new/review.html @@ -0,0 +1,17 @@ +{% extends 'task_orders/_new.html' %} + +{% from "components/text_input.html" import TextInput %} +{% from "components/options_input.html" import OptionsInput %} +{% from "components/date_input.html" import DateInput %} + +{% block heading %} + Review & Download +{% endblock %} + +{% block form %} + +{% include "fragments/flash.html" %} + +Download your Task Order Packet. + +{% endblock %} diff --git a/tests/routes/task_orders/test_new_task_order.py b/tests/routes/task_orders/test_new_task_order.py index 182fe4bb..2b9009d9 100644 --- a/tests/routes/task_orders/test_new_task_order.py +++ b/tests/routes/task_orders/test_new_task_order.py @@ -26,6 +26,19 @@ def slice_data_for_section(task_order_data, section): return {k: v for k, v in task_order_data.items() if k in attrs} +def serialize_dates(data): + if not data: + return data + + dates = { + k: v.strftime("%m/%d/%Y") for k, v in data.items() if hasattr(v, "strftime") + } + + data.update(dates) + + return data + + # TODO: this test will need to be more complicated when we add validation to # the forms def test_create_new_task_order(client, user_session): @@ -43,6 +56,7 @@ def test_create_new_task_order(client, user_session): assert url_for("task_orders.new", screen=2) in response.headers["Location"] funding_data = slice_data_for_section(task_order_data, "funding") + funding_data = serialize_dates(funding_data) response = client.post( response.headers["Location"], data=funding_data, follow_redirects=False )