Merge pull request #63 from dod-ccpo/submit-request

Allow user to submit request
This commit is contained in:
richard-dds 2018-07-11 13:16:10 -04:00 committed by GitHub
commit b685b478df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 213 additions and 112 deletions

View File

@ -10,6 +10,7 @@ from atst.handlers.login import Login
from atst.handlers.workspace import Workspace from atst.handlers.workspace import Workspace
from atst.handlers.request import Request from atst.handlers.request import Request
from atst.handlers.request_new import RequestNew from atst.handlers.request_new import RequestNew
from atst.handlers.request_submit import RequestsSubmit
from atst.handlers.dev import Dev from atst.handlers.dev import Dev
from atst.home import home from atst.home import home
from atst.api_client import ApiClient from atst.api_client import ApiClient
@ -71,6 +72,12 @@ def make_app(config, deps, **kwargs):
{"page": "requests_new", "requests_client": deps["requests_client"]}, {"page": "requests_new", "requests_client": deps["requests_client"]},
name="request_form_update", name="request_form_update",
), ),
url(
r"/requests/submit/(\S+)",
RequestsSubmit,
{"requests_client": deps["requests_client"]},
name="requests_submit",
),
url(r"/users", MainHandler, {"page": "users"}, name="users"), url(r"/users", MainHandler, {"page": "users"}, name="users"),
url(r"/reports", MainHandler, {"page": "reports"}, name="reports"), url(r"/reports", MainHandler, {"page": "reports"}, name="reports"),
url(r"/calculator", MainHandler, {"page": "calculator"}, name="calculator"), url(r"/calculator", MainHandler, {"page": "calculator"}, name="calculator"),

View File

@ -2,7 +2,7 @@ from wtforms.fields.html5 import IntegerField
from wtforms.fields import RadioField, StringField, TextAreaField from wtforms.fields import RadioField, StringField, TextAreaField
from wtforms.validators import NumberRange, InputRequired from wtforms.validators import NumberRange, InputRequired
from wtforms_tornado import Form from wtforms_tornado import Form
from .fields import DateField, NewlineListField from .fields import DateField
from .validators import DateRange from .validators import DateRange
import pendulum import pendulum

View File

@ -1,43 +1,14 @@
import tornado import tornado
from atst.handler import BaseHandler from atst.handler import BaseHandler
from atst.forms.request import RequestForm from atst.forms.request import RequestForm
from atst.forms.org import OrgForm from atst.forms.org import OrgForm
from atst.forms.poc import POCForm from atst.forms.poc import POCForm
from atst.forms.review import ReviewForm from atst.forms.review import ReviewForm
from atst.forms.financial import FinancialForm from atst.forms.financial import FinancialForm
import tornado.httputil
class RequestNew(BaseHandler): class RequestNew(BaseHandler):
screens = [
{
"title": "Details of Use",
"section": "details_of_use",
"form": RequestForm,
"subitems": [
{"title": "Overall request details", "id": "overall-request-details"},
{"title": "Cloud Resources", "id": "cloud-resources"},
{"title": "Support Staff", "id": "support-staff"},
],
},
{
"title": "Information About You",
"section": "information_about_you",
"form": OrgForm,
},
{
"title": "Primary Point of Contact",
"section": "primary_poc",
"form": POCForm,
},
{"title": "Review & Submit", "section": "review_submit", "form": ReviewForm},
{
"title": "Financial Verification",
"section": "financial_verification",
"form": FinancialForm,
},
]
def initialize(self, page, requests_client): def initialize(self, page, requests_client):
self.page = page self.page = page
self.requests_client = requests_client self.requests_client = requests_client
@ -47,58 +18,62 @@ class RequestNew(BaseHandler):
def post(self, screen=1, request_id=None): def post(self, screen=1, request_id=None):
self.check_xsrf_cookie() self.check_xsrf_cookie()
screen = int(screen) screen = int(screen)
form_metadata = self.screens[screen - 1] post_data = self.request.arguments
form_section = form_metadata["section"] jedi_flow = JEDIRequestFlow(
form = form_metadata["form"](self.request.arguments) self.requests_client, screen, post_data=post_data, request_id=request_id
)
if form.validate(): if jedi_flow.validate():
response = yield self.create_or_update_request( response = yield jedi_flow.create_or_update_request(self.get_current_user())
form_section, form.data, request_id
)
if response.ok: if response.ok:
where = self.application.default_router.reverse_url( where = self.application.default_router.reverse_url(
"request_form_update", "request_form_update", str(screen + 1), jedi_flow.request_id
str(screen + 1),
request_id or response.json["id"],
) )
self.redirect(where) self.redirect(where)
else: else:
self.set_status(response.code) self.set_status(response.code)
else: else:
self.show_form(screen, form) self.render(
"requests/screen-%d.html.to" % int(screen),
f=jedi_flow.form,
data=post_data,
page=self.page,
screens=jedi_flow.screens,
current=screen,
next_screen=jedi_flow.next_screen,
request_id=jedi_flow.request_id,
)
@tornado.web.authenticated @tornado.web.authenticated
@tornado.gen.coroutine @tornado.gen.coroutine
def get(self, screen=1, request_id=None): def get(self, screen=1, request_id=None):
form = None screen = int(screen)
form_data = None request = None
is_review_section = screen == 4
if request_id: if request_id:
request = yield self.get_request(request_id) response = yield self.requests_client.get(
if request.ok: "/users/{}/requests/{}".format(
if is_review_section: self.get_current_user()["id"], request_id
form_data = request.json["body"] ),
else: raise_error=False,
form_metadata = self.screens[int(screen) - 1] )
section = form_metadata["section"] if response.ok:
form_data = request.json["body"].get(section, request.json["body"]) request = response.json
form = form_metadata["form"](data=form_data)
self.show_form(screen=screen, form=form, request_id=request_id, data=form_data) jedi_flow = JEDIRequestFlow(
self.requests_client, screen, request, request_id=request_id
)
def show_form(self, screen=1, form=None, request_id=None, data=None):
if not form:
form = self.screens[int(screen) - 1]["form"](self.request.arguments)
self.render( self.render(
"requests/screen-%d.html.to" % int(screen), "requests/screen-%d.html.to" % int(screen),
f=form, f=jedi_flow.form,
data=data, data=jedi_flow.current_step_data,
page=self.page, page=self.page,
screens=self.screens, screens=jedi_flow.screens,
current=int(screen), current=screen,
next_screen=int(screen) + 1, next_screen=screen + 1,
request_id=request_id, request_id=request_id,
can_submit=jedi_flow.can_submit
) )
@tornado.gen.coroutine @tornado.gen.coroutine
@ -109,16 +84,126 @@ class RequestNew(BaseHandler):
) )
return request return request
class JEDIRequestFlow(object):
def __init__(
self,
requests_client,
current_step,
request=None,
post_data=None,
request_id=None,
):
self.requests_client = requests_client
self.current_step = current_step
self.request = request
self.post_data = post_data
self.is_post = self.post_data is not None
self.request_id = request_id
self.form = self._form()
def _form(self):
if self.is_post:
return self.form_class()(self.post_data)
elif self.request:
return self.form_class()(data=self.current_step_data)
else:
return self.form_class()()
def validate(self):
return self.form.validate()
@property
def current_screen(self):
return self.screens[self.current_step - 1]
@property
def form_section(self):
return self.current_screen["section"]
def form_class(self):
return self.current_screen["form"]
@property
def current_step_data(self):
if self.is_post:
return self.post_data
if self.request:
if self.form_section == "review_submit":
return self.request["body"]
else:
return self.request["body"].get(self.form_section, {})
else:
return {}
@property
def can_submit(self):
return self.request and self.request["status"] != "incomplete"
@property
def next_screen(self):
return self.current_step + 1
@property
def screens(self):
return [
{
"title": "Details of Use",
"section": "details_of_use",
"form": RequestForm,
"subitems": [
{
"title": "Overall request details",
"id": "overall-request-details",
},
{"title": "Cloud Resources", "id": "cloud-resources"},
{"title": "Support Staff", "id": "support-staff"},
],
"show": True,
},
{
"title": "Information About You",
"section": "information_about_you",
"form": OrgForm,
"show": True,
},
{
"title": "Primary Point of Contact",
"section": "primary_poc",
"form": POCForm,
"show": True,
},
{
"title": "Review & Submit",
"section": "review_submit",
"form": ReviewForm,
"show":True,
},
{
"title": "Financial Verification",
"section": "financial_verification",
"form": FinancialForm,
"show": self.request and self.request["status"] == "approved",
},
]
@tornado.gen.coroutine @tornado.gen.coroutine
def create_or_update_request(self, form_section, form_data, request_id=None): def create_or_update_request(self, user):
request_data = { request_data = {
"creator_id": self.get_current_user()["id"], "creator_id": user["id"],
"request": {form_section: form_data}, "request": {self.form_section: self.form.data},
} }
if request_id: if self.request_id:
response = yield self.requests_client.patch( response = yield self.requests_client.patch(
"/requests/{}".format(request_id), json=request_data "/requests/{}".format(self.request_id), json=request_data
) )
else: else:
response = yield self.requests_client.post("/requests", json=request_data) response = yield self.requests_client.post("/requests", json=request_data)
self.request = response.json
self.request_id = self.request["id"]
return response return response

View File

@ -0,0 +1,17 @@
import tornado
from atst.handler import BaseHandler
class RequestsSubmit(BaseHandler):
def initialize(self, requests_client):
self.requests_client = requests_client
@tornado.web.authenticated
@tornado.gen.coroutine
def post(self, request_id):
yield self.requests_client.post(
"/requests/{}/submit".format(request_id),
allow_nonstandard_methods=True
)
self.redirect("/requests")

View File

@ -1,5 +1,9 @@
{% extends '../requests_new.html.to' %} {% extends '../requests_new.html.to' %}
{% block form_action %}
<form method='POST' action="{{ reverse_url('requests_submit', request_id) }}" autocomplete="off">
{% end %}
{% block form %} {% block form %}
{% autoescape None %} {% autoescape None %}
@ -8,9 +12,8 @@
{% end %} {% end %}
<h2 id="review-submit">Review &amp; Submit</h2> <h2 id="review-submit">Review &amp; Submit</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem ullam veritatis fuga non repellendus repellat dolorum labore nulla iure aspernatur ipsam recusandae saepe harum iste, dolorem adipisci dolores eum, doloribus?</p>
<h3>Details of Use <a href="">Edit</a></h3> <h3>Details of Use <a href="{{ reverse_url('request_form_update', 1, request_id) }}">Edit</a></h3>
<h4>Overall Request Details</h4> <h4>Overall Request Details</h4>
@ -29,12 +32,6 @@
<label>What organizations are supported by these applications?</label> <label>What organizations are supported by these applications?</label>
<b>{{ data.get('details_of_use', {}).get('supported_organizations') }}</b> <b>{{ data.get('details_of_use', {}).get('supported_organizations') }}</b>
<label>Please enter the Unique Item Identifier (UII)s related to your application(s) if you already have them.</label>
<b>{{ data.get('details_of_use', {}).get('uii_ids') }}</b>
<label>Please provide the Program Element (PE) Numbers related to your request</label>
<b>{{ data.get('details_of_use', {}).get('pe_id') }}</b>
<h4>Cloud Resources</h4> <h4>Cloud Resources</h4>
<label>Total Number of vCPU cores</label> <label>Total Number of vCPU cores</label>
@ -68,7 +65,7 @@
<br><br><hr> <br><br><hr>
<h3>Information About You <a href="">Edit</a></h3> <h3>Information About You <a href="{{ reverse_url('request_form_update', 2, request_id) }}">Edit</a></h3>
<label>First Name</label> <label>First Name</label>
<b>{{ data.get('information_about_you', {}).get('fname_request') }}</b> <b>{{ data.get('information_about_you', {}).get('fname_request') }}</b>
@ -97,7 +94,7 @@
<br><br><hr> <br><br><hr>
<h3>Primary Government/Military Point of Contact (POC) <a href="">Edit</a></h3> <h3>Primary Government/Military Point of Contact (POC) <a href="{{ reverse_url('request_form_update', 3, request_id) }}">Edit</a></h3>
@ -119,5 +116,5 @@
{% end %} {% end %}
{% block next %} {% block next %}
<input type='submit' class='usa-button usa-button-primary' value='Submit' /> <input type='submit' class='usa-button usa-button-primary' value='Submit' {{ "disabled" if not can_submit else "" }} />
{% end %} {% end %}

View File

@ -7,25 +7,27 @@
<ul> <ul>
{% for i,s in enumerate(screens) %} {% for i,s in enumerate(screens) %}
<li> {% if s["show"] %}
{% if i+1==current %} <li>
<a class="sidenav__link sidenav__link--active" href="{{ reverse_url('request_form_update', i+1, request_id) if request_id else reverse_url('request_form_new',i+1) }}"> {% if i+1==current %}
{{ i+1 }}. {{ s['title'] }} <a class="sidenav__link sidenav__link--active" href="{{ reverse_url('request_form_update', i+1, request_id) if request_id else reverse_url('request_form_new',i+1) }}">
</a> {{ i+1 }}. {{ s['title'] }}
{% if s.get('subitems') %} </a>
<ul> {% if s.get('subitems') %}
{% for j,t in enumerate(s['subitems']) %} <ul>
<li><a class="sidenav__link" href="#{{ t['id'] }}">{{ t['title'] }}</a></li> {% for j,t in enumerate(s['subitems']) %}
{% end %} <li><a class="sidenav__link" href="#{{ t['id'] }}">{{ t['title'] }}</a></li>
</ul> {% end %}
{% end %} </ul>
{% end %}
{% else %} {% else %}
<a class="sidenav__link" href="{{ reverse_url('request_form_update', i+1, request_id) if request_id else reverse_url('request_form_new',i+1) }}"> <a class="sidenav__link" href="{{ reverse_url('request_form_update', i+1, request_id) if request_id else reverse_url('request_form_new',i+1) }}">
{{ i+1 }}. {{ s['title'] }} {{ i+1 }}. {{ s['title'] }}
</a> </a>
{% end %}
</li>
{% end %} {% end %}
</li>
{% end %} {% end %}
</ul> </ul>
</div> </div>

View File

@ -9,20 +9,20 @@
{% block content %} {% block content %}
<div class="col col--grow"> <div class="col col--grow">
<div class="panel"> <div class="panel">
<main class="panel__content"> <main class="panel__content">
<div class="panel__heading"> <div class="panel__heading">
<h1>New Request</h1> <h1>New Request</h1>
</div> </div>
{% block form_action %}
{% if request_id %} {% if request_id %}
<form method='POST' action="{{ reverse_url('request_form_update', current, request_id) }}" autocomplete="off"> <form method='POST' action="{{ reverse_url('request_form_update', current, request_id) }}" autocomplete="off">
{% else %} {% else %}
<form method='POST' action="{{ reverse_url('request_form_new', current) }}" autocomplete="off"> <form method='POST' action="{{ reverse_url('request_form_new', current) }}" autocomplete="off">
{% end %}
{% end %} {% end %}
{% module xsrf_form_html() %} {% module xsrf_form_html() %}
@ -34,18 +34,10 @@
{% end %} {% end %}
</form> </form>
</main> </main>
</div> </div>
</div> </div>
{% end %} {% end %}

View File

@ -49,6 +49,7 @@ class MockRequestsClient(MockApiClient):
"id": "66b8ef71-86d3-48ef-abc2-51bfa1732b6b", "id": "66b8ef71-86d3-48ef-abc2-51bfa1732b6b",
"creator": "49903ae7-da4a-49bf-a6dc-9dff5d004238", "creator": "49903ae7-da4a-49bf-a6dc-9dff5d004238",
"body": {}, "body": {},
"status": "incomplete"
} }
return self._get_response("GET", path, 200, json=json) return self._get_response("GET", path, 200, json=json)