Merge pull request #685 from dod-ccpo/use-pf-name-and-dc-for-to

Portfolio Name and Defense Component read only
This commit is contained in:
montana-mil 2019-03-11 14:10:55 -04:00 committed by GitHub
commit a2754d0646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 149 additions and 41 deletions

View File

@ -27,26 +27,11 @@ from .data import (
from atst.utils.localization import translate from atst.utils.localization import translate
class AppInfoForm(BaseForm): class AppInfoWithExistingPortfolioForm(BaseForm):
portfolio_name = StringField(
translate("forms.task_order.portfolio_name_label"),
description=translate("forms.task_order.portfolio_name_description"),
validators=[
Required(),
Length(
min=4,
max=100,
message=translate("forms.portfolio.name_length_validation_message"),
),
],
)
scope = TextAreaField( scope = TextAreaField(
translate("forms.task_order.scope_label"), translate("forms.task_order.scope_label"),
description=translate("forms.task_order.scope_description"), description=translate("forms.task_order.scope_description"),
) )
defense_component = SelectField(
translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES
)
app_migration = RadioField( app_migration = RadioField(
translate("forms.task_order.app_migration.label"), translate("forms.task_order.app_migration.label"),
description=translate("forms.task_order.app_migration.description"), description=translate("forms.task_order.app_migration.description"),
@ -88,6 +73,24 @@ class AppInfoForm(BaseForm):
) )
class AppInfoForm(AppInfoWithExistingPortfolioForm):
portfolio_name = StringField(
translate("forms.task_order.portfolio_name_label"),
description=translate("forms.task_order.portfolio_name_description"),
validators=[
Required(),
Length(
min=4,
max=100,
message=translate("forms.portfolio.name_length_validation_message"),
),
],
)
defense_component = SelectField(
translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES
)
class FundingForm(BaseForm): class FundingForm(BaseForm):
performance_length = SelectField( performance_length = SelectField(
translate("forms.task_order.performance_length.label"), translate("forms.task_order.performance_length.label"),

View File

@ -36,6 +36,10 @@ class Portfolio(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
def user_count(self): def user_count(self):
return len(self.members) return len(self.members)
@property
def num_task_orders(self):
return len(self.task_orders)
@property @property
def members(self): def members(self):
return ( return (

View File

@ -46,12 +46,14 @@ TASK_ORDER_SECTIONS = [
class ShowTaskOrderWorkflow: class ShowTaskOrderWorkflow:
def __init__(self, user, screen=1, task_order_id=None): def __init__(self, user, screen=1, task_order_id=None, portfolio_id=None):
self.user = user self.user = user
self.screen = screen self.screen = screen
self.task_order_id = task_order_id self.task_order_id = task_order_id
self._section = TASK_ORDER_SECTIONS[screen - 1]
self._task_order = None self._task_order = None
self.portfolio_id = portfolio_id
self._portfolio = None
self._section = TASK_ORDER_SECTIONS[screen - 1]
self._form = None self._form = None
@property @property
@ -61,6 +63,16 @@ class ShowTaskOrderWorkflow:
return self._task_order return self._task_order
@property
def portfolio(self):
if not self._portfolio:
if self.task_order:
self._portfolio = self.task_order.portfolio
elif self.portfolio_id:
self._portfolio = Portfolios.get(self.user, self.portfolio_id)
return self._portfolio
@property @property
def form(self): def form(self):
form_type = ( form_type = (
@ -90,6 +102,11 @@ class ShowTaskOrderWorkflow:
else: else:
self._form = self._section[form_type]() self._form = self._section[form_type]()
if self.pf_attributes_read_only and self.screen == 1:
self._form = task_order_form.AppInfoWithExistingPortfolioForm(
obj=self.task_order
)
return self._form return self._form
@property @property
@ -110,9 +127,17 @@ class ShowTaskOrderWorkflow:
@property @property
def is_complete(self): def is_complete(self):
if self.task_order: if self.task_order and TaskOrders.all_sections_complete(self.task_order):
if TaskOrders.all_sections_complete(self.task_order): return True
return True else:
return False
@property
def pf_attributes_read_only(self):
if self.task_order and self.portfolio.num_task_orders > 1:
return True
elif self.portfolio_id:
return True
else: else:
return False return False
@ -128,15 +153,27 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
self.portfolio_id = portfolio_id self.portfolio_id = portfolio_id
self._task_order = None self._task_order = None
self._section = TASK_ORDER_SECTIONS[screen - 1] self._section = TASK_ORDER_SECTIONS[screen - 1]
form_type = ( self._form = None
"unclassified_form"
if "unclassified_form" in self._section and not app.config.get("CLASSIFIED")
else "form"
)
self._form = self._section[form_type](self.form_data, obj=self.task_order)
@property @property
def form(self): def form(self):
if not self._form:
form_type = (
"unclassified_form"
if "unclassified_form" in self._section
and not app.config.get("CLASSIFIED")
else "form"
)
if self.pf_attributes_read_only and self.screen == 1:
self._form = task_order_form.AppInfoWithExistingPortfolioForm(
self.form_data
)
else:
self._form = self._section[form_type](
self.form_data, obj=self.task_order
)
return self._form return self._form
@property @property
@ -206,21 +243,24 @@ def get_started():
@task_orders_bp.route("/task_orders/new/<int:screen>/<task_order_id>") @task_orders_bp.route("/task_orders/new/<int:screen>/<task_order_id>")
@task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/new/<int:screen>") @task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/new/<int:screen>")
def new(screen, task_order_id=None, portfolio_id=None): def new(screen, task_order_id=None, portfolio_id=None):
if task_order_id and screen is 4: workflow = ShowTaskOrderWorkflow(
task_order = TaskOrders.get(g.current_user, task_order_id) g.current_user, screen, task_order_id, portfolio_id
if not TaskOrders.all_sections_complete(task_order): )
flash("task_order_draft")
workflow = ShowTaskOrderWorkflow(g.current_user, screen, task_order_id)
template_args = { template_args = {
"current": screen, "current": screen,
"task_order_id": task_order_id, "task_order_id": task_order_id,
"portfolio_id": portfolio_id,
"screens": workflow.display_screens, "screens": workflow.display_screens,
"form": workflow.form, "form": workflow.form,
"complete": workflow.is_complete, "complete": workflow.is_complete,
} }
if task_order_id and screen is 4:
if not TaskOrders.all_sections_complete(workflow.task_order):
flash("task_order_draft")
if workflow.pf_attributes_read_only:
template_args["portfolio"] = workflow.portfolio
url_args = {"screen": screen} url_args = {"screen": screen}
if task_order_id: if task_order_id:
url_args["task_order_id"] = task_order_id url_args["task_order_id"] = task_order_id

View File

@ -4,6 +4,7 @@
{% from "components/options_input.html" import OptionsInput %} {% from "components/options_input.html" import OptionsInput %}
{% from "components/date_input.html" import DateInput %} {% from "components/date_input.html" import DateInput %}
{% from "components/multi_checkbox_input.html" import MultiCheckboxInput %} {% from "components/multi_checkbox_input.html" import MultiCheckboxInput %}
{% from "components/review_field.html" import ReviewField %}
{% block heading %} {% block heading %}
{{ "task_orders.new.app_info.section_title"| translate }} {{ "task_orders.new.app_info.section_title"| translate }}
@ -14,11 +15,21 @@
<!-- App Info Section --> <!-- App Info Section -->
<h3 class="task-order-form__heading subheading">{{ "task_orders.new.app_info.basic_info_title"| translate }}</h3> <h3 class="task-order-form__heading subheading">{{ "task_orders.new.app_info.basic_info_title"| translate }}</h3>
{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization", validation="portfolioName") }}
{% if portfolio %}
{{ ReviewField(heading="forms.portfolio.name_label" | translate, field=portfolio.name) }}
{% else %}
{{ TextInput(form.portfolio_name, placeholder="The name of your office or organization", validation="portfolioName") }}
{% endif %}
{{ TextInput(form.scope, paragraph=True) }} {{ TextInput(form.scope, paragraph=True) }}
<p><i>{{ "task_orders.new.app_info.sample_scope" | translate | safe }}</i></p> <p><i>{{ "task_orders.new.app_info.sample_scope" | translate | safe }}</i></p>
<div class="subheading--black"> <div class="subheading--black">
{{ OptionsInput(form.defense_component) }} {% if portfolio %}
{{ ReviewField(heading="forms.task_order.defense_component_label" | translate, field=portfolio.defense_component) }}
{% else %}
{{ OptionsInput(form.defense_component) }}
{% endif %}
</div> </div>
<hr> <hr>

View File

@ -9,6 +9,26 @@ from atst.utils.localization import translate
from tests.factories import UserFactory, TaskOrderFactory, PortfolioFactory from tests.factories import UserFactory, TaskOrderFactory, PortfolioFactory
class TestShowTaskOrderWorkflow:
def test_portfolio_when_task_order_exists(self):
portfolio = PortfolioFactory.create()
task_order = TaskOrderFactory(portfolio=portfolio)
assert portfolio.num_task_orders > 0
workflow = ShowTaskOrderWorkflow(
user=task_order.creator, task_order_id=task_order.id
)
assert portfolio == workflow.portfolio
def test_portfolio_with_portfolio_id(self):
user = UserFactory.create()
portfolio = PortfolioFactory.create(owner=user)
workflow = ShowTaskOrderWorkflow(
user=portfolio.owner, portfolio_id=portfolio.id
)
assert portfolio == workflow.portfolio
def test_new_task_order(client, user_session): def test_new_task_order(client, user_session):
creator = UserFactory.create() creator = UserFactory.create()
user_session() user_session()
@ -42,6 +62,37 @@ def serialize_dates(data):
return data return data
def test_new_to_can_edit_pf_attributes_screen_1():
portfolio = PortfolioFactory.create()
workflow = ShowTaskOrderWorkflow(user=portfolio.owner)
assert not workflow.pf_attributes_read_only
def test_new_pf_can_edit_pf_attributes_on_back_navigation():
portfolio = PortfolioFactory.create()
pf_task_order = TaskOrderFactory(portfolio=portfolio)
pf_workflow = ShowTaskOrderWorkflow(
user=pf_task_order.creator, task_order_id=pf_task_order.id
)
assert not pf_workflow.pf_attributes_read_only
def test_to_on_pf_cannot_edit_pf_attributes():
portfolio = PortfolioFactory.create()
pf_task_order = TaskOrderFactory(portfolio=portfolio)
workflow = ShowTaskOrderWorkflow(user=portfolio.owner, portfolio_id=portfolio.id)
assert portfolio.num_task_orders == 1
assert workflow.pf_attributes_read_only
second_task_order = TaskOrderFactory(portfolio=portfolio)
second_workflow = ShowTaskOrderWorkflow(
user=portfolio.owner, task_order_id=second_task_order.id
)
assert portfolio.num_task_orders > 1
assert second_workflow.pf_attributes_read_only
# TODO: this test will need to be more complicated when we add validation to # TODO: this test will need to be more complicated when we add validation to
# the forms # the forms
def test_create_new_task_order(client, user_session, pdf_upload): def test_create_new_task_order(client, user_session, pdf_upload):
@ -66,7 +117,6 @@ def test_create_new_task_order(client, user_session, pdf_upload):
created_task_order = TaskOrders.get(creator, created_task_order_id) created_task_order = TaskOrders.get(creator, created_task_order_id)
assert created_task_order.portfolio is not None assert created_task_order.portfolio is not None
assert created_task_order.portfolio.name == portfolio_name assert created_task_order.portfolio.name == portfolio_name
assert created_task_order.portfolio is not None
assert created_task_order.portfolio.defense_component == defense_component assert created_task_order.portfolio.defense_component == defense_component
funding_data = slice_data_for_section(task_order_data, "funding") funding_data = slice_data_for_section(task_order_data, "funding")
@ -91,10 +141,8 @@ def test_create_new_task_order_for_portfolio(client, user_session):
task_order_data = TaskOrderFactory.dictionary() task_order_data = TaskOrderFactory.dictionary()
app_info_data = slice_data_for_section(task_order_data, "app_info") app_info_data = slice_data_for_section(task_order_data, "app_info")
portfolio_name = "This is ignored for now" app_info_data["portfolio_name"] = portfolio.name
app_info_data["portfolio_name"] = portfolio_name app_info_data["defense_component"] = portfolio.defense_component
defense_component = "Defense Health Agency" # this is also ignored
app_info_data["defense_component"] = defense_component
response = client.post( response = client.post(
url_for("task_orders.update", screen=1, portfolio_id=portfolio.id), url_for("task_orders.update", screen=1, portfolio_id=portfolio.id),
@ -105,6 +153,8 @@ def test_create_new_task_order_for_portfolio(client, user_session):
created_task_order_id = response.headers["Location"].split("/")[-1] created_task_order_id = response.headers["Location"].split("/")[-1]
created_task_order = TaskOrders.get(creator, created_task_order_id) created_task_order = TaskOrders.get(creator, created_task_order_id)
assert created_task_order.portfolio_name == portfolio.name
assert created_task_order.defense_component == portfolio.defense_component
assert created_task_order.portfolio == portfolio assert created_task_order.portfolio == portfolio