Merge pull request #255 from dod-ccpo/review-request-page-#159038263
Review request page #159038263
This commit is contained in:
@@ -25,6 +25,16 @@ class Authorization(object):
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def check_can_approve_request(cls, user):
|
||||
if (
|
||||
Permissions.REVIEW_AND_APPROVE_JEDI_WORKSPACE_REQUEST
|
||||
in user.atat_permissions
|
||||
):
|
||||
return True
|
||||
else:
|
||||
raise UnauthorizedError(user, "cannot review and approve requests")
|
||||
|
||||
@classmethod
|
||||
def check_workspace_permission(cls, user, workspace, permission, message):
|
||||
if not Authorization.has_workspace_permission(user, workspace, permission):
|
||||
|
||||
@@ -30,6 +30,8 @@ def readableInteger(value):
|
||||
|
||||
|
||||
def getOptionLabel(value, options):
|
||||
if hasattr(value, "value"):
|
||||
value = value.value
|
||||
try:
|
||||
return next(tup[1] for tup in options if tup[0] == value)
|
||||
except StopIteration:
|
||||
@@ -50,6 +52,19 @@ def mixedContentToJson(value):
|
||||
return app.jinja_env.filters["tojson"](value)
|
||||
|
||||
|
||||
def findFilter(value, filter_name, filter_args=[]):
|
||||
if not filter_name:
|
||||
return value
|
||||
elif filter_name in app.jinja_env.filters:
|
||||
return app.jinja_env.filters[filter_name](value, *filter_args)
|
||||
else:
|
||||
raise ValueError("filter name {} not found".format(filter_name))
|
||||
|
||||
|
||||
def renderList(value):
|
||||
return app.jinja_env.filters["safe"]("<br>".join(value))
|
||||
|
||||
|
||||
def register_filters(app):
|
||||
app.jinja_env.filters["iconSvg"] = iconSvg
|
||||
app.jinja_env.filters["dollars"] = dollars
|
||||
@@ -57,3 +72,5 @@ def register_filters(app):
|
||||
app.jinja_env.filters["readableInteger"] = readableInteger
|
||||
app.jinja_env.filters["getOptionLabel"] = getOptionLabel
|
||||
app.jinja_env.filters["mixedContentToJson"] = mixedContentToJson
|
||||
app.jinja_env.filters["findFilter"] = findFilter
|
||||
app.jinja_env.filters["renderList"] = renderList
|
||||
|
||||
@@ -140,3 +140,13 @@ WORKSPACE_ROLES = [
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
FUNDING_TYPES = [
|
||||
("", "- Select -"),
|
||||
("RDTE", "Research, Development, Testing & Evaluation (RDT&E)"),
|
||||
("OM", "Operations & Maintenance (O&M)"),
|
||||
("PROC", "Procurement (PROC)"),
|
||||
("OTHER", "Other"),
|
||||
]
|
||||
|
||||
TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")]
|
||||
|
||||
@@ -10,6 +10,7 @@ from atst.domain.task_orders import TaskOrders
|
||||
|
||||
from .fields import NewlineListField, SelectField
|
||||
from .forms import ValidatedForm
|
||||
from .data import FUNDING_TYPES
|
||||
|
||||
|
||||
PE_REGEX = re.compile(
|
||||
@@ -161,13 +162,7 @@ class ExtendedFinancialForm(BaseFinancialForm):
|
||||
|
||||
funding_type = SelectField(
|
||||
description="What is the source of funding?",
|
||||
choices=[
|
||||
("", "- Select -"),
|
||||
("RDTE", "Research, Development, Testing & Evaluation (RDT&E)"),
|
||||
("OM", "Operations & Maintenance (O&M)"),
|
||||
("PROC", "Procurement (PROC)"),
|
||||
("OTHER", "Other"),
|
||||
],
|
||||
choices=FUNDING_TYPES,
|
||||
validators=[Required()],
|
||||
render_kw={"required": False},
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ from atst.models import Base
|
||||
|
||||
class Source(Enum):
|
||||
MANUAL = "Manual"
|
||||
EDA = "eda"
|
||||
EDA = "EDA"
|
||||
|
||||
|
||||
class FundingType(Enum):
|
||||
|
||||
@@ -1,9 +1,52 @@
|
||||
from flask import render_template
|
||||
from flask import render_template, g, Response
|
||||
from flask import current_app as app
|
||||
|
||||
from . import requests_bp
|
||||
from atst.forms.data import SERVICE_BRANCHES
|
||||
from atst.domain.requests import Requests
|
||||
from atst.domain.exceptions import NotFoundError
|
||||
from atst.domain.authz import Authorization
|
||||
|
||||
|
||||
@requests_bp.route("/request_approval", methods=["GET"])
|
||||
def requests_approval():
|
||||
return render_template("request_approval.html", service_branches=SERVICE_BRANCHES)
|
||||
def task_order_dictionary(task_order):
|
||||
return {
|
||||
c.name: getattr(task_order, c.name)
|
||||
for c in task_order.__table__.columns
|
||||
if c.name not in ["id", "attachment_id"]
|
||||
}
|
||||
|
||||
|
||||
@requests_bp.route("/requests/approval/<string:request_id>", methods=["GET"])
|
||||
def approval(request_id):
|
||||
request = Requests.get(g.current_user, request_id)
|
||||
Authorization.check_can_approve_request(g.current_user)
|
||||
|
||||
data = request.body
|
||||
if request.task_order:
|
||||
data["task_order"] = task_order_dictionary(request.task_order)
|
||||
|
||||
return render_template(
|
||||
"requests/approval.html",
|
||||
data=data,
|
||||
request_id=request_id,
|
||||
status=request.status.value,
|
||||
financial_review=True,
|
||||
pdf_available=request.task_order and request.task_order.pdf,
|
||||
)
|
||||
|
||||
|
||||
@requests_bp.route("/requests/task_order_download/<string:request_id>", methods=["GET"])
|
||||
def task_order_pdf_download(request_id):
|
||||
request = Requests.get(g.current_user, request_id)
|
||||
if request.task_order and request.task_order.pdf:
|
||||
pdf = request.task_order.pdf
|
||||
generator = app.uploader.download_stream(pdf.object_name)
|
||||
return Response(
|
||||
generator,
|
||||
headers={
|
||||
"Content-Disposition": "attachment; filename={}".format(pdf.filename)
|
||||
},
|
||||
mimetype="application/pdf",
|
||||
)
|
||||
|
||||
else:
|
||||
raise NotFoundError("task_order pdf")
|
||||
|
||||
@@ -70,7 +70,9 @@ class RequestsIndex(object):
|
||||
else "-"
|
||||
)
|
||||
|
||||
if Requests.is_pending_financial_verification(request):
|
||||
if viewing_role == "ccpo":
|
||||
edit_link = url_for("requests.approval", request_id=request.id)
|
||||
elif Requests.is_pending_financial_verification(request):
|
||||
edit_link = url_for(
|
||||
"requests.financial_verification", request_id=request.id
|
||||
)
|
||||
|
||||
@@ -9,6 +9,8 @@ from atst.forms.data import (
|
||||
ASSISTANCE_ORG_TYPES,
|
||||
DATA_TRANSFER_AMOUNTS,
|
||||
COMPLETION_DATE_RANGES,
|
||||
FUNDING_TYPES,
|
||||
TASK_ORDER_SOURCES,
|
||||
)
|
||||
|
||||
|
||||
@@ -19,6 +21,8 @@ def option_data():
|
||||
"assistance_org_types": ASSISTANCE_ORG_TYPES,
|
||||
"data_transfer_amounts": DATA_TRANSFER_AMOUNTS,
|
||||
"completion_date_ranges": COMPLETION_DATE_RANGES,
|
||||
"funding_types": FUNDING_TYPES,
|
||||
"task_order_sources": TASK_ORDER_SOURCES,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,8 +37,11 @@ class Uploader:
|
||||
)
|
||||
return (fyle.filename, object_name)
|
||||
|
||||
def download(self, path):
|
||||
pass
|
||||
def download_stream(self, object_name):
|
||||
obj = self.container.get_object(object_name=object_name)
|
||||
with NamedTemporaryFile() as tempfile:
|
||||
obj.download(tempfile.name, overwrite_existing=True)
|
||||
return open(tempfile.name, "rb")
|
||||
|
||||
def _get_container(self, provider, container, key, secret):
|
||||
if provider == "LOCAL":
|
||||
|
||||
Reference in New Issue
Block a user