Merge pull request #323 from dod-ccpo/internal-comments

Hook up internal request comments
This commit is contained in:
patricksmithdds 2018-09-24 17:53:06 -04:00 committed by GitHub
commit 6ac19d315d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 94 additions and 58 deletions

View File

@ -225,8 +225,8 @@ class Requests(object):
return Requests._add_review(user, request, review_data)
@classmethod
def update_internal_comments(cls, user, request, comment_text):
def add_internal_comment(cls, user, request, comment_text):
Authorization.check_can_approve_request(user)
request.internal_comments = RequestInternalComment(text=comment_text, user=user)
request = RequestsQuery.add_and_commit(request)
comment = RequestInternalComment(request=request, text=comment_text, user=user)
RequestsQuery.add_and_commit(comment)
return request

View File

@ -1,5 +1,5 @@
from wtforms.fields import TextAreaField
from wtforms.validators import Optional
from wtforms.validators import InputRequired
from .forms import ValidatedForm
@ -7,6 +7,7 @@ from .forms import ValidatedForm
class InternalCommentForm(ValidatedForm):
text = TextAreaField(
"CCPO Internal Notes",
description="Add comments or notes for internal CCPO reference and follow-up here.<strong>These comments <em>will not</em> be visible to the person making the JEDI request.</strong>",
validators=[Optional()],
default="",
description="Add comments or notes for internal CCPO reference and follow-up here. <strong>These comments <em>will not</em> be visible to the person making the JEDI request.</strong>",
validators=[InputRequired()],
)

View File

@ -45,7 +45,7 @@ class Request(Base, mixins.TimestampsMixin):
"RequestRevision", back_populates="request", order_by="RequestRevision.sequence"
)
internal_comments = relationship("RequestInternalComment", uselist=False)
internal_comments = relationship("RequestInternalComment")
@property
def latest_revision(self):
@ -167,10 +167,6 @@ class Request(Base, mixins.TimestampsMixin):
def reviews(self):
return [status.review for status in self.status_events if status.review]
@property
def internal_comments_text(self):
return self.internal_comments.text if self.internal_comments else ""
@property
def is_pending_financial_verification(self):
return self.status == RequestStatus.PENDING_FINANCIAL_VERIFICATION

View File

@ -14,3 +14,4 @@ class RequestInternalComment(Base, mixins.TimestampsMixin):
user = relationship("User")
request_id = Column(ForeignKey("requests.id", ondelete="CASCADE"), nullable=False)
request = relationship("Request")

View File

@ -14,14 +14,12 @@ from atst.domain.exceptions import NotFoundError
from atst.forms.ccpo_review import CCPOReviewForm
from atst.forms.internal_comment import InternalCommentForm
from datetime import date
def map_ccpo_authorizing(user):
return {"fname_ccpo": user.first_name, "lname_ccpo": user.last_name}
def render_approval(request, form=None):
def render_approval(request, form=None, internal_comment_form=None):
data = request.body
if request.has_financial_data:
data["task_order"] = request.task_order.to_dictionary()
@ -30,21 +28,8 @@ def render_approval(request, form=None):
mo_data = map_ccpo_authorizing(g.current_user)
form = CCPOReviewForm(data=mo_data)
internal_comment_form = InternalCommentForm(text=request.internal_comments_text)
# Dummy internal comments
comments = [
{
"time_created": date(2018, 9, 18),
"full_name_commenter": "Darth Vader",
"message": "We'll have no more of this Obi-Wan Kenobi jibberish...and don't talk to me about your mission, either. You're fortunate he doesn't blast you into a million pieces right here. ",
},
{
"time_created": date(2018, 9, 20),
"full_name_commenter": "Grand Moff Tarkin",
"message": "We'll have no more of this Obi-Wan Kenobi jibberish...and don't talk to me about your mission, either. You're fortunate he doesn't blast you into a million pieces right here. ",
},
]
if not internal_comment_form:
internal_comment_form = InternalCommentForm()
return render_template(
"requests/approval.html",
@ -52,9 +37,9 @@ def render_approval(request, form=None):
reviews=list(reversed(request.reviews)),
request=request,
current_status=request.status.value,
f=form or CCPOReviewForm(),
review_form=form or CCPOReviewForm(),
internal_comment_form=internal_comment_form,
comments=comments,
comments=request.internal_comments,
)
@ -101,10 +86,12 @@ def task_order_pdf_download(request_id):
@requests_bp.route("/requests/internal_comments/<string:request_id>", methods=["POST"])
def create_internal_comment(request_id):
# form = InternalCommentForm(http_request.form)
# if form.validate():
# request = Requests.get(g.current_user, request_id)
# Requests.update_internal_comments(g.current_user, request, form.data["text"])
return redirect(
url_for("requests.approval", request_id=request_id, _anchor="ccpo-notes")
)
form = InternalCommentForm(http_request.form)
request = Requests.get(g.current_user, request_id)
if form.validate():
Requests.add_internal_comment(g.current_user, request, form.data.get("text"))
return redirect(
url_for("requests.approval", request_id=request_id, _anchor="ccpo-notes")
)
else:
return render_approval(request, internal_comment_form=form)

View File

@ -8,7 +8,7 @@
<article class='col col--grow request-approval'>
{% if f.errors %}
{% if review_form.errors or internal_comment_form.errors %}
{{ Alert('There were some errors',
message="<p>Please see below.</p>",
level='error'
@ -32,11 +32,6 @@
</section>
{{ Alert('Comments and comment form are fake!',
message="<p>Please note, the comments and comment form below are just mocked out. Submitting it will do nothing. These will be hooked up to real functionality shortly. </p><p><strong>Engineer: please remove this alert when you do your thing.</strong></p>",
level='warning'
) }}
<section class='internal-notes' id='ccpo-notes'>
<form method="POST" action="{{ url_for('requests.create_internal_comment', request_id=request.id) }}">
<div class='panel'>
@ -51,8 +46,8 @@
<li>
<article class='comment-log__log-item'>
<div>
<h3 class='comment-log__log-item__header'>{{ comment.full_name_commenter }}</h3>
<p>{{ comment.message }}</p>
<h3 class='comment-log__log-item__header'>{{ comment.user.full_name }}</h3>
<p>{{ comment.text }}</p>
</div>
{% set timestamp=comment.time_created | formattedDate("%Y-%m-%d %H:%M:%S %Z") %}
<footer class='comment-log__log-item__timestamp'>
@ -88,7 +83,7 @@
<section class='request-approval__review'>
<form method="POST" action="{{ url_for("requests.submit_approval", request_id=request.id) }}" autocomplete="off">
{{ f.csrf_token }}
{{ review_form.csrf_token }}
<ccpo-approval inline-template>
<div>
@ -165,7 +160,7 @@
<h3>Message to Requestor <span class='subtitle'>(optional)</span></h3>
<div v-if='approving' key='approving' v-cloak>
{{ TextInput(
f.comment,
review_form.comment,
label='Approval comments or notes',
description='Provide any comments or notes regarding the approval of this request. <strong>This message will be shared with the person making the JEDI request.</strong>.',
paragraph=True,
@ -175,7 +170,7 @@
<div v-else key='denying' v-cloak>
{{ TextInput(
f.comment,
review_form.comment,
label='Revision instructions or notes',
paragraph=True,
noMaxWidth=True
@ -194,21 +189,21 @@
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(f.fname_mao, placeholder="First name of mission authorizing official") }}
{{ TextInput(review_form.fname_mao, placeholder="First name of mission authorizing official") }}
</div>
<div class='form-col form-col--half'>
{{ TextInput(f.lname_mao, placeholder="Last name of mission authorizing official") }}
{{ TextInput(review_form.lname_mao, placeholder="Last name of mission authorizing official") }}
</div>
</div>
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(f.email_mao, placeholder="name@mail.mil", validation='email') }}
{{ TextInput(review_form.email_mao, placeholder="name@mail.mil", validation='email') }}
</div>
<div class='form-col form-col--half'>
{{ TextInput(f.phone_mao, placeholder="(123) 456-7890", validation='usPhone') }}
{{ TextInput(review_form.phone_mao, placeholder="(123) 456-7890", validation='usPhone') }}
</div>
</div>
@ -218,11 +213,11 @@
<div class='form-row'>
<div class='form-col form-col--half'>
{{ TextInput(f.fname_ccpo, placeholder="First name of CCPO authorizing official") }}
{{ TextInput(review_form.fname_ccpo, placeholder="First name of CCPO authorizing official") }}
</div>
<div class='form-col form-col--half'>
{{ TextInput(f.lname_ccpo, placeholder="Last name of CCPO authorizing official") }}
{{ TextInput(review_form.lname_ccpo, placeholder="Last name of CCPO authorizing official") }}
</div>
</div>
</div>

View File

@ -223,10 +223,13 @@ def test_request_changes_to_financial_verification_info():
assert current_review.fname_mao == review_data["fname_mao"]
def test_update_internal_comments():
def test_add_internal_comment():
request = RequestFactory.create()
ccpo = UserFactory.from_atat_role("ccpo")
request = Requests.update_internal_comments(ccpo, request, "this is my comment")
assert len(request.internal_comments) == 0
assert request.internal_comments.text == "this is my comment"
request = Requests.add_internal_comment(ccpo, request, "this is my comment")
assert len(request.internal_comments) == 1
assert request.internal_comments[0].text == "this is my comment"

View File

@ -108,3 +108,56 @@ def test_can_submit_request_denial(client, user_session):
)
assert response.status_code == 302
assert request.status == RequestStatus.CHANGES_REQUESTED
def test_ccpo_user_can_comment_on_request(client, user_session):
user = UserFactory.from_atat_role("ccpo")
user_session(user)
request = RequestFactory.create_with_status(
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
)
assert len(request.internal_comments) == 0
comment_text = "This is the greatest request in the history of requests"
comment_form_data = {"text": comment_text}
response = client.post(
url_for("requests.create_internal_comment", request_id=request.id),
data=comment_form_data,
)
assert response.status_code == 302
assert len(request.internal_comments) == 1
assert request.internal_comments[0].text == comment_text
def test_comment_text_is_required(client, user_session):
user = UserFactory.from_atat_role("ccpo")
user_session(user)
request = RequestFactory.create_with_status(
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
)
assert len(request.internal_comments) == 0
comment_form_data = {"text": ""}
response = client.post(
url_for("requests.create_internal_comment", request_id=request.id),
data=comment_form_data,
)
assert response.status_code == 200
assert len(request.internal_comments) == 0
def test_other_user_cannot_comment_on_request(client, user_session):
user = UserFactory.create()
user_session(user)
request = RequestFactory.create_with_status(
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
)
comment_text = "What is this even"
comment_form_data = {"text": comment_text}
response = client.post(
url_for("requests.create_internal_comment", request_id=request.id),
data=comment_form_data,
)
assert response.status_code == 404