From 0b276b49c85eca8f796acc803b1ce7fd31afbc00 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 24 Jan 2019 16:37:46 -0500 Subject: [PATCH 01/29] Route to KO Review TO page --- atst/routes/portfolios/task_orders.py | 5 +++ templates/components/datepicker.html | 50 ++++++++++++++++++++++ templates/portfolios/task_orders/show.html | 2 +- templates/task_orders/new/app_info.html | 12 ++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 templates/components/datepicker.html diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index f9428be2..928d15db 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -69,6 +69,11 @@ def view_task_order(portfolio_id, task_order_id): ) +@portfolios_bp.route("/portfolios//task_order//review") +def review_task_order(portfolio_id, task_order_id): + return render_template("/portfolios/task_orders/review.html") + + @portfolios_bp.route( "/portfolios//task_order//invitations" ) diff --git a/templates/components/datepicker.html b/templates/components/datepicker.html new file mode 100644 index 00000000..c597aeb5 --- /dev/null +++ b/templates/components/datepicker.html @@ -0,0 +1,50 @@ +{% from "components/icon.html" import Icon %} + +{% macro DatePicker(field) -%} + + +
+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ {{ Icon("ok", classes="icon--green") }} +
+ +
+
+ +{%- endmacro %} diff --git a/templates/portfolios/task_orders/show.html b/templates/portfolios/task_orders/show.html index 8c68c7f8..cd04a4bc 100644 --- a/templates/portfolios/task_orders/show.html +++ b/templates/portfolios/task_orders/show.html @@ -93,7 +93,7 @@ complete=all_sections_complete) %}
Edit diff --git a/templates/task_orders/new/app_info.html b/templates/task_orders/new/app_info.html index 85b52a6c..37813827 100644 --- a/templates/task_orders/new/app_info.html +++ b/templates/task_orders/new/app_info.html @@ -4,6 +4,7 @@ {% from "components/options_input.html" import OptionsInput %} {% from "components/date_input.html" import DateInput %} {% from "components/multi_checkbox_input.html" import MultiCheckboxInput %} +{% from "components/datepicker.html" import DatePicker %} {% block heading %} {{ "task_orders.new.app_info.section_title"| translate }} @@ -11,6 +12,17 @@ {% block form %} +
+ + + For example: 04 28 1986 + + {{ DatePicker() }} +
+ +

{{ "task_orders.new.app_info.basic_info_title"| translate }}

{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }} From 3358aa99ba28cfb225c00cf457e1c14bf3486d34 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 25 Jan 2019 09:28:20 -0500 Subject: [PATCH 02/29] Add basic review page --- atst/routes/portfolios/task_orders.py | 8 +- templates/portfolios/task_orders/review.html | 224 +++++++++++++++++++ translations.yaml | 2 + 3 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 templates/portfolios/task_orders/review.html diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 928d15db..3dd4c08e 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -71,7 +71,13 @@ def view_task_order(portfolio_id, task_order_id): @portfolios_bp.route("/portfolios//task_order//review") def review_task_order(portfolio_id, task_order_id): - return render_template("/portfolios/task_orders/review.html") + portfolio = Portfolios.get(g.current_user, portfolio_id) + task_order = TaskOrders.get(g.current_user, task_order_id) + return render_template( + "/portfolios/task_orders/review.html", + portfolio=portfolio, + task_order=task_order, + ) @portfolios_bp.route( diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html new file mode 100644 index 00000000..7089b383 --- /dev/null +++ b/templates/portfolios/task_orders/review.html @@ -0,0 +1,224 @@ +{% extends "base.html" %} + +{% from "components/edit_link.html" import EditLink %} +{% from "components/required_label.html" import RequiredLabel %} +{% from "components/icon.html" import Icon %} + +{% block content %} + +
+ + {% include "fragments/flash.html" %} + + {% macro TOEditLink(screen=1, anchor=None) %} + {% if task_order %} + {{ EditLink(url_for("task_orders.new", screen=screen, task_order_id=task_order.id, _anchor=anchor)) }} + {% else %} + {{ EditLink(url_for("task_orders.new", screen=screen, _anchor=anchor)) }} + {% endif %} + {% endmacro %} + + {% macro ReviewField(heading, field, filter=None) %} +
+

{{ heading }}

+ {% if field %} +

{{ field | findFilter(filter) }}

+ {% else %} + {{ RequiredLabel() }} + {% endif %} + {% if caller %} + {{ caller() }} + {% endif %} +
+ {% endmacro %} + + {% macro ReviewOfficerInfo(heading, first_name, last_name, email, phone_number, dod_id, officer) %} +
+

{{ heading | translate }}

+ {{ first_name }} {{ last_name }}
+ {{ email }}
+ {% if phone_number %} + {{ phone_number | usPhone }} + {% else %} + {{ RequiredLabel() }} + {% endif %} +
+ {{ "task_orders.new.review.dod_id" | translate }} {{ dod_id}}
+ {% if officer %} + {{ Icon('ok', classes='icon--green') }} {{ "task_orders.new.review.invited"| translate }} + {% else %} + {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_invited"| translate }} + {% endif %} +
+ {% endmacro %} + +
+ +
+

+
Task Order Builder
+ {{ "task_orders.new.review.section_title"| translate }} +

+
+ +
+
{{ "task_orders.new.review.app_info"| translate }}
+ +
+ {{ ReviewField(("task_orders.new.review.portfolio" | translate), task_order.portfolio_name) }} + {{ ReviewField(("task_orders.new.review.dod" | translate), task_order.defense_component, filter="normalizeOrder") }} +
+
+ {{ ReviewField(("task_orders.new.review.scope" | translate), task_order.scope) }} +
+
+ +
{{ "task_orders.new.review.reporting"| translate }}
+ +
+ {{ ReviewField(("forms.task_order.app_migration.label" | translate), ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate), filter="removeHtml") }} + {{ ReviewField(("forms.task_order.native_apps.label" | translate), ("forms.task_order.native_apps.{}".format(task_order.native_apps))| translate) }} +
+ +

{{ "task_orders.new.review.complexity"| translate }}

+ {% if task_order.complexity %} +
    + {% for item in task_order.complexity %} +
  • + {{ Icon('ok', classes='icon--gray icon--medium') }}{{ "forms.task_order.complexity.{}".format(item) | translate }}{% if item == 'other' %}: {{ task_order.complexity_other }}{% endif %} +
  • + {% endfor %} +
+ {% else %} +

{{ RequiredLabel() }}

+ {% endif %} + +
+
+

{{ "task_orders.new.review.team"| translate }}

+ {% if task_order.dev_team %} +
    + {% for item in task_order.dev_team %} +
  • + {% 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 %} +
  • + {% endfor %} +
+ {% else %} +

{{ RequiredLabel() }}

+ {% endif %} +
+ + {{ ReviewField(("forms.task_order.team_experience.label" |translate), ("forms.task_order.team_experience.{}".format(task_order.team_experience)) | translate) }} +
+ +
+ +
{{ "task_orders.new.review.funding"| translate }}
+ +
+ {% call ReviewField(("task_orders.new.review.performance_period" | translate), task_order.performance_length, filter="translateDuration") %} + {% if task_order.csp_estimate %} +

+ {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} +

+ {% else %} +
+ {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} + {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_uploaded"| translate }} + {% endif %} + {% endcall %} + +
+ + + + + + + + + + + + + + + + + + + + + + + +

{{ "task_orders.new.review.to_value"| translate }}

+ {% if task_order.budget %} + {{ task_order.budget | dollarsWithCents }} + {% endif %} +

{{ "task_orders.new.review.clin_1"| translate }}

+ {% if task_order.clin_01 %} + {{ task_order.clin_01 | dollarsWithCents }} + {% else %} + {{ RequiredLabel() }} + {% endif %} +

+ {{ "task_orders.new.review.clin_2"| translate }} + {% if not config.CLASSIFIED %} +
{{ "task_orders.new.review.classified_inactive"| translate }}
+ {% endif %} +

+ {% if task_order.clin_02 and config.CLASSIFIED %} + {{ task_order.clin_02 | dollarsWithCents or RequiredLabel() }} + {% endif %} +

{{ "task_orders.new.review.clin_3"| translate }}

+ {% if task_order.clin_03 %} + {{ task_order.clin_03 | dollarsWithCents or RequiredLabel() }} + {% else %} + {{ RequiredLabel() }} + {% endif %} +

+ {{ "task_orders.new.review.clin_4"| translate }} + {% if not config.CLASSIFIED %} +
{{ "task_orders.new.review.classified_inactive"| translate }}
+ {% endif %} +

+ {% if task_order.clin_04 and config.CLASSIFIED %} + {{ task_order.clin_04 | dollarsWithCents or RequiredLabel() }} + {% endif %} +
+
+
+ +
+ +
{{ "task_orders.new.review.oversight"| translate }}
+ +
+ {{ ReviewOfficerInfo("task_orders.new.review.ko", task_order.ko_first_name, task_order.ko_last_name, task_order.ko_email, task_order.ko_phone_number, task_order.ko_dod_id, task_order.contracting_officer) }} + {{ ReviewOfficerInfo("task_orders.new.review.cor", task_order.cor_first_name, task_order.cor_last_name, task_order.cor_email, task_order.cor_phone_number, task_order.cor_dod_id, task_order.contracting_officer_representative) }} +
+
+ {{ ReviewOfficerInfo("task_orders.new.review.so", task_order.so_first_name, task_order.so_last_name, task_order.so_email, task_order.so_phone_number, task_order.so_dod_id, task_order.security_officer) }} +
+ +
+ +
{{ "task_orders.ko_review.task_order_information"| translate }}
+ +
+
+ +
+ +
+ + + +
+{% endblock %} diff --git a/translations.yaml b/translations.yaml index 6b7ad7fa..ccc6878c 100644 --- a/translations.yaml +++ b/translations.yaml @@ -456,6 +456,8 @@ task_orders: description: Your Security Officer will need to answer some security configuration questions in order to generate a DD-254 document, then electronically sign. add_button_text: Add / Invite Security Officer invite_button_text: Invite Security Officer + ko_review: + task_order_information: Task Order Information testing: example_string: Hello World example_with_variables: 'Hello, {name}!' From f6a066f9835e0abf7136bf3d7f16ba632d3b2728 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 25 Jan 2019 11:17:08 -0500 Subject: [PATCH 03/29] Add basic KO review form --- atst/forms/ko_review.py | 38 ++++++++++++++++++++ atst/routes/portfolios/task_orders.py | 15 +++++++- templates/portfolios/task_orders/review.html | 15 ++++++++ translations.yaml | 7 ++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 atst/forms/ko_review.py diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py new file mode 100644 index 00000000..7c7cb399 --- /dev/null +++ b/atst/forms/ko_review.py @@ -0,0 +1,38 @@ +from flask_wtf.file import FileAllowed + +from wtforms.fields import StringField, TextAreaField, FileField +from wtforms.validators import Optional, Length, InputRequired + +from .forms import CacheableForm +from .validators import IsNumber + +from atst.utils.localization import translate + +class KOReviewForm(CacheableForm): + pdf = FileField( + translate("forms.ko_review.pdf_label"), + validators=[ + FileAllowed(["pdf"], translate("forms.ko_review.pdf_description")), + InputRequired(), + ], + render_kw={"required": False}, + ) + to_number = StringField( + translate("forms.ko_review.to_number"), + validators=[ + Length(min=10), + IsNumber(), + ], + ) + loa = StringField( + translate("forms.ko_review.loa"), + validators=[ + Length(min=10), + IsNumber(), + ], + ) + custom_clauses = TextAreaField( + translate("forms.ko_review.custom_clauses_label"), + description=translate("forms.ko_review.custom_clauses_description"), + validators=[Optional()] + ) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 3dd4c08e..c9bec4a9 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -9,6 +9,7 @@ from atst.domain.task_orders import TaskOrders from atst.domain.portfolios import Portfolios from atst.forms.officers import EditTaskOrderOfficersForm from atst.models.task_order import Status as TaskOrderStatus +from atst.forms.ko_review import KOReviewForm @portfolios_bp.route("/portfolios//task_orders") @@ -70,15 +71,27 @@ def view_task_order(portfolio_id, task_order_id): @portfolios_bp.route("/portfolios//task_order//review") -def review_task_order(portfolio_id, task_order_id): +def review_task_order(portfolio_id, task_order_id, form=None): portfolio = Portfolios.get(g.current_user, portfolio_id) task_order = TaskOrders.get(g.current_user, task_order_id) return render_template( "/portfolios/task_orders/review.html", portfolio=portfolio, task_order=task_order, + form=form or KOReviewForm(), ) +@portfolios_bp.route("/portfolios//task_order//submit_review", methods=["POST"]) +def submit_review_task_order(portfolio_id, task_order_id, form=None): + portfolio = Portfolios.get(g.current_user, portfolio_id) + task_order = TaskOrders.get(g.current_user, task_order_id) + form = KOReviewForm(http_request.form) + + if form.validate(): + return redirect(url_for("task_orders.view_task_order")) + else: + return review(portfolio.id, task_order.id) + @portfolios_bp.route( "/portfolios//task_order//invitations" diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 7089b383..4d863c6e 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -10,6 +10,16 @@ {% include "fragments/flash.html" %} + {% block form_action %} + {% if task_order_id %} +
+ {% else %} + + {% endif %} + {% endblock %} + + {% block form %} + {% macro TOEditLink(screen=1, anchor=None) %} {% if task_order %} {{ EditLink(url_for("task_orders.new", screen=screen, task_order_id=task_order.id, _anchor=anchor)) }} @@ -195,6 +205,8 @@
+
Period of Performance Start Date
+
{{ "task_orders.new.review.oversight"| translate }}
@@ -211,8 +223,11 @@
{{ "task_orders.ko_review.task_order_information"| translate }}
+
Upload a copy of your Task Order document
+ + {% endblock %}
diff --git a/translations.yaml b/translations.yaml index ccc6878c..5014cf86 100644 --- a/translations.yaml +++ b/translations.yaml @@ -54,6 +54,13 @@ forms: lname_mao_label: Last Name (optional) phone_ext_mao_label: Extension (optional) phone_mao_label: Mission Owner phone number (optional) + ko_review: + pdf_label: Upload a copy of your Task Order document + pdf_description: Upload a PDF of the Task Order that you entered in your system of record for your organization. + to_number: Task Order Number + loa: Line of Accounting (LOA) \#1 + custom_clauses_label: Task Order Custom Clauses (optional) + custom_clauses_description: This will put a pause on the CSP access once the KO signs until the CCPO reviews that language to make sure it is legal. edit_member: portfolio_role_label: Portfolio Role edit_user: From 1ca4d62a3a1aae3685ab74db16133e705f560be8 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 25 Jan 2019 15:58:41 -0500 Subject: [PATCH 04/29] Render form fields on the page --- atst/forms/ko_review.py | 22 ++++++++------------ atst/routes/portfolios/task_orders.py | 6 +++++- templates/portfolios/task_orders/review.html | 15 +++++++++++-- translations.yaml | 4 +++- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 7c7cb399..51ff7fba 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -8,31 +8,27 @@ from .validators import IsNumber from atst.utils.localization import translate + class KOReviewForm(CacheableForm): pdf = FileField( translate("forms.ko_review.pdf_label"), + description=translate("forms.ko_review.pdf_description"), validators=[ - FileAllowed(["pdf"], translate("forms.ko_review.pdf_description")), + FileAllowed( + ["pdf", "png"], translate("forms.task_order.file_format_not_allowed") + ), InputRequired(), ], - render_kw={"required": False}, + render_kw={"required": False, "accept": ".pdf,.png,application/pdf,image/png"}, ) to_number = StringField( - translate("forms.ko_review.to_number"), - validators=[ - Length(min=10), - IsNumber(), - ], + translate("forms.ko_review.to_number"), validators=[Length(min=10), IsNumber()] ) loa = StringField( - translate("forms.ko_review.loa"), - validators=[ - Length(min=10), - IsNumber(), - ], + translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()] ) custom_clauses = TextAreaField( translate("forms.ko_review.custom_clauses_label"), description=translate("forms.ko_review.custom_clauses_description"), - validators=[Optional()] + validators=[Optional()], ) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index c9bec4a9..3c63137c 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -81,7 +81,11 @@ def review_task_order(portfolio_id, task_order_id, form=None): form=form or KOReviewForm(), ) -@portfolios_bp.route("/portfolios//task_order//submit_review", methods=["POST"]) + +@portfolios_bp.route( + "/portfolios//task_order//submit_review", + methods=["POST"], +) def submit_review_task_order(portfolio_id, task_order_id, form=None): portfolio = Portfolios.get(g.current_user, portfolio_id) task_order = TaskOrders.get(g.current_user, task_order_id) diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 4d863c6e..1615a283 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -3,6 +3,9 @@ {% from "components/edit_link.html" import EditLink %} {% from "components/required_label.html" import RequiredLabel %} {% from "components/icon.html" import Icon %} +{% from "components/datepicker.html" import DatePicker %} +{% from "components/text_input.html" import TextInput %} + {% block content %} @@ -205,7 +208,10 @@
-
Period of Performance Start Date
+
{{ "task_orders.ko_review.performance_start"| translate }}
+ {{ DatePicker() }} +
{{ "task_orders.ko_review.performance_end"| translate }}
+ {{ DatePicker() }}
@@ -223,7 +229,12 @@
{{ "task_orders.ko_review.task_order_information"| translate }}
-
Upload a copy of your Task Order document
+ {{ form.pdf.label }} + {{ form.pdf.description }} + {{ form.pdf }} + {{ TextInput(form.to_number) }} + {{ TextInput(form.loa) }} + {{ TextInput(form.custom_clauses, paragraph=True) }} diff --git a/translations.yaml b/translations.yaml index 5014cf86..6c9bc1e0 100644 --- a/translations.yaml +++ b/translations.yaml @@ -58,7 +58,7 @@ forms: pdf_label: Upload a copy of your Task Order document pdf_description: Upload a PDF of the Task Order that you entered in your system of record for your organization. to_number: Task Order Number - loa: Line of Accounting (LOA) \#1 + loa: Line of Accounting (LOA) #1 custom_clauses_label: Task Order Custom Clauses (optional) custom_clauses_description: This will put a pause on the CSP access once the KO signs until the CCPO reviews that language to make sure it is legal. edit_member: @@ -464,6 +464,8 @@ task_orders: add_button_text: Add / Invite Security Officer invite_button_text: Invite Security Officer ko_review: + performance_start: Period of Performance Start Date + performance_end: Period of Performance End Date task_order_information: Task Order Information testing: example_string: Hello World From b91d869bbe78ecebedc626da487557e7e3e748ae Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 28 Jan 2019 11:12:11 -0500 Subject: [PATCH 05/29] Clean up routes --- atst/routes/portfolios/task_orders.py | 11 +++++++---- templates/portfolios/task_orders/review.html | 4 +--- templates/portfolios/task_orders/show.html | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 3c63137c..6883dc2f 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -71,9 +71,10 @@ def view_task_order(portfolio_id, task_order_id): @portfolios_bp.route("/portfolios//task_order//review") -def review_task_order(portfolio_id, task_order_id, form=None): - portfolio = Portfolios.get(g.current_user, portfolio_id) +def ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) + # get permission: make sure g.current_user is task_order.contracting_officer + portfolio = Portfolios.get(g.current_user, portfolio_id) return render_template( "/portfolios/task_orders/review.html", portfolio=portfolio, @@ -86,14 +87,16 @@ def review_task_order(portfolio_id, task_order_id, form=None): "/portfolios//task_order//submit_review", methods=["POST"], ) -def submit_review_task_order(portfolio_id, task_order_id, form=None): - portfolio = Portfolios.get(g.current_user, portfolio_id) +def submit_ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) form = KOReviewForm(http_request.form) + portfolio = Portfolios.get(g.current_user, portfolio_id) if form.validate(): + # add form data to TO data return redirect(url_for("task_orders.view_task_order")) else: + # stay on the page and fix the fields that didnt validate return review(portfolio.id, task_order.id) diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 1615a283..1453cfe4 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -15,9 +15,7 @@ {% block form_action %} {% if task_order_id %} - - {% else %} - + {% endif %} {% endblock %} diff --git a/templates/portfolios/task_orders/show.html b/templates/portfolios/task_orders/show.html index cd04a4bc..a75a1836 100644 --- a/templates/portfolios/task_orders/show.html +++ b/templates/portfolios/task_orders/show.html @@ -93,7 +93,7 @@ complete=all_sections_complete) %}
Edit From 75bfc5fbdb3e80637458d7d7959b7884e6db2bed Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 28 Jan 2019 15:29:19 -0500 Subject: [PATCH 06/29] fix routes --- atst/routes/portfolios/task_orders.py | 24 ++++++++++++++++---- templates/portfolios/task_orders/review.html | 7 ++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 6883dc2f..e7c50fac 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -84,20 +84,36 @@ def ko_review(portfolio_id, task_order_id, form=None): @portfolios_bp.route( - "/portfolios//task_order//submit_review", - methods=["POST"], + "/portfolios//task_order//review", methods=["POST"] ) def submit_ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) form = KOReviewForm(http_request.form) portfolio = Portfolios.get(g.current_user, portfolio_id) + # import ipdb + + # ipdb.set_trace() if form.validate(): + form_data = {**http_request.form, **http_request.files} # add form data to TO data - return redirect(url_for("task_orders.view_task_order")) + import ipdb; ipdb.set_trace() + return redirect( + url_for( + "portfolios.view_task_order", + portfolio_id=portfolio_id, + task_order_id=task_order_id, + form=form, + ) + ) else: # stay on the page and fix the fields that didnt validate - return review(portfolio.id, task_order.id) + return render_template( + "/portfolios/task_orders/review.html", + portfolio=portfolio, + task_order=task_order, + form=form, + ) @portfolios_bp.route( diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 1453cfe4..e399df65 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -13,11 +13,8 @@ {% include "fragments/flash.html" %} - {% block form_action %} - {% if task_order_id %} - - {% endif %} - {% endblock %} + + {{ form.csrf_token }} {% block form %} From 7bef2e86ca10096b071db3d5f72be5a80946d848 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 29 Jan 2019 15:20:41 -0500 Subject: [PATCH 07/29] Edit form fields --- atst/forms/ko_review.py | 24 ++++++++++++++++++-- atst/routes/portfolios/task_orders.py | 7 +----- templates/portfolios/task_orders/review.html | 18 ++------------- translations.yaml | 5 ++-- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 51ff7fba..4081b5a3 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -1,15 +1,36 @@ +import pendulum from flask_wtf.file import FileAllowed +from wtforms.fields.html5 import DateField from wtforms.fields import StringField, TextAreaField, FileField from wtforms.validators import Optional, Length, InputRequired from .forms import CacheableForm -from .validators import IsNumber +from .validators import IsNumber, DateRange from atst.utils.localization import translate class KOReviewForm(CacheableForm): + start_date = DateField( + translate("forms.ko_review.start_date_label"), + validators=[ + DateRange( + lower_bound=pendulum.duration(days=0), + message=translate("forms.ko_review.invalid_date"), + ) + ], + ) + end_date = DateField( + translate("forms.ko_review.end_date_label"), + validators=[ + DateRange( + lower_bound=pendulum.duration(days=0), + message=translate("forms.ko_review.invalid_date"), + ) + ], + format="%m/%d/%Y", + ) pdf = FileField( translate("forms.ko_review.pdf_label"), description=translate("forms.ko_review.pdf_description"), @@ -17,7 +38,6 @@ class KOReviewForm(CacheableForm): FileAllowed( ["pdf", "png"], translate("forms.task_order.file_format_not_allowed") ), - InputRequired(), ], render_kw={"required": False, "accept": ".pdf,.png,application/pdf,image/png"}, ) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index e7c50fac..7d01f190 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -90,14 +90,10 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) form = KOReviewForm(http_request.form) portfolio = Portfolios.get(g.current_user, portfolio_id) - # import ipdb - - # ipdb.set_trace() if form.validate(): form_data = {**http_request.form, **http_request.files} - # add form data to TO data - import ipdb; ipdb.set_trace() + TaskOrders.update(user=g.current_user, task_order=task_order, **form_data) return redirect( url_for( "portfolios.view_task_order", @@ -107,7 +103,6 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): ) ) else: - # stay on the page and fix the fields that didnt validate return render_template( "/portfolios/task_orders/review.html", portfolio=portfolio, diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index e399df65..97b8d00d 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -31,8 +31,6 @@

{{ heading }}

{% if field %}

{{ field | findFilter(filter) }}

- {% else %} - {{ RequiredLabel() }} {% endif %} {% if caller %} {{ caller() }} @@ -47,8 +45,6 @@ {{ email }}
{% if phone_number %} {{ phone_number | usPhone }} - {% else %} - {{ RequiredLabel() }} {% endif %}
{{ "task_orders.new.review.dod_id" | translate }} {{ dod_id}}
@@ -97,8 +93,6 @@ {% endfor %} - {% else %} -

{{ RequiredLabel() }}

{% endif %}
@@ -116,8 +110,6 @@ {% endfor %} - {% else %} -

{{ RequiredLabel() }}

{% endif %}
@@ -157,8 +149,6 @@ {% if task_order.clin_01 %} {{ task_order.clin_01 | dollarsWithCents }} - {% else %} - {{ RequiredLabel() }} {% endif %} @@ -180,8 +170,6 @@ {% if task_order.clin_03 %} {{ task_order.clin_03 | dollarsWithCents or RequiredLabel() }} - {% else %} - {{ RequiredLabel() }} {% endif %} @@ -203,10 +191,8 @@
-
{{ "task_orders.ko_review.performance_start"| translate }}
- {{ DatePicker() }} -
{{ "task_orders.ko_review.performance_end"| translate }}
- {{ DatePicker() }} + {{ DatePicker(form.start_date) }} + {{ DatePicker(form.end_date) }}
diff --git a/translations.yaml b/translations.yaml index 6c9bc1e0..4869ff80 100644 --- a/translations.yaml +++ b/translations.yaml @@ -55,6 +55,9 @@ forms: phone_ext_mao_label: Extension (optional) phone_mao_label: Mission Owner phone number (optional) ko_review: + start_date_label: Period of Performance Start Date + end_date_label: Period of Performance End Date + invalid_date: Must be a date in the future. pdf_label: Upload a copy of your Task Order document pdf_description: Upload a PDF of the Task Order that you entered in your system of record for your organization. to_number: Task Order Number @@ -464,8 +467,6 @@ task_orders: add_button_text: Add / Invite Security Officer invite_button_text: Invite Security Officer ko_review: - performance_start: Period of Performance Start Date - performance_end: Period of Performance End Date task_order_information: Task Order Information testing: example_string: Hello World From be003c18219573cd020396249dce45448c7e5c5b Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 29 Jan 2019 16:02:08 -0500 Subject: [PATCH 08/29] Rebase fixes --- templates/components/datepicker.html | 50 -------------------- templates/portfolios/task_orders/review.html | 2 +- templates/task_orders/new/app_info.html | 12 ----- 3 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 templates/components/datepicker.html diff --git a/templates/components/datepicker.html b/templates/components/datepicker.html deleted file mode 100644 index c597aeb5..00000000 --- a/templates/components/datepicker.html +++ /dev/null @@ -1,50 +0,0 @@ -{% from "components/icon.html" import Icon %} - -{% macro DatePicker(field) -%} - - -
- - - -
- - -
- -
- - -
- -
- - -
- -
- {{ Icon("ok", classes="icon--green") }} -
- -
-
- -{%- endmacro %} diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 97b8d00d..a12e5ec7 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -3,7 +3,7 @@ {% from "components/edit_link.html" import EditLink %} {% from "components/required_label.html" import RequiredLabel %} {% from "components/icon.html" import Icon %} -{% from "components/datepicker.html" import DatePicker %} +{% from "components/date_picker.html" import DatePicker %} {% from "components/text_input.html" import TextInput %} diff --git a/templates/task_orders/new/app_info.html b/templates/task_orders/new/app_info.html index 37813827..85b52a6c 100644 --- a/templates/task_orders/new/app_info.html +++ b/templates/task_orders/new/app_info.html @@ -4,7 +4,6 @@ {% from "components/options_input.html" import OptionsInput %} {% from "components/date_input.html" import DateInput %} {% from "components/multi_checkbox_input.html" import MultiCheckboxInput %} -{% from "components/datepicker.html" import DatePicker %} {% block heading %} {{ "task_orders.new.app_info.section_title"| translate }} @@ -12,17 +11,6 @@ {% block form %} -
- - - For example: 04 28 1986 - - {{ DatePicker() }} -
- -

{{ "task_orders.new.app_info.basic_info_title"| translate }}

{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization") }} From 40780bf244f002ef1aa9ba462c58ff645fffcc8e Mon Sep 17 00:00:00 2001 From: Montana Date: Wed, 30 Jan 2019 11:48:13 -0500 Subject: [PATCH 09/29] Migration to add custom_clauses to TO; form fixes --- ...1c4d28_add_custom_clauses_to_task_order.py | 30 +++++++++++++++++++ atst/forms/ko_review.py | 5 ++-- atst/models/task_order.py | 10 +++++-- atst/routes/portfolios/task_orders.py | 16 +++++----- templates/portfolios/task_orders/review.html | 3 +- 5 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 alembic/versions/0ff4c31c4d28_add_custom_clauses_to_task_order.py diff --git a/alembic/versions/0ff4c31c4d28_add_custom_clauses_to_task_order.py b/alembic/versions/0ff4c31c4d28_add_custom_clauses_to_task_order.py new file mode 100644 index 00000000..789d813b --- /dev/null +++ b/alembic/versions/0ff4c31c4d28_add_custom_clauses_to_task_order.py @@ -0,0 +1,30 @@ +"""Add Custom Clauses to Task Order + +Revision ID: 0ff4c31c4d28 +Revises: da9d1c911a52 +Create Date: 2019-01-30 11:28:37.193854 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '0ff4c31c4d28' +down_revision = 'da9d1c911a52' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('task_orders', 'loa', type_=sa.String(), nullable=True) + op.add_column('task_orders', sa.Column('custom_clauses', sa.String(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('task_orders', 'custom_clauses') + op.alter_column('task_orders', 'loa', type_=sa.ARRAY(sa.String()), nullable=True) + # ### end Alembic commands ### diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 4081b5a3..8b882f21 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -29,7 +29,6 @@ class KOReviewForm(CacheableForm): message=translate("forms.ko_review.invalid_date"), ) ], - format="%m/%d/%Y", ) pdf = FileField( translate("forms.ko_review.pdf_label"), @@ -37,11 +36,11 @@ class KOReviewForm(CacheableForm): validators=[ FileAllowed( ["pdf", "png"], translate("forms.task_order.file_format_not_allowed") - ), + ) ], render_kw={"required": False, "accept": ".pdf,.png,application/pdf,image/png"}, ) - to_number = StringField( + number = StringField( translate("forms.ko_review.to_number"), validators=[Length(min=10), IsNumber()] ) loa = StringField( diff --git a/atst/models/task_order.py b/atst/models/task_order.py index 846f1fbc..c38eb356 100644 --- a/atst/models/task_order.py +++ b/atst/models/task_order.py @@ -72,7 +72,8 @@ class TaskOrder(Base, mixins.TimestampsMixin): 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) + loa = Column(String) # Line of Accounting (LOA) + custom_clauses = Column(String) # Custom Clauses @hybrid_property def csp_estimate(self): @@ -93,7 +94,12 @@ class TaskOrder(Base, mixins.TimestampsMixin): @property def is_submitted(self): - return self.number is not None + + return ( + self.number is not None + and self.start_date is not None + and self.end_date is not None + ) @property def status(self): diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 7d01f190..3a76e0db 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -71,7 +71,7 @@ def view_task_order(portfolio_id, task_order_id): @portfolios_bp.route("/portfolios//task_order//review") -def ko_review(portfolio_id, task_order_id, form=None): +def ko_review(portfolio_id, task_order_id): task_order = TaskOrders.get(g.current_user, task_order_id) # get permission: make sure g.current_user is task_order.contracting_officer portfolio = Portfolios.get(g.current_user, portfolio_id) @@ -79,7 +79,7 @@ def ko_review(portfolio_id, task_order_id, form=None): "/portfolios/task_orders/review.html", portfolio=portfolio, task_order=task_order, - form=form or KOReviewForm(), + form=KOReviewForm(obj=task_order), ) @@ -92,22 +92,20 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): portfolio = Portfolios.get(g.current_user, portfolio_id) if form.validate(): - form_data = {**http_request.form, **http_request.files} - TaskOrders.update(user=g.current_user, task_order=task_order, **form_data) + TaskOrders.update(user=g.current_user, task_order=task_order, **form.data) return redirect( url_for( "portfolios.view_task_order", portfolio_id=portfolio_id, task_order_id=task_order_id, - form=form, ) ) else: return render_template( - "/portfolios/task_orders/review.html", - portfolio=portfolio, - task_order=task_order, - form=form, + "/portfolios/task_orders/review.html", + portfolio=portfolio, + task_order=task_order, + form=form, ) diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index a12e5ec7..dfdcc282 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -213,7 +213,8 @@ {{ form.pdf.label }} {{ form.pdf.description }} {{ form.pdf }} - {{ TextInput(form.to_number) }} + {{ TextInput(form.number) }} + {{ TextInput(form.loa) }} {{ TextInput(form.custom_clauses, paragraph=True) }} From 2e3450ed8bfef1d5b3a6f1d817e30eadfa8bf621 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 31 Jan 2019 11:40:06 -0500 Subject: [PATCH 10/29] Styling --- atst/models/task_order.py | 2 +- styles/components/_forms.scss | 4 +++ templates/components/date_picker.html | 20 ++++++++++++-- templates/fragments/ko_review_alert.html | 7 +++++ templates/portfolios/task_orders/review.html | 29 ++++++++++++++------ translations.yaml | 7 +++++ 6 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 templates/fragments/ko_review_alert.html diff --git a/atst/models/task_order.py b/atst/models/task_order.py index c38eb356..b7c25450 100644 --- a/atst/models/task_order.py +++ b/atst/models/task_order.py @@ -73,7 +73,7 @@ class TaskOrder(Base, mixins.TimestampsMixin): so_dod_id = Column(String) # DOD ID number = Column(String, unique=True) # Task Order Number loa = Column(String) # Line of Accounting (LOA) - custom_clauses = Column(String) # Custom Clauses + custom_clauses = Column(String) # Custom Clauses @hybrid_property def csp_estimate(self): diff --git a/styles/components/_forms.scss b/styles/components/_forms.scss index 598b2dae..a65f93b4 100644 --- a/styles/components/_forms.scss +++ b/styles/components/_forms.scss @@ -126,6 +126,10 @@ width: 100%; } + legend { + @include h4; + } + label { font-weight: 400; } diff --git a/templates/components/date_picker.html b/templates/components/date_picker.html index 41f4d80c..14bbf4e4 100644 --- a/templates/components/date_picker.html +++ b/templates/components/date_picker.html @@ -1,6 +1,11 @@ {% from "components/icon.html" import Icon %} -{% macro DatePicker(field, mindate=None, maxdate=None) -%} +{% macro DatePicker( + field, + label=field.label | striptags, + description=field.description, + mindate=None, + maxdate=None) -%} -
+
+ +
+ {{ label }} +
+ + {% if description %} + {{ description | safe }} + {% endif %} +
@@ -63,7 +77,7 @@ {% if maxdate and not mindate %}Date must be before or on {{maxdate.strftime("%m/%d/%Y")}}{% endif %} {% if mindate and not maxdate %}Date must be after or on {{mindate.strftime("%m/%d/%Y")}}{% endif %}

-
+
{%- endmacro %} diff --git a/templates/fragments/ko_review_alert.html b/templates/fragments/ko_review_alert.html new file mode 100644 index 00000000..473b01bb --- /dev/null +++ b/templates/fragments/ko_review_alert.html @@ -0,0 +1,7 @@ +
+

{{ "fragments.ko_review_alert.make_sure" | translate }}:

+
    +
  • {{ "fragments.ko_review_alert.bullet_1" | translate }}
  • +
  • {{ "fragments.ko_review_alert.bullet_2" | translate }}
  • +
  • {{ "fragments.ko_review_alert.bullet_3" | translate }}
  • +
diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index dfdcc282..42dbc156 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -5,6 +5,7 @@ {% from "components/icon.html" import Icon %} {% from "components/date_picker.html" import DatePicker %} {% from "components/text_input.html" import TextInput %} +{% from "components/alert.html" import Alert %} {% block content %} @@ -56,6 +57,11 @@
{% endmacro %} + {% set message = "task_orders.ko_review.submitted_by" | translate({"name": task_order.creator.full_name}) %} + + {{ Alert(("task_orders.ko_review.alert_title" | translate), message, level='warning', + fragment="fragments/ko_review_alert.html") }} +
@@ -191,8 +197,10 @@
- {{ DatePicker(form.start_date) }} - {{ DatePicker(form.end_date) }} +
+ {{ DatePicker(form.start_date) }} + {{ DatePicker(form.end_date) }} +

@@ -210,13 +218,16 @@
{{ "task_orders.ko_review.task_order_information"| translate }}
- {{ form.pdf.label }} - {{ form.pdf.description }} - {{ form.pdf }} - {{ TextInput(form.number) }} - - {{ TextInput(form.loa) }} - {{ TextInput(form.custom_clauses, paragraph=True) }} +
+
+
{{ form.pdf.label }}
+ {{ form.pdf.description }} + {{ form.pdf }} +
+ {{ TextInput(form.number) }} + {{ TextInput(form.loa) }} + {{ TextInput(form.custom_clauses, paragraph=True) }} +
diff --git a/translations.yaml b/translations.yaml index 4869ff80..a5b65c92 100644 --- a/translations.yaml +++ b/translations.yaml @@ -274,6 +274,11 @@ fragments: learn_more_link_text: Learn more about the JEDI Cloud Task Order and the Financial Verification process. paragraph_1: 'The next step is to create a Task Order associated with JEDI Cloud. Please contact a Contracting Officer (KO), Contracting Officer Representative (COR), or a Financial Manager to help with this step.' paragraph_2: 'Once the Task Order has been created, you will be asked to provide details about the task order in the Financial Verification step.' + ko_review_alert: + make_sure: Make sure to take a moment to + bullet_1: Verify that all information is accurate and up-to-date + bullet_2: Upload your Task Order (TO) document + bullet_3: Add both the Task Order (TO) and Lines of Accounting (LOA) login: ccpo_logo_alt_text: Cloud Computing Program Office Logo certificate_selection: @@ -467,6 +472,8 @@ task_orders: add_button_text: Add / Invite Security Officer invite_button_text: Invite Security Officer ko_review: + alert_title: Verify Your Info + submitted_by: Below is an overview of the projected portfolio submitted by {name} task_order_information: Task Order Information testing: example_string: Hello World From 4ed445dd99def24fe5662f3b7c28df85adda48c1 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 31 Jan 2019 13:44:42 -0500 Subject: [PATCH 11/29] Fix rendering bug --- atst/forms/data.py | 4 ++-- translations.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index d8619984..6b6125fe 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -197,8 +197,8 @@ APPLICATION_COMPLEXITY = [ DEV_TEAM = [ ( - "government_civilians", - translate("forms.task_order.dev_team.government_civilians"), + "civilians", + translate("forms.task_order.dev_team.civilians"), ), ("military", translate("forms.task_order.dev_team.military")), ("contractor", translate("forms.task_order.dev_team.contractor")), diff --git a/translations.yaml b/translations.yaml index a5b65c92..0c33c1a4 100644 --- a/translations.yaml +++ b/translations.yaml @@ -208,7 +208,7 @@ forms: dev_team: 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 + civilians: Government Civilians military: Military contractor: Contractor other: "Other (E.g. University or other partner)" From 516ed9b90e929c0051b9b1a3c8b7c43f89384ffe Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 31 Jan 2019 13:45:00 -0500 Subject: [PATCH 12/29] Only the KO can view --- atst/forms/data.py | 5 +---- atst/forms/ko_review.py | 2 +- atst/routes/portfolios/task_orders.py | 20 +++++++++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index 6b6125fe..b7b8469c 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -196,10 +196,7 @@ APPLICATION_COMPLEXITY = [ ] DEV_TEAM = [ - ( - "civilians", - translate("forms.task_order.dev_team.civilians"), - ), + ("civilians", translate("forms.task_order.dev_team.civilians")), ("military", translate("forms.task_order.dev_team.military")), ("contractor", translate("forms.task_order.dev_team.contractor")), ("other", translate("forms.task_order.dev_team.other")), diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 8b882f21..35ff6be0 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -3,7 +3,7 @@ from flask_wtf.file import FileAllowed from wtforms.fields.html5 import DateField from wtforms.fields import StringField, TextAreaField, FileField -from wtforms.validators import Optional, Length, InputRequired +from wtforms.validators import Optional, Length from .forms import CacheableForm from .validators import IsNumber, DateRange diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 3a76e0db..af5ac64a 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -73,14 +73,20 @@ def view_task_order(portfolio_id, task_order_id): @portfolios_bp.route("/portfolios//task_order//review") def ko_review(portfolio_id, task_order_id): task_order = TaskOrders.get(g.current_user, task_order_id) - # get permission: make sure g.current_user is task_order.contracting_officer portfolio = Portfolios.get(g.current_user, portfolio_id) - return render_template( - "/portfolios/task_orders/review.html", - portfolio=portfolio, - task_order=task_order, - form=KOReviewForm(obj=task_order), - ) + if task_order.contracting_officer == g.current_user: + return render_template( + "/portfolios/task_orders/review.html", + portfolio=portfolio, + task_order=task_order, + form=KOReviewForm(obj=task_order), + ) + else: + return render_template( + "portfolios/task_orders/show.html", + portfolio=portfolio, + task_order=task_order, + ) @portfolios_bp.route( From d980260e7f07c402f216d3ed0f606c80d31e468a Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 31 Jan 2019 15:17:09 -0500 Subject: [PATCH 13/29] Update test --- tests/models/test_task_order.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/models/test_task_order.py b/tests/models/test_task_order.py index 62ebcfaf..560c6989 100644 --- a/tests/models/test_task_order.py +++ b/tests/models/test_task_order.py @@ -1,5 +1,5 @@ from werkzeug.datastructures import FileStorage -import pytest +import pytest, datetime from atst.models.attachment import Attachment from atst.models.task_order import TaskOrder, Status @@ -33,7 +33,11 @@ def test_is_submitted(): to = TaskOrder() assert not to.is_submitted - to = TaskOrder(number="42") + to = TaskOrder( + number="42", + start_date=datetime.date.today(), + end_date=datetime.date.today() + datetime.timedelta(days=1), + ) assert to.is_submitted From 70b4a51d8afb54755862220949099d0942e0efab Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 1 Feb 2019 10:35:19 -0500 Subject: [PATCH 14/29] Save start and end dates --- atst/forms/ko_review.py | 2 ++ templates/components/date_picker.html | 1 + 2 files changed, 3 insertions(+) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 35ff6be0..4f1a2c9d 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -20,6 +20,7 @@ class KOReviewForm(CacheableForm): message=translate("forms.ko_review.invalid_date"), ) ], + format="%m/%d/%Y", ) end_date = DateField( translate("forms.ko_review.end_date_label"), @@ -29,6 +30,7 @@ class KOReviewForm(CacheableForm): message=translate("forms.ko_review.invalid_date"), ) ], + format="%m/%d/%Y", ) pdf = FileField( translate("forms.ko_review.pdf_label"), diff --git a/templates/components/date_picker.html b/templates/components/date_picker.html index 14bbf4e4..55b033bf 100644 --- a/templates/components/date_picker.html +++ b/templates/components/date_picker.html @@ -65,6 +65,7 @@ {% if maxdate %}max="{{ maxdate.year }}"{% endif %} {% if mindate %}min="{{ mindate.year }}"{% endif %} /> +
From 49ed0598539eede77958465fce30bb2573f264dd Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 1 Feb 2019 10:49:34 -0500 Subject: [PATCH 15/29] Raise AuthorizationError if user is not KO --- atst/domain/authz.py | 8 ++++++-- atst/forms/ko_review.py | 6 ++---- atst/routes/portfolios/task_orders.py | 12 +++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/atst/domain/authz.py b/atst/domain/authz.py index 5b476aad..9f4d4cf6 100644 --- a/atst/domain/authz.py +++ b/atst/domain/authz.py @@ -36,9 +36,13 @@ class Authorization(object): def is_ccpo(cls, user): return user.atat_role.name == "ccpo" + @classmethod + def is_ko(cls, user, task_order): + return task_order.contracting_officer == user + @classmethod def check_task_order_permission(cls, user, task_order, permission, message): - if Authorization._check_is_task_order_officer(task_order, user): + if Authorization._check_is_task_order_officer(user, task_order): return True Authorization.check_portfolio_permission( @@ -46,7 +50,7 @@ class Authorization(object): ) @classmethod - def _check_is_task_order_officer(cls, task_order, user): + def _check_is_task_order_officer(cls, user, task_order): for officer in [ "contracting_officer", "contracting_officer_representative", diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 4f1a2c9d..4664b80d 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -36,11 +36,9 @@ class KOReviewForm(CacheableForm): translate("forms.ko_review.pdf_label"), description=translate("forms.ko_review.pdf_description"), validators=[ - FileAllowed( - ["pdf", "png"], translate("forms.task_order.file_format_not_allowed") - ) + FileAllowed(["pdf"], translate("forms.task_order.file_format_not_allowed")) ], - render_kw={"required": False, "accept": ".pdf,.png,application/pdf,image/png"}, + render_kw={"required": False, "accept": ".pdf,application/pdf"}, ) number = StringField( translate("forms.ko_review.to_number"), validators=[Length(min=10), IsNumber()] diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index af5ac64a..a35445a2 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -7,6 +7,7 @@ from . import portfolios_bp from atst.database import db from atst.domain.task_orders import TaskOrders from atst.domain.portfolios import Portfolios +from atst.domain.authz import Authorization from atst.forms.officers import EditTaskOrderOfficersForm from atst.models.task_order import Status as TaskOrderStatus from atst.forms.ko_review import KOReviewForm @@ -74,19 +75,16 @@ def view_task_order(portfolio_id, task_order_id): def ko_review(portfolio_id, task_order_id): task_order = TaskOrders.get(g.current_user, task_order_id) portfolio = Portfolios.get(g.current_user, portfolio_id) - if task_order.contracting_officer == g.current_user: + if not Authorization.is_ko(g.current_user, task_order): + message = "review Task Order {}".format(task_order.id) + raise UnauthorizedError(g.current_user, message) + else: return render_template( "/portfolios/task_orders/review.html", portfolio=portfolio, task_order=task_order, form=KOReviewForm(obj=task_order), ) - else: - return render_template( - "portfolios/task_orders/show.html", - portfolio=portfolio, - task_order=task_order, - ) @portfolios_bp.route( From 377149c76672883ca8bf20d213ed3a6d54a6d1c2 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 1 Feb 2019 14:51:40 -0500 Subject: [PATCH 16/29] Raise Unauthorized error in authz instead of route --- atst/domain/authz.py | 6 ++++-- atst/forms/ko_review.py | 2 +- atst/routes/portfolios/task_orders.py | 19 ++++++++----------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/atst/domain/authz.py b/atst/domain/authz.py index 9f4d4cf6..de3c2156 100644 --- a/atst/domain/authz.py +++ b/atst/domain/authz.py @@ -37,8 +37,10 @@ class Authorization(object): return user.atat_role.name == "ccpo" @classmethod - def is_ko(cls, user, task_order): - return task_order.contracting_officer == user + def check_is_ko(cls, user, task_order): + if task_order.contracting_officer != user: + message = "review Task Order {}".format(task_order.id) + raise UnauthorizedError(user, message) @classmethod def check_task_order_permission(cls, user, task_order, permission, message): diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 4664b80d..4baaa032 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -41,7 +41,7 @@ class KOReviewForm(CacheableForm): render_kw={"required": False, "accept": ".pdf,application/pdf"}, ) number = StringField( - translate("forms.ko_review.to_number"), validators=[Length(min=10), IsNumber()] + translate("forms.ko_review.to_number"), validators=[Length(min=10)] ) loa = StringField( translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()] diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index a35445a2..5ed29cb6 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -75,16 +75,13 @@ def view_task_order(portfolio_id, task_order_id): def ko_review(portfolio_id, task_order_id): task_order = TaskOrders.get(g.current_user, task_order_id) portfolio = Portfolios.get(g.current_user, portfolio_id) - if not Authorization.is_ko(g.current_user, task_order): - message = "review Task Order {}".format(task_order.id) - raise UnauthorizedError(g.current_user, message) - else: - return render_template( - "/portfolios/task_orders/review.html", - portfolio=portfolio, - task_order=task_order, - form=KOReviewForm(obj=task_order), - ) + Authorization.check_is_ko(g.current_user, task_order) + return render_template( + "/portfolios/task_orders/review.html", + portfolio=portfolio, + task_order=task_order, + form=KOReviewForm(obj=task_order), + ) @portfolios_bp.route( @@ -95,7 +92,7 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): form = KOReviewForm(http_request.form) portfolio = Portfolios.get(g.current_user, portfolio_id) - if form.validate(): + if form.validate() and Authorization.check_is_ko(g.current_user, task_order): TaskOrders.update(user=g.current_user, task_order=task_order, **form.data) return redirect( url_for( From 848a4ac276a454730806b3445b81fdb65bc5f116 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 09:31:19 -0500 Subject: [PATCH 17/29] Use html partials for TO review pages --- atst/routes/portfolios/task_orders.py | 3 +- templates/components/review_field.html | 11 ++ .../fragments/task_order_review/app_info.html | 9 + .../fragments/task_order_review/funding.html | 72 ++++++++ .../task_order_review/oversight.html | 25 +++ .../task_order_review/reporting.html | 41 +++++ templates/portfolios/task_orders/review.html | 171 +----------------- templates/task_orders/new/review.html | 132 +------------- 8 files changed, 169 insertions(+), 295 deletions(-) create mode 100644 templates/components/review_field.html create mode 100644 templates/fragments/task_order_review/app_info.html create mode 100644 templates/fragments/task_order_review/funding.html create mode 100644 templates/fragments/task_order_review/oversight.html create mode 100644 templates/fragments/task_order_review/reporting.html diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 5ed29cb6..7fe47504 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -92,7 +92,8 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): form = KOReviewForm(http_request.form) portfolio = Portfolios.get(g.current_user, portfolio_id) - if form.validate() and Authorization.check_is_ko(g.current_user, task_order): + if form.validate(): + Authorization.check_is_ko(g.current_user, task_order) TaskOrders.update(user=g.current_user, task_order=task_order, **form.data) return redirect( url_for( diff --git a/templates/components/review_field.html b/templates/components/review_field.html new file mode 100644 index 00000000..e05e8c70 --- /dev/null +++ b/templates/components/review_field.html @@ -0,0 +1,11 @@ +{% macro ReviewField(heading, field, filter=None) %} +
+

{{ heading }}

+ {% if field %} +

{{ field | findFilter(filter) }}

+ {% endif %} + {% if caller %} + {{ caller() }} + {% endif %} +
+{% endmacro %} diff --git a/templates/fragments/task_order_review/app_info.html b/templates/fragments/task_order_review/app_info.html new file mode 100644 index 00000000..93db4525 --- /dev/null +++ b/templates/fragments/task_order_review/app_info.html @@ -0,0 +1,9 @@ +{% from "components/review_field.html" import ReviewField %} + +
+ {{ ReviewField(("task_orders.new.review.portfolio" | translate), task_order.portfolio_name) }} + {{ ReviewField(("task_orders.new.review.dod" | translate), task_order.defense_component, filter="normalizeOrder") }} +
+
+ {{ ReviewField(("task_orders.new.review.scope" | translate), task_order.scope) }} +
diff --git a/templates/fragments/task_order_review/funding.html b/templates/fragments/task_order_review/funding.html new file mode 100644 index 00000000..8b9e1225 --- /dev/null +++ b/templates/fragments/task_order_review/funding.html @@ -0,0 +1,72 @@ +{% from "components/review_field.html" import ReviewField %} + +
+ {% call ReviewField(("task_orders.new.review.performance_period" | translate), task_order.performance_length, filter="translateDuration") %} + {% if task_order.csp_estimate %} +

+ {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} +

+ {% else %} +
+ {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} + {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_uploaded"| translate }} + {% endif %} + {% endcall %} + +
+ + + + + + + + + + + + + + + + + + + + + + + +

{{ "task_orders.new.review.to_value"| translate }}

+ {% if task_order.budget %} + {{ task_order.budget | dollarsWithCents }} + {% endif %} +

{{ "task_orders.new.review.clin_1"| translate }}

+ {% if task_order.clin_01 %} + {{ task_order.clin_01 | dollarsWithCents }} + {% endif %} +

+ {{ "task_orders.new.review.clin_2"| translate }} + {% if not config.CLASSIFIED %} +
{{ "task_orders.new.review.classified_inactive"| translate }}
+ {% endif %} +

+ {% if task_order.clin_02 and config.CLASSIFIED %} + {{ task_order.clin_02 | dollarsWithCents or RequiredLabel() }} + {% endif %} +

{{ "task_orders.new.review.clin_3"| translate }}

+ {% if task_order.clin_03 %} + {{ task_order.clin_03 | dollarsWithCents or RequiredLabel() }} + {% endif %} +

+ {{ "task_orders.new.review.clin_4"| translate }} + {% if not config.CLASSIFIED %} +
{{ "task_orders.new.review.classified_inactive"| translate }}
+ {% endif %} +

+ {% if task_order.clin_04 and config.CLASSIFIED %} + {{ task_order.clin_04 | dollarsWithCents or RequiredLabel() }} + {% endif %} +
+
+
diff --git a/templates/fragments/task_order_review/oversight.html b/templates/fragments/task_order_review/oversight.html new file mode 100644 index 00000000..cc519892 --- /dev/null +++ b/templates/fragments/task_order_review/oversight.html @@ -0,0 +1,25 @@ +{% macro ReviewOfficerInfo(heading, first_name, last_name, email, phone_number, dod_id, officer) %} +
+

{{ heading | translate }}

+ {{ first_name }} {{ last_name }}
+ {{ email }}
+ {% if phone_number %} + {{ phone_number | usPhone }} + {% endif %} +
+ {{ "task_orders.new.review.dod_id" | translate }} {{ dod_id}}
+ {% if officer %} + {{ Icon('ok', classes='icon--green') }} {{ "task_orders.new.review.invited"| translate }} + {% else %} + {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_invited"| translate }} + {% endif %} +
+{% endmacro %} + +
+ {{ ReviewOfficerInfo("task_orders.new.review.ko", task_order.ko_first_name, task_order.ko_last_name, task_order.ko_email, task_order.ko_phone_number, task_order.ko_dod_id, task_order.contracting_officer) }} + {{ ReviewOfficerInfo("task_orders.new.review.cor", task_order.cor_first_name, task_order.cor_last_name, task_order.cor_email, task_order.cor_phone_number, task_order.cor_dod_id, task_order.contracting_officer_representative) }} +
+
+ {{ ReviewOfficerInfo("task_orders.new.review.so", task_order.so_first_name, task_order.so_last_name, task_order.so_email, task_order.so_phone_number, task_order.so_dod_id, task_order.security_officer) }} +
diff --git a/templates/fragments/task_order_review/reporting.html b/templates/fragments/task_order_review/reporting.html new file mode 100644 index 00000000..0b30db0c --- /dev/null +++ b/templates/fragments/task_order_review/reporting.html @@ -0,0 +1,41 @@ +{% from "components/review_field.html" import ReviewField %} + +
+ {{ ReviewField(("forms.task_order.app_migration.label" | translate), ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate), filter="removeHtml") }} + {{ ReviewField(("forms.task_order.native_apps.label" | translate), ("forms.task_order.native_apps.{}".format(task_order.native_apps))| translate) }} +
+ +

{{ "task_orders.new.review.complexity"| translate }}

+{% if task_order.complexity %} +
    + {% for item in task_order.complexity %} +
  • + {{ Icon('ok', classes='icon--gray icon--medium') }}{{ "forms.task_order.complexity.{}".format(item) | translate }}{% if item == 'other' %}: {{ task_order.complexity_other }}{% endif %} +
  • + {% endfor %} +
+{% else %} +

{{ RequiredLabel() }}

+{% endif %} + +
+
+

{{ "task_orders.new.review.team"| translate }}

+ {% if task_order.dev_team %} +
    + {% for item in task_order.dev_team %} +
  • + {% 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 %} +
  • + {% endfor %} +
+ {% else %} +

{{ RequiredLabel() }}

+ {% endif %} +
+ {{ ReviewField(("forms.task_order.team_experience.label" |translate), ("forms.task_order.team_experience.{}".format(task_order.team_experience)) | translate) }} +
diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 42dbc156..e58d3850 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -6,7 +6,7 @@ {% from "components/date_picker.html" import DatePicker %} {% from "components/text_input.html" import TextInput %} {% from "components/alert.html" import Alert %} - +{% from "components/review_field.html" import ReviewField %} {% block content %} @@ -19,44 +19,6 @@ {% block form %} - {% macro TOEditLink(screen=1, anchor=None) %} - {% if task_order %} - {{ EditLink(url_for("task_orders.new", screen=screen, task_order_id=task_order.id, _anchor=anchor)) }} - {% else %} - {{ EditLink(url_for("task_orders.new", screen=screen, _anchor=anchor)) }} - {% endif %} - {% endmacro %} - - {% macro ReviewField(heading, field, filter=None) %} -
-

{{ heading }}

- {% if field %} -

{{ field | findFilter(filter) }}

- {% endif %} - {% if caller %} - {{ caller() }} - {% endif %} -
- {% endmacro %} - - {% macro ReviewOfficerInfo(heading, first_name, last_name, email, phone_number, dod_id, officer) %} -
-

{{ heading | translate }}

- {{ first_name }} {{ last_name }}
- {{ email }}
- {% if phone_number %} - {{ phone_number | usPhone }} - {% endif %} -
- {{ "task_orders.new.review.dod_id" | translate }} {{ dod_id}}
- {% if officer %} - {{ Icon('ok', classes='icon--green') }} {{ "task_orders.new.review.invited"| translate }} - {% else %} - {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_invited"| translate }} - {% endif %} -
- {% endmacro %} - {% set message = "task_orders.ko_review.submitted_by" | translate({"name": task_order.creator.full_name}) %} {{ Alert(("task_orders.ko_review.alert_title" | translate), message, level='warning', @@ -73,147 +35,24 @@
{{ "task_orders.new.review.app_info"| translate }}
- -
- {{ ReviewField(("task_orders.new.review.portfolio" | translate), task_order.portfolio_name) }} - {{ ReviewField(("task_orders.new.review.dod" | translate), task_order.defense_component, filter="normalizeOrder") }} -
-
- {{ ReviewField(("task_orders.new.review.scope" | translate), task_order.scope) }} -
+ {% include "fragments/task_order_review/app_info.html" %}
{{ "task_orders.new.review.reporting"| translate }}
- -
- {{ ReviewField(("forms.task_order.app_migration.label" | translate), ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate), filter="removeHtml") }} - {{ ReviewField(("forms.task_order.native_apps.label" | translate), ("forms.task_order.native_apps.{}".format(task_order.native_apps))| translate) }} -
- -

{{ "task_orders.new.review.complexity"| translate }}

- {% if task_order.complexity %} -
    - {% for item in task_order.complexity %} -
  • - {{ Icon('ok', classes='icon--gray icon--medium') }}{{ "forms.task_order.complexity.{}".format(item) | translate }}{% if item == 'other' %}: {{ task_order.complexity_other }}{% endif %} -
  • - {% endfor %} -
- {% endif %} - -
-
-

{{ "task_orders.new.review.team"| translate }}

- {% if task_order.dev_team %} -
    - {% for item in task_order.dev_team %} -
  • - {% 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 %} -
  • - {% endfor %} -
- {% endif %} -
- - {{ ReviewField(("forms.task_order.team_experience.label" |translate), ("forms.task_order.team_experience.{}".format(task_order.team_experience)) | translate) }} -
- + {% include "fragments/task_order_review/reporting.html" %}
{{ "task_orders.new.review.funding"| translate }}
- -
- {% call ReviewField(("task_orders.new.review.performance_period" | translate), task_order.performance_length, filter="translateDuration") %} - {% if task_order.csp_estimate %} -

- {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} -

- {% else %} -
- {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} - {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_uploaded"| translate }} - {% endif %} - {% endcall %} - -
- - - - - - - - - - - - - - - - - - - - - - - -

{{ "task_orders.new.review.to_value"| translate }}

- {% if task_order.budget %} - {{ task_order.budget | dollarsWithCents }} - {% endif %} -

{{ "task_orders.new.review.clin_1"| translate }}

- {% if task_order.clin_01 %} - {{ task_order.clin_01 | dollarsWithCents }} - {% endif %} -

- {{ "task_orders.new.review.clin_2"| translate }} - {% if not config.CLASSIFIED %} -
{{ "task_orders.new.review.classified_inactive"| translate }}
- {% endif %} -

- {% if task_order.clin_02 and config.CLASSIFIED %} - {{ task_order.clin_02 | dollarsWithCents or RequiredLabel() }} - {% endif %} -

{{ "task_orders.new.review.clin_3"| translate }}

- {% if task_order.clin_03 %} - {{ task_order.clin_03 | dollarsWithCents or RequiredLabel() }} - {% endif %} -

- {{ "task_orders.new.review.clin_4"| translate }} - {% if not config.CLASSIFIED %} -
{{ "task_orders.new.review.classified_inactive"| translate }}
- {% endif %} -

- {% if task_order.clin_04 and config.CLASSIFIED %} - {{ task_order.clin_04 | dollarsWithCents or RequiredLabel() }} - {% endif %} -
-
-
+ {% include "fragments/task_order_review/funding.html" %}
{{ DatePicker(form.start_date) }} {{ DatePicker(form.end_date) }}
-
{{ "task_orders.new.review.oversight"| translate }}
- -
- {{ ReviewOfficerInfo("task_orders.new.review.ko", task_order.ko_first_name, task_order.ko_last_name, task_order.ko_email, task_order.ko_phone_number, task_order.ko_dod_id, task_order.contracting_officer) }} - {{ ReviewOfficerInfo("task_orders.new.review.cor", task_order.cor_first_name, task_order.cor_last_name, task_order.cor_email, task_order.cor_phone_number, task_order.cor_dod_id, task_order.contracting_officer_representative) }} -
-
- {{ ReviewOfficerInfo("task_orders.new.review.so", task_order.so_first_name, task_order.so_last_name, task_order.so_email, task_order.so_phone_number, task_order.so_dod_id, task_order.security_officer) }} -
- + {% include "fragments/task_order_review/oversight.html" %}
{{ "task_orders.ko_review.task_order_information"| translate }}
diff --git a/templates/task_orders/new/review.html b/templates/task_orders/new/review.html index dcca1abf..8834da68 100644 --- a/templates/task_orders/new/review.html +++ b/templates/task_orders/new/review.html @@ -3,6 +3,7 @@ {% from "components/edit_link.html" import EditLink %} {% from "components/required_label.html" import RequiredLabel %} {% from "components/icon.html" import Icon %} +{% from "components/review_field.html" import ReviewField %} {% block heading %} {{ "task_orders.new.review.section_title"| translate }} @@ -18,50 +19,9 @@ {% endif %} {% endmacro %} -{% macro ReviewField(heading, field, filter=None) %} -
-

{{ heading }}

- {% if field %} -

{{ field | findFilter(filter) }}

- {% else %} - {{ RequiredLabel() }} - {% endif %} - {% if caller %} - {{ caller() }} - {% endif %} -
-{% endmacro %} - -{% macro ReviewOfficerInfo(heading, first_name, last_name, email, phone_number, dod_id, officer) %} -
-

{{ heading | translate }}

- {{ first_name }} {{ last_name }}
- {{ email }}
- {% if phone_number %} - {{ phone_number | usPhone }} - {% else %} - {{ RequiredLabel() }} - {% endif %} -
- {{ "task_orders.new.review.dod_id" | translate }} {{ dod_id}}
- {% if officer %} - {{ Icon('ok', classes='icon--green') }} {{ "task_orders.new.review.invited"| translate }} - {% else %} - {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_invited"| translate }} - {% endif %} -
-{% endmacro %} -

{{ "task_orders.new.review.app_info"| translate }} {{ TOEditLink(screen=1) }}

- -
- {{ ReviewField(("task_orders.new.review.portfolio" | translate), task_order.portfolio_name) }} - {{ ReviewField(("task_orders.new.review.dod" | translate), task_order.defense_component, filter="normalizeOrder") }} -
-
- {{ ReviewField(("task_orders.new.review.scope" | translate), task_order.scope) }} -
+{% include "fragments/task_order_review/app_info.html" %}

{{ "task_orders.new.review.reporting"| translate }} {{ TOEditLink(screen=1, anchor="reporting") }}

@@ -133,95 +93,11 @@

{{ "task_orders.new.review.funding"| translate }} {{ TOEditLink(screen=2) }}

- -
- {% call ReviewField(("task_orders.new.review.performance_period" | translate), task_order.performance_length, filter="translateDuration") %} - {% if task_order.csp_estimate %} -

- {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }} -

- {% else %} -

- {{ Icon('download') }} {{ "task_orders.new.review.usage_est_link"| translate }}
- {{ Icon('alert', classes='icon--red') }} {{ "task_orders.new.review.not_uploaded"| translate }} -

- {% endif %} - {% endcall %} - -
- - - - - - - - - - - - - - - - - - - - - - - -

{{ "task_orders.new.review.to_value"| translate }}

- {% if task_order.budget %} - {{ task_order.budget | dollars }} - {% endif %} -

{{ "task_orders.new.review.clin_1"| translate }}

- {% if task_order.clin_01 %} - {{ task_order.clin_01 | dollars }} - {% else %} - {{ RequiredLabel() }} - {% endif %} -

- {{ "task_orders.new.review.clin_2"| translate }} - {% if not config.CLASSIFIED %} -
{{ "task_orders.new.review.classified_inactive"| translate }}
- {% endif %} -

- {% if task_order.clin_02 and config.CLASSIFIED %} - {{ task_order.clin_02 | dollars or RequiredLabel() }} - {% endif %} -

{{ "task_orders.new.review.clin_3"| translate }}

- {% if task_order.clin_03 %} - {{ task_order.clin_03 | dollars or RequiredLabel() }} - {% else %} - {{ RequiredLabel() }} - {% endif %} -

- {{ "task_orders.new.review.clin_4"| translate }} - {% if not config.CLASSIFIED %} -
{{ "task_orders.new.review.classified_inactive"| translate }}
- {% endif %} -

- {% if task_order.clin_04 and config.CLASSIFIED %} - {{ task_order.clin_04 | dollars or RequiredLabel() }} - {% endif %} -
-
-
- +{% include "fragments/task_order_review/funding.html" %}

{{ "task_orders.new.review.oversight"| translate }} {{ TOEditLink(screen=3) }}

- -
- {{ ReviewOfficerInfo("task_orders.new.review.ko", task_order.ko_first_name, task_order.ko_last_name, task_order.ko_email, task_order.ko_phone_number, task_order.ko_dod_id, task_order.contracting_officer) }} - {{ ReviewOfficerInfo("task_orders.new.review.cor", task_order.cor_first_name, task_order.cor_last_name, task_order.cor_email, task_order.cor_phone_number, task_order.cor_dod_id, task_order.contracting_officer_representative) }} -
-
- {{ ReviewOfficerInfo("task_orders.new.review.so", task_order.so_first_name, task_order.so_last_name, task_order.so_email, task_order.so_phone_number, task_order.so_dod_id, task_order.security_officer) }} -
- +{% include "fragments/task_order_review/oversight.html" %} {% endblock %} {% block next %} From 7abbde2817661eb4a53c35dbc921b59b6906510e Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 09:51:43 -0500 Subject: [PATCH 18/29] CI did not like portfolio variable --- atst/routes/portfolios/task_orders.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 7fe47504..565d11e4 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -90,7 +90,6 @@ def ko_review(portfolio_id, task_order_id): def submit_ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) form = KOReviewForm(http_request.form) - portfolio = Portfolios.get(g.current_user, portfolio_id) if form.validate(): Authorization.check_is_ko(g.current_user, task_order) @@ -105,7 +104,7 @@ def submit_ko_review(portfolio_id, task_order_id, form=None): else: return render_template( "/portfolios/task_orders/review.html", - portfolio=portfolio, + portfolio=Portfolios.get(g.current_user, portfolio_id), task_order=task_order, form=form, ) From 7305ae0ddff52b9c746c40adfd0194a00fb64f2a Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 09:56:51 -0500 Subject: [PATCH 19/29] Translate KO Review page title --- templates/portfolios/task_orders/review.html | 2 +- translations.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index e58d3850..854da35b 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -28,7 +28,7 @@

-
Task Order Builder
+
{{ "task_orders.ko_review.title" | translate }}
{{ "task_orders.new.review.section_title"| translate }}

diff --git a/translations.yaml b/translations.yaml index 0c33c1a4..70ebe56c 100644 --- a/translations.yaml +++ b/translations.yaml @@ -473,6 +473,7 @@ task_orders: invite_button_text: Invite Security Officer ko_review: alert_title: Verify Your Info + title: Task Order Builder submitted_by: Below is an overview of the projected portfolio submitted by {name} task_order_information: Task Order Information testing: From d7eb8ab56fe1d64e2909fe7b26128301ea6d6a7f Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 13:58:02 -0500 Subject: [PATCH 20/29] Add in some template code that was lost in a rebase --- .../fragments/task_order_review/funding.html | 10 +++---- .../task_order_review/reporting.html | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/templates/fragments/task_order_review/funding.html b/templates/fragments/task_order_review/funding.html index 8b9e1225..f1df6364 100644 --- a/templates/fragments/task_order_review/funding.html +++ b/templates/fragments/task_order_review/funding.html @@ -20,7 +20,7 @@

{{ "task_orders.new.review.to_value"| translate }}

{% if task_order.budget %} - {{ task_order.budget | dollarsWithCents }} + {{ task_order.budget | dollars }} {% endif %} @@ -28,7 +28,7 @@

{{ "task_orders.new.review.clin_1"| translate }}

{% if task_order.clin_01 %} - {{ task_order.clin_01 | dollarsWithCents }} + {{ task_order.clin_01 | dollars }} {% endif %} @@ -41,7 +41,7 @@ {% if task_order.clin_02 and config.CLASSIFIED %} - {{ task_order.clin_02 | dollarsWithCents or RequiredLabel() }} + {{ task_order.clin_02 | dollars or RequiredLabel() }} {% endif %} @@ -49,7 +49,7 @@

{{ "task_orders.new.review.clin_3"| translate }}

{% if task_order.clin_03 %} - {{ task_order.clin_03 | dollarsWithCents or RequiredLabel() }} + {{ task_order.clin_03 | dollars or RequiredLabel() }} {% endif %} @@ -62,7 +62,7 @@ {% if task_order.clin_04 and config.CLASSIFIED %} - {{ task_order.clin_04 | dollarsWithCents or RequiredLabel() }} + {{ task_order.clin_04 | dollars or RequiredLabel() }} {% endif %} diff --git a/templates/fragments/task_order_review/reporting.html b/templates/fragments/task_order_review/reporting.html index 0b30db0c..b493a00b 100644 --- a/templates/fragments/task_order_review/reporting.html +++ b/templates/fragments/task_order_review/reporting.html @@ -1,8 +1,23 @@ {% from "components/review_field.html" import ReviewField %}
- {{ ReviewField(("forms.task_order.app_migration.label" | translate), ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate), filter="removeHtml") }} - {{ ReviewField(("forms.task_order.native_apps.label" | translate), ("forms.task_order.native_apps.{}".format(task_order.native_apps))| translate) }} + {{ + ReviewField( + ("forms.task_order.app_migration.label" | translate), + ( + ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate) if task_order.app_migration + ) + ) + }} + + {{ + ReviewField( + ("forms.task_order.native_apps.label" | translate), + ( + ("forms.task_order.native_apps.{}".format(task_order.native_apps) | translate) if task_order.native_apps + ) + ) + }}

{{ "task_orders.new.review.complexity"| translate }}

@@ -37,5 +52,12 @@

{{ RequiredLabel() }}

{% endif %}
- {{ ReviewField(("forms.task_order.team_experience.label" |translate), ("forms.task_order.team_experience.{}".format(task_order.team_experience)) | translate) }} + {{ + ReviewField( + ("forms.task_order.team_experience.label" |translate), + ( + ("forms.task_order.team_experience.{}".format(task_order.team_experience) | translate) if task_order.team_experience + ) + ) + }}
From d0243f32b18c53ed68b187c54c546917e21f8c28 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 14:36:27 -0500 Subject: [PATCH 21/29] Authorize user before validating form --- atst/routes/portfolios/task_orders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 565d11e4..0615ebda 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -88,11 +88,11 @@ def ko_review(portfolio_id, task_order_id): "/portfolios//task_order//review", methods=["POST"] ) def submit_ko_review(portfolio_id, task_order_id, form=None): + Authorization.check_is_ko(g.current_user, task_order) task_order = TaskOrders.get(g.current_user, task_order_id) form = KOReviewForm(http_request.form) if form.validate(): - Authorization.check_is_ko(g.current_user, task_order) TaskOrders.update(user=g.current_user, task_order=task_order, **form.data) return redirect( url_for( From 4d2b15a4f51c15f7696b27286448b27176842c28 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 4 Feb 2019 14:42:47 -0500 Subject: [PATCH 22/29] Test KO can view KO Review page --- atst/routes/portfolios/task_orders.py | 2 +- tests/routes/portfolios/test_task_orders.py | 91 +++++++++++++-------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index 0615ebda..f9ef106f 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -88,8 +88,8 @@ def ko_review(portfolio_id, task_order_id): "/portfolios//task_order//review", methods=["POST"] ) def submit_ko_review(portfolio_id, task_order_id, form=None): - Authorization.check_is_ko(g.current_user, task_order) task_order = TaskOrders.get(g.current_user, task_order_id) + Authorization.check_is_ko(g.current_user, task_order) form = KOReviewForm(http_request.form) if form.validate(): diff --git a/tests/routes/portfolios/test_task_orders.py b/tests/routes/portfolios/test_task_orders.py index ed2f1c82..48d4aa21 100644 --- a/tests/routes/portfolios/test_task_orders.py +++ b/tests/routes/portfolios/test_task_orders.py @@ -68,41 +68,6 @@ class TestPortfolioFunding: assert context["total_balance"] == active_to1.budget + active_to2.budget -def test_ko_can_view_task_order(client, user_session): - portfolio = PortfolioFactory.create() - ko = UserFactory.create() - PortfolioRoleFactory.create( - role=Roles.get("officer"), - portfolio=portfolio, - user=ko, - status=PortfolioStatus.ACTIVE, - ) - task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko) - user_session(ko) - response = client.get( - url_for( - "portfolios.view_task_order", - portfolio_id=portfolio.id, - task_order_id=task_order.id, - ) - ) - assert response.status_code == 200 - - -def test_can_view_task_order_invitations(client, user_session): - portfolio = PortfolioFactory.create() - user_session(portfolio.owner) - task_order = TaskOrderFactory.create(portfolio=portfolio) - response = client.get( - url_for( - "portfolios.task_order_invitations", - portfolio_id=portfolio.id, - task_order_id=task_order.id, - ) - ) - assert response.status_code == 200 - - class TestTaskOrderInvitations: def setup(self): self.portfolio = PortfolioFactory.create() @@ -151,3 +116,59 @@ class TestTaskOrderInvitations: updated_task_order = TaskOrders.get(self.portfolio.owner, self.task_order.id) assert updated_task_order.so_first_name != "Boba" + + +def test_ko_can_view_task_order(client, user_session): + portfolio = PortfolioFactory.create() + ko = UserFactory.create() + PortfolioRoleFactory.create( + role=Roles.get("officer"), + portfolio=portfolio, + user=ko, + status=PortfolioStatus.ACTIVE, + ) + task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko) + user_session(ko) + response = client.get( + url_for( + "portfolios.view_task_order", + portfolio_id=portfolio.id, + task_order_id=task_order.id, + ) + ) + assert response.status_code == 200 + + +def test_can_view_task_order_invitations(client, user_session): + portfolio = PortfolioFactory.create() + user_session(portfolio.owner) + task_order = TaskOrderFactory.create(portfolio=portfolio) + response = client.get( + url_for( + "portfolios.task_order_invitations", + portfolio_id=portfolio.id, + task_order_id=task_order.id, + ) + ) + assert response.status_code == 200 + + +def test_ko_can_view_ko_review_page(client, user_session): + portfolio = PortfolioFactory.create() + ko = UserFactory.create() + PortfolioRoleFactory.create( + role=Roles.get("officer"), + portfolio=portfolio, + user=ko, + status=PortfolioStatus.ACTIVE, + ) + task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko) + user_session(ko) + response = client.get( + url_for( + "portfolios.ko_review", + portfolio_id=portfolio.id, + task_order_id=task_order.id, + ) + ) + assert response.status_code == 200 From 110dfdb4b10ef69a63c3a1312af6091ffd32c399 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 09:04:34 -0500 Subject: [PATCH 23/29] MO and COR redirect to build pages --- atst/routes/portfolios/task_orders.py | 7 +++-- templates/portfolios/task_orders/show.html | 11 +++++++- tests/routes/portfolios/test_task_orders.py | 30 +++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/atst/routes/portfolios/task_orders.py b/atst/routes/portfolios/task_orders.py index f9ef106f..d5278ec6 100644 --- a/atst/routes/portfolios/task_orders.py +++ b/atst/routes/portfolios/task_orders.py @@ -68,6 +68,7 @@ def view_task_order(portfolio_id, task_order_id): portfolio=portfolio, task_order=task_order, all_sections_complete=completed, + user=g.current_user, ) @@ -75,6 +76,7 @@ def view_task_order(portfolio_id, task_order_id): def ko_review(portfolio_id, task_order_id): task_order = TaskOrders.get(g.current_user, task_order_id) portfolio = Portfolios.get(g.current_user, portfolio_id) + Authorization.check_is_ko(g.current_user, task_order) return render_template( "/portfolios/task_orders/review.html", @@ -89,9 +91,10 @@ def ko_review(portfolio_id, task_order_id): ) def submit_ko_review(portfolio_id, task_order_id, form=None): task_order = TaskOrders.get(g.current_user, task_order_id) - Authorization.check_is_ko(g.current_user, task_order) - form = KOReviewForm(http_request.form) + form_data = {**http_request.form, **http_request.files} + form = KOReviewForm(form_data) + Authorization.check_is_ko(g.current_user, task_order) if form.validate(): TaskOrders.update(user=g.current_user, task_order=task_order, **form.data) return redirect( diff --git a/templates/portfolios/task_orders/show.html b/templates/portfolios/task_orders/show.html index a75a1836..54787e00 100644 --- a/templates/portfolios/task_orders/show.html +++ b/templates/portfolios/task_orders/show.html @@ -92,8 +92,17 @@ link_text="edit", complete=all_sections_complete) %}
+ {% if user == task_order.contracting_officer %} + {% set url=url_for("portfolios.ko_review", portfolio_id=portfolio.id, task_order_id=task_order.id) %} + {% elif user == task_order.creator or user == task_order.contracting_officer_representative %} + {% set url = url_for("task_orders.new", screen=1, task_order_id=task_order.id) %} + {% else %} + {% set url = url_for("portfolios.ko_review", portfolio_id=portfolio.id, task_order_id=task_order.id) %} + {% endif %} +

creator: {{task_order.creator.full_name}}

+

cor: {{task_order.contracting_officer_representative.full_name}}

Edit diff --git a/tests/routes/portfolios/test_task_orders.py b/tests/routes/portfolios/test_task_orders.py index 48d4aa21..0501d9a6 100644 --- a/tests/routes/portfolios/test_task_orders.py +++ b/tests/routes/portfolios/test_task_orders.py @@ -172,3 +172,33 @@ def test_ko_can_view_ko_review_page(client, user_session): ) ) assert response.status_code == 200 + + +def test_mo_redirected_to_build_page(client, user_session): + portfolio = PortfolioFactory.create() + user_session(portfolio.owner) + task_order = TaskOrderFactory.create(portfolio=portfolio) + + response = client.get( + url_for("task_orders.new", screen=1, task_order_id=task_order.id) + ) + assert response.status_code == 200 + + +def test_cor_redirected_to_build_page(client, user_session): + portfolio = PortfolioFactory.create() + cor = UserFactory.create() + PortfolioRoleFactory.create( + role=Roles.get("officer"), + portfolio=portfolio, + user=cor, + status=PortfolioStatus.ACTIVE, + ) + task_order = TaskOrderFactory.create( + portfolio=portfolio, contracting_officer_representative=cor + ) + user_session(cor) + response = client.get( + url_for("task_orders.new", screen=1, task_order_id=task_order.id) + ) + assert response.status_code == 200 From 837192e37826e1038ef09e053f0159a8185fdcc4 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 09:40:36 -0500 Subject: [PATCH 24/29] Don't handle non-KOCORMO case --- templates/portfolios/task_orders/show.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/portfolios/task_orders/show.html b/templates/portfolios/task_orders/show.html index 54787e00..20d12974 100644 --- a/templates/portfolios/task_orders/show.html +++ b/templates/portfolios/task_orders/show.html @@ -96,8 +96,6 @@ {% set url=url_for("portfolios.ko_review", portfolio_id=portfolio.id, task_order_id=task_order.id) %} {% elif user == task_order.creator or user == task_order.contracting_officer_representative %} {% set url = url_for("task_orders.new", screen=1, task_order_id=task_order.id) %} - {% else %} - {% set url = url_for("portfolios.ko_review", portfolio_id=portfolio.id, task_order_id=task_order.id) %} {% endif %}

creator: {{task_order.creator.full_name}}

cor: {{task_order.contracting_officer_representative.full_name}}

From d100438dba7bd1b560c310c92bcb6f0d4026881f Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 09:43:12 -0500 Subject: [PATCH 25/29] KO goes to KO review, everyone else goes to TO form --- templates/portfolios/task_orders/show.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/portfolios/task_orders/show.html b/templates/portfolios/task_orders/show.html index 20d12974..04e49824 100644 --- a/templates/portfolios/task_orders/show.html +++ b/templates/portfolios/task_orders/show.html @@ -94,11 +94,9 @@
{% if user == task_order.contracting_officer %} {% set url=url_for("portfolios.ko_review", portfolio_id=portfolio.id, task_order_id=task_order.id) %} - {% elif user == task_order.creator or user == task_order.contracting_officer_representative %} + {% else %} {% set url = url_for("task_orders.new", screen=1, task_order_id=task_order.id) %} {% endif %} -

creator: {{task_order.creator.full_name}}

-

cor: {{task_order.contracting_officer_representative.full_name}}

From 72b36395cefdc83c2831dfcf8dceeb2eb170fbe7 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 10:43:35 -0500 Subject: [PATCH 26/29] Add missing filter --- templates/fragments/task_order_review/reporting.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/fragments/task_order_review/reporting.html b/templates/fragments/task_order_review/reporting.html index b493a00b..718cc89f 100644 --- a/templates/fragments/task_order_review/reporting.html +++ b/templates/fragments/task_order_review/reporting.html @@ -6,7 +6,8 @@ ("forms.task_order.app_migration.label" | translate), ( ("forms.task_order.app_migration.{}".format(task_order.app_migration) | translate) if task_order.app_migration - ) + ), + filter='safe' ) }} From c0da06f2170ac2f7c965abac1179f3b4666465fc Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 10:45:41 -0500 Subject: [PATCH 27/29] Drop back end date validators --- atst/forms/ko_review.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 4baaa032..a75dc75d 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -14,22 +14,10 @@ from atst.utils.localization import translate class KOReviewForm(CacheableForm): start_date = DateField( translate("forms.ko_review.start_date_label"), - validators=[ - DateRange( - lower_bound=pendulum.duration(days=0), - message=translate("forms.ko_review.invalid_date"), - ) - ], format="%m/%d/%Y", ) end_date = DateField( translate("forms.ko_review.end_date_label"), - validators=[ - DateRange( - lower_bound=pendulum.duration(days=0), - message=translate("forms.ko_review.invalid_date"), - ) - ], format="%m/%d/%Y", ) pdf = FileField( From cd7885c38692558b1db2508273497b6a9ef630a0 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 10:57:13 -0500 Subject: [PATCH 28/29] formatting --- atst/forms/ko_review.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index a75dc75d..2655ff5d 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -13,13 +13,9 @@ from atst.utils.localization import translate class KOReviewForm(CacheableForm): start_date = DateField( - translate("forms.ko_review.start_date_label"), - format="%m/%d/%Y", - ) - end_date = DateField( - translate("forms.ko_review.end_date_label"), - format="%m/%d/%Y", + translate("forms.ko_review.start_date_label"), format="%m/%d/%Y" ) + end_date = DateField(translate("forms.ko_review.end_date_label"), format="%m/%d/%Y") pdf = FileField( translate("forms.ko_review.pdf_label"), description=translate("forms.ko_review.pdf_description"), From 5a775cc5a268c2fc99dac9cd497bf1c4b8ca5adb Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 5 Feb 2019 11:12:50 -0500 Subject: [PATCH 29/29] Remove unused imports --- atst/forms/ko_review.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/atst/forms/ko_review.py b/atst/forms/ko_review.py index 2655ff5d..aec28e7f 100644 --- a/atst/forms/ko_review.py +++ b/atst/forms/ko_review.py @@ -1,4 +1,3 @@ -import pendulum from flask_wtf.file import FileAllowed from wtforms.fields.html5 import DateField @@ -6,7 +5,7 @@ from wtforms.fields import StringField, TextAreaField, FileField from wtforms.validators import Optional, Length from .forms import CacheableForm -from .validators import IsNumber, DateRange +from .validators import IsNumber from atst.utils.localization import translate