First page of request form rendering, but flow is still WIP

This commit is contained in:
richard-dds 2018-08-02 10:21:26 -04:00
parent 2618d92e12
commit f15fc19eaf
17 changed files with 398 additions and 152 deletions

View File

@ -101,7 +101,6 @@ class RequestNew(BaseHandler):
class JEDIRequestFlow(object): class JEDIRequestFlow(object):
def __init__( def __init__(
self, self,
requests_repo,
pe_numbers_repo, pe_numbers_repo,
current_step, current_step,
request=None, request=None,
@ -110,6 +109,8 @@ class JEDIRequestFlow(object):
current_user=None, current_user=None,
existing_request=None, existing_request=None,
): ):
self.pe_numbers_repo = pe_numbers_repo
self.requests_repo = requests_repo self.requests_repo = requests_repo
self.pe_numbers_repo = pe_numbers_repo self.pe_numbers_repo = pe_numbers_repo

View File

@ -1,8 +1,14 @@
from flask import Blueprint, g, render_template from flask import Blueprint, g, render_template, url_for, redirect, request
import pendulum import pendulum
from collections import defaultdict
from atst.domain.requests import Requests from atst.domain.requests import Requests
from atst.forms.financial import FinancialForm from atst.forms.financial import FinancialForm
from atst.forms.request import RequestForm
from atst.forms.org import OrgForm
from atst.forms.poc import POCForm
from atst.forms.review import ReviewForm
requests_bp = Blueprint("requests", __name__) requests_bp = Blueprint("requests", __name__)
@ -34,13 +40,72 @@ def requests_index():
@requests_bp.route("/requests/new/<int:screen>", methods=["GET"]) @requests_bp.route("/requests/new/<int:screen>", methods=["GET"])
def requests_new(): def requests_form_new():
pass pass
@requests_bp.route("/requests/new/<int:screen>/<string:request_id>", methods=["GET"]) @requests_bp.route("/requests/new/<int:screen>/<string:request_id>", methods=["GET"])
def requests_form_update(): def requests_form_update(screen=1, request_id=None):
pass request = Requests.get(request_id) if request_id is not None else None
jedi_flow = JEDIRequestFlow(screen, request, request_id=request_id)
return render_template(
"requests/screen-%d.html" % int(screen),
f=jedi_flow.form,
data=jedi_flow.current_step_data,
screens=jedi_flow.screens,
current=screen,
next_screen=screen + 1,
request_id=request_id,
can_submit=jedi_flow.can_submit
)
@requests_bp.route("/requests/new/<int:screen>/<string:request_id>", methods=["POST"])
def requests_update(screen=1, request_id=None):
screen = int(screen)
post_data = str(request.data)
current_user = g.current_user
existing_request = Requests.get(request_id) if request_id is not None else None
jedi_flow = JEDIRequestFlow(
screen,
post_data=post_data,
request_id=request_id,
current_user=current_user,
existing_request=existing_request,
)
rerender_args = dict(
f=jedi_flow.form,
data=post_data,
screens=jedi_flow.screens,
current=screen,
next_screen=jedi_flow.next_screen,
request_id=jedi_flow.request_id,
)
if jedi_flow.validate():
jedi_flow.create_or_update_request()
valid = jedi_flow.validate_warnings()
if valid:
if jedi_flow.next_screen > len(jedi_flow.screens):
where = "/requests"
else:
where = url_for(
"requests.requests_Form_update", screen=jedi_flow.next_screen, request_id=jedi_flow.request_id
)
return redirect(where)
else:
return render_template(
"requests/screen-%d.html" % int(screen),
**rerender_args
)
else:
return render_template(
"requests/screen-%d.html" % int(screen),
**rerender_args
)
@requests_bp.route("/requests/verify/<string:request_id>", methods=["GET"]) @requests_bp.route("/requests/verify/<string:request_id>", methods=["GET"])
@ -53,3 +118,129 @@ def financial_verification(request_id=None):
@requests_bp.route("/requests/verify/<string:request_id>", methods=["POST"]) @requests_bp.route("/requests/verify/<string:request_id>", methods=["POST"])
def update_financial_verification(): def update_financial_verification():
pass pass
class JEDIRequestFlow(object):
def __init__(
self,
current_step,
request=None,
post_data=None,
request_id=None,
current_user=None,
existing_request=None,
):
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()
self.current_user = current_user
self.existing_request = existing_request
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()
def validate_warnings(self):
existing_request_data = (
self.existing_request
and self.existing_request.body.get(self.form_section)
) or None
valid = self.form.perform_extra_validation(
existing_request_data,
)
return valid
@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):
data = {}
if self.is_post:
data = self.post_data
if self.request:
if self.form_section == "review_submit":
data = self.request.body
else:
data = self.request.body.get(self.form_section, {})
return defaultdict(lambda: defaultdict(lambda: 'Input required'), data)
@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,
},
]
def create_or_update_request(self):
request_data = {
self.form_section: self.form.data
}
if self.request_id:
Requests.update(request_id, request_data)
else:
request = Requests.create(self.current_user["id"], request_data)
self.request_id = request.id

View File

@ -90,3 +90,56 @@
</div> </div>
</div> </div>
{%- endmacro %} {%- endmacro %}
{% macro TextInput(field, placeholder='') -%}
<div class='usa-input {% if errors %}usa-input--error{% endif %}'>
<label for={{field.name}}>
{{ field.label }}
{% if field.description %}
<span class='usa-input__help'>{{ field.description | safe }}</span>
{% endif %}
{% if errors %}
{{ Icon('alert') }}
{% endif %}
</label>
{{ field(placeholder=placeholder) | safe }}
{% if field.errors %}
{% for error in field.errors %}
<span class='usa-input__message'>{{ error }}</span>
{% endfor %}
{% endif %}
</div>
{%- endmacro %}
{% macro OptionsInput(field, inline=False) -%}
<div class='usa-input {% if errors %}usa-input--error{% endif %}'>
<fieldset class="usa-input__choices {% if inline %}usa-input__choices--inline{% endif %}">
<legend>
{{ field.label }}
{% if field.description %}
<span class='usa-input__help'>{{ field.description | safe }}</span>
{% endif %}
{% if field.errors %}
{{ Icon('alert') }}
{% endif %}
</legend>
{{ field() }}
{% if field.errors %}
{% for error in field.errors %}
<span class='usa-input__message'>{{ error }}</span>
{% endfor %}
{% endif %}
</fieldset>
</div>
{%- endmacro %}

View File

@ -0,0 +1,13 @@
<div class="progress-menu progress-menu--four">
<ul>
{% for s in screens %}
<li class="progress-menu__item">
<a href="{{ url_for('requests.requests_form_update', screen=loop.index+1, request_id=request_id) if request_id else url_for('requests.requests_form_new', screen=loop.index+1) }}"
{% if g.matchesPath('/requests/new/{{ loop.index + 1 }}') %}class="active"{% endif %}
>
{{ s['title'] }}
</a>
</li>
{% endfor %}
</ul>
</div>

View File

@ -1,13 +0,0 @@
<div class="progress-menu progress-menu--four">
<ul>
{% for i,s in enumerate(screens) %}
<li class="progress-menu__item">
<a href="{{ reverse_url('request_form_update', i+1, request_id) if request_id else reverse_url('request_form_new',i+1) }}"
{% if matchesPath('/requests/new/'+str(i+1)) %}class="active"{% end %}
>
{{ s['title'] }}
</a>
</li>
{% end %}
</ul>
</div>

View File

@ -0,0 +1,12 @@
{% extends '../requests_new.html.to' %}
{% block form %}
<h2>New JEDI Request</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Natus error omnis a, tenetur similique quo officiis voluptates eum recusandae dolorem minus dignissimos, magni consequatur, maxime debitis reprehenderit sint non iusto?</p>
<a class='usa-button usa-button-secondary' href='{{ url_for('requests.request_form_new',next_screen) }}'>New Application</a>
<a class='usa-button usa-button-secondary' href='{{ url_for('requests.request_form_new',next_screen) }}'>Existing Application</a>
<a class='usa-button usa-button-secondary' href='{{ url_for('requests.request_form_new',next_screen) }}'>Sandbox Environment</a>
{% endblock %}

View File

@ -1,12 +0,0 @@
{% extends '../requests_new.html.to' %}
{% block form %}
<h2>New JEDI Request</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Natus error omnis a, tenetur similique quo officiis voluptates eum recusandae dolorem minus dignissimos, magni consequatur, maxime debitis reprehenderit sint non iusto?</p>
<a class='usa-button usa-button-secondary' href='{{ reverse_url('request_form_new',next_screen) }}'>New Application</a>
<a class='usa-button usa-button-secondary' href='{{ reverse_url('request_form_new',next_screen) }}'>Existing Application</a>
<a class='usa-button usa-button-secondary' href='{{ reverse_url('request_form_new',next_screen) }}'>Sandbox Environment</a>
{% end %}

View File

@ -0,0 +1,46 @@
{% extends 'requests_new.html' %}
{% from "components.html" import Alert, TextInput, OptionsInput %}
{% block subtitle %}
<h2>Details of Use</h2>
{% endblock %}
{% block form %}
{% if f.errors %}
{{ Alert('There were some errors',
message="<p>Please see below.</p>",
level='error'
) }}
{% endif %}
<p>Wed like to know a little about how you plan to use JEDI Cloud services to process your request. Please answer the following questions to the best of your ability. Note that the CCPO does not directly help with migrating systems to JEDI Cloud. These questions are for learning about your cloud readiness and financial usage of the JEDI Cloud; your estimates will not be used for any department level reporting.</p>
<p><em>All fields are required, unless specified optional.</em></p>
<h2>General</h2>
{{ TextInput(f.dod_component) }}
{{ TextInput(f.jedi_usage,placeholder="e.g. We are migrating XYZ application to the cloud so that...") }}
<h2>Cloud Readiness</h2>
{{ TextInput(f.num_software_systems,placeholder="Number of systems") }}
{{ OptionsInput(f.jedi_migration) }}
{{ OptionsInput(f.rationalization_software_systems) }}
{{ OptionsInput(f.technical_support_team) }}
{{ OptionsInput(f.organization_providing_assistance) }}
{{ OptionsInput(f.engineering_assessment) }}
{{ TextInput(f.data_transfers) }}
{{ TextInput(f.expected_completion_date) }}
{{ OptionsInput(f.cloud_native) }}
<h2>Financial Usage</h2>
{{ TextInput(f.estimated_monthly_spend) }}
<p>So this means you are spending approximately <b>$X</b> annually</p>
{{ TextInput(f.dollar_value) }}
{{ TextInput(f.number_user_sessions) }}
{{ TextInput(f.average_daily_traffic) }}
{{ TextInput(f.start_date) }}
{% endblock %}

View File

@ -1,45 +0,0 @@
{% extends '../requests_new.html.to' %}
{% block subtitle %}
<h2>Details of Use</h2>
{% end %}
{% block form %}
{% autoescape None %}
{% if f.errors %}
{% module Alert('There were some errors',
message="<p>Please see below.</p>",
level='error'
) %}
{% end %}
<p>Wed like to know a little about how you plan to use JEDI Cloud services to process your request. Please answer the following questions to the best of your ability. Note that the CCPO does not directly help with migrating systems to JEDI Cloud. These questions are for learning about your cloud readiness and financial usage of the JEDI Cloud; your estimates will not be used for any department level reporting.</p>
<p><em>All fields are required, unless specified optional.</em></p>
<h2>General</h2>
{% module TextInput(f.dod_component) %}
{% module TextInput(f.jedi_usage,placeholder="e.g. We are migrating XYZ application to the cloud so that...") %}
<h2>Cloud Readiness</h2>
{% module TextInput(f.num_software_systems,placeholder="Number of systems") %}
{% module OptionsInput(f.jedi_migration) %}
{% module OptionsInput(f.rationalization_software_systems) %}
{% module OptionsInput(f.technical_support_team) %}
{% module OptionsInput(f.organization_providing_assistance) %}
{% module OptionsInput(f.engineering_assessment) %}
{% module TextInput(f.data_transfers) %}
{% module TextInput(f.expected_completion_date) %}
{% module OptionsInput(f.cloud_native) %}
<h2>Financial Usage</h2>
{% module TextInput(f.estimated_monthly_spend) %}
<p>So this means you are spending approximately <b>$X</b> annually</p>
{% module TextInput(f.dollar_value) %}
{% module TextInput(f.number_user_sessions) %}
{% module TextInput(f.average_daily_traffic) %}
{% module TextInput(f.start_date) %}
{% end %}

View File

@ -0,0 +1,32 @@
{% extends 'requests_new.html' %}
{% from "components.html" import Alert, TextInput, OptionsInput %}
{% block subtitle %}
<h2>Information About You</h2>
{% endblock %}
{% block form %}
{% if f.errors %}
{{ Alert('There were some errors',
message="<p>Please see below.</p>",
level='error'
) }}
{% endif %}
<p>Please tell us more about you.</p>
{{ TextInput(f.fname_request,placeholder='First Name') }}
{{ TextInput(f.lname_request,placeholder='Last Name') }}
{{ TextInput(f.email_request,placeholder='jane@mail.mil') }}
{{ TextInput(f.phone_number,placeholder='(123) 456-7890') }}
<p>We want to collect the following information from you for security auditing and determining priviledged user access</p>
{{ TextInput(f.service_branch,placeholder='e.g. US Air Force, US Army, US Navy, Marine Corps, Defense Media Agency') }}
{{ OptionsInput(f.citizenship) }}
{{ OptionsInput(f.designation) }}
{{ TextInput(f.date_latest_training) }}
{% endblock %}

View File

@ -1,31 +0,0 @@
{% extends '../requests_new.html.to' %}
{% block subtitle %}
<h2>Information About You</h2>
{% end %}
{% block form %}
{% autoescape None %}
{% if f.errors %}
{% module Alert('There were some errors',
message="<p>Please see below.</p>",
level='error'
) %}
{% end %}
<p>Please tell us more about you.</p>
{% module TextInput(f.fname_request,placeholder='First Name') %}
{% module TextInput(f.lname_request,placeholder='Last Name') %}
{% module TextInput(f.email_request,placeholder='jane@mail.mil') %}
{% module TextInput(f.phone_number,placeholder='(123) 456-7890') %}
<p>We want to collect the following information from you for security auditing and determining priviledged user access</p>
{% module TextInput(f.service_branch,placeholder='e.g. US Air Force, US Army, US Navy, Marine Corps, Defense Media Agency') %}
{% module OptionsInput(f.citizenship) %}
{% module OptionsInput(f.designation) %}
{% module TextInput(f.date_latest_training) %}
{% end %}

View File

@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block content %}
<div class="col">
{% include 'requests/menu.html' %}
<div class="panel">
<div class="panel__heading">
<h1>New Request</h1>
<div class="subtitle">{% block subtitle %}{% endblock %}</div>
</div>
<div class="panel__content">
{% block form_action %}
{% if request_id %}
<form method='POST' action="{{ url_for('requests.requests_form_update', screen=current, request_id=request_id) }}" autocomplete="off">
{% else %}
<form method='POST' action="{{ url_for('requests.requests_form_update', screen=current) }}" autocomplete="off">
{% endif %}
{% endblock %}
{{ f.csrf_token }}
{% block form %}
form goes here
{% endblock %}
</div>
</div>
{% block next %}
<div class='action-group'>
<input type='submit' class='usa-button usa-button-primary' value='Save & Continue' />
</div>
{% endblock %}
</form>
</div>
{% endblock %}

View File

@ -1,46 +0,0 @@
{% extends "base.html.to" %}
{% block content %}
<div class="col">
{% include 'requests/menu.html.to' %}
<div class="panel">
<div class="panel__heading">
<h1>New Request</h1>
<div class="subtitle">{% block subtitle %}{% end %}</div>
</div>
<div class="panel__content">
{% block form_action %}
{% if request_id %}
<form method='POST' action="{{ reverse_url('request_form_update', current, request_id) }}" autocomplete="off">
{% else %}
<form method='POST' action="{{ reverse_url('request_form_new', current) }}" autocomplete="off">
{% end %}
{% end %}
{{ form.csrf_token }}
{% block form %}
form goes here
{% end %}
</div>
</div>
{% block next %}
<div class='action-group'>
<input type='submit' class='usa-button usa-button-primary' value='Save & Continue' />
</div>
{% end %}
</form>
</div>
{% end %}