Merge pull request #566 from dod-ccpo/upload-csp-estimate

Upload proof of CSP estimate
This commit is contained in:
patricksmithdds
2019-01-23 14:56:28 -05:00
committed by GitHub
20 changed files with 223 additions and 31 deletions

View File

@@ -8,7 +8,7 @@ from atst.domain.exceptions import UploadError
class FileProviderInterface:
_PERMITTED_MIMETYPES = ["application/pdf"]
_PERMITTED_MIMETYPES = ["application/pdf", "image/png"]
def _enforce_mimetype(self, fyle):
# TODO: for hardening, we should probably use a better library for
@@ -57,6 +57,7 @@ class RackspaceFileProvider(FileProviderInterface):
object_name = uuid4().hex
with NamedTemporaryFile() as tempfile:
tempfile.write(fyle.stream.read())
tempfile.seek(0)
self.container.upload_object(
file_path=tempfile.name,
object_name=object_name,

View File

@@ -25,7 +25,7 @@ class TaskOrders(object):
],
"funding": [
"performance_length",
# "pdf",
"csp_estimate",
"clin_01",
"clin_02",
"clin_03",

View File

@@ -54,12 +54,11 @@ def mixedContentToJson(value):
This coerces the file upload in form data to its filename
so that the data can be JSON serialized.
"""
if (
isinstance(value, dict)
and "legacy_task_order" in value
and hasattr(value["legacy_task_order"]["pdf"], "filename")
):
value["legacy_task_order"]["pdf"] = value["legacy_task_order"]["pdf"].filename
if isinstance(value, dict):
for k, v in value.items():
if hasattr(v, "filename"):
value[k] = v.filename
return app.jinja_env.filters["tojson"](value)

View File

@@ -11,6 +11,7 @@ from wtforms.fields import (
from wtforms.fields.html5 import DateField, TelField
from wtforms.widgets import ListWidget, CheckboxInput
from wtforms.validators import Length
from flask_wtf.file import FileAllowed
from atst.forms.validators import IsNumber, PhoneNumber, RequiredIf
@@ -86,9 +87,15 @@ class FundingForm(CacheableForm):
end_date = DateField(
translate("forms.task_order.end_date_label"), format="%m/%d/%Y"
)
pdf = FileField(
translate("forms.task_order.pdf_label"),
description=translate("forms.task_order.pdf_description"),
csp_estimate = FileField(
translate("forms.task_order.csp_estimate_label"),
description=translate("forms.task_order.csp_estimate_description"),
validators=[
FileAllowed(
["pdf", "png"], translate("forms.task_order.file_format_not_allowed")
)
],
render_kw={"accept": ".pdf,.png,application/pdf,image/png"},
)
clin_01 = IntegerField(translate("forms.task_order.clin_01_label"))
clin_02 = IntegerField(translate("forms.task_order.clin_02_label"))

View File

@@ -2,10 +2,12 @@ from enum import Enum
import pendulum
from sqlalchemy import Column, Numeric, String, ForeignKey, Date, Integer
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.types import ARRAY
from sqlalchemy.orm import relationship
from werkzeug.datastructures import FileStorage
from atst.models import Base, types, mixins
from atst.models import Attachment, Base, types, mixins
class Status(Enum):
@@ -49,7 +51,7 @@ class TaskOrder(Base, mixins.TimestampsMixin):
end_date = Column(Date)
performance_length = Column(Integer)
attachment_id = Column(ForeignKey("attachments.id"))
pdf = relationship("Attachment")
_csp_estimate = relationship("Attachment")
clin_01 = Column(Numeric(scale=2))
clin_02 = Column(Numeric(scale=2))
clin_03 = Column(Numeric(scale=2))
@@ -72,6 +74,23 @@ class TaskOrder(Base, mixins.TimestampsMixin):
number = Column(String, unique=True) # Task Order Number
loa = Column(ARRAY(String)) # Line of Accounting (LOA)
@hybrid_property
def csp_estimate(self):
return self._csp_estimate
@csp_estimate.setter
def csp_estimate(self, new_csp_estimate):
if isinstance(new_csp_estimate, Attachment):
self._csp_estimate = new_csp_estimate
elif isinstance(new_csp_estimate, FileStorage):
self._csp_estimate = Attachment.attach(
new_csp_estimate, "task_order", self.id
)
elif not new_csp_estimate and self._csp_estimate:
self._csp_estimate = None
else:
raise TypeError("Could not set csp_estimate with invalid type")
@property
def is_submitted(self):
return self.number is not None

View File

@@ -1,8 +1,9 @@
from io import BytesIO
from flask import g, Response
from flask import g, Response, current_app as app
from . import task_orders_bp
from atst.domain.task_orders import TaskOrders
from atst.domain.exceptions import NotFoundError
from atst.utils.docx import Docx
@@ -17,3 +18,22 @@ def download_summary(task_order_id):
headers={"Content-Disposition": "attachment; filename={}".format(filename)},
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
@task_orders_bp.route("/task_orders/csp_estimate/<task_order_id>")
def download_csp_estimate(task_order_id):
task_order = TaskOrders.get(g.current_user, task_order_id)
if task_order.csp_estimate:
estimate = task_order.csp_estimate
generator = app.csp.files.download(estimate.object_name)
return Response(
generator,
headers={
"Content-Disposition": "attachment; filename={}".format(
estimate.filename
)
},
)
else:
raise NotFoundError("task_order CSP estimate")

View File

@@ -272,8 +272,9 @@ def new(screen, task_order_id=None, portfolio_id=None):
"/portfolios/<portfolio_id>/task_orders/new/<int:screen>", methods=["POST"]
)
def update(screen, task_order_id=None, portfolio_id=None):
form_data = {**http_request.form, **http_request.files}
workflow = UpdateTaskOrderWorkflow(
g.current_user, http_request.form, screen, task_order_id, portfolio_id
g.current_user, form_data, screen, task_order_id, portfolio_id
)
if workflow.validate():