Merge pull request #508 from dod-ccpo/task-order-download

Task Order review and download
This commit is contained in:
dandds
2019-01-02 13:48:45 -05:00
committed by GitHub
15 changed files with 313 additions and 7 deletions

View File

@@ -57,9 +57,13 @@ class TaskOrder(Base, mixins.TimestampsMixin):
self.number, self.budget, self.end_date, self.id
)
@property
def portfolio_name(self):
return self.workspace.name
def to_dictionary(self):
return {
"portfolio_name": self.workspace.name,
"portfolio_name": self.portfolio_name,
**{
c.name: getattr(self, c.name)
for c in self.__table__.columns

View File

@@ -3,3 +3,5 @@ from flask import Blueprint
task_orders_bp = Blueprint("task_orders", __name__)
from . import new
from . import index
from . import invite

View File

@@ -0,0 +1,19 @@
from io import BytesIO
from flask import Response
from . import task_orders_bp
from atst.domain.task_orders import TaskOrders
from atst.utils.docx import Docx
@task_orders_bp.route("/task_orders/download_summary/<task_order_id>")
def download_summary(task_order_id):
task_order = TaskOrders.get(task_order_id)
byte_str = BytesIO()
Docx.render(byte_str, data=task_order.to_dictionary())
filename = "{}.docx".format(task_order.portfolio_name)
return Response(
byte_str,
headers={"Content-Disposition": "attachment; filename={}".format(filename)},
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)

View File

@@ -0,0 +1,15 @@
from flask import redirect, url_for
from . import task_orders_bp
from atst.domain.task_orders import TaskOrders
from atst.utils.flash import formatted_flash as flash
# TODO: add a real implementation for this
@task_orders_bp.route("/task_orders/invite/<task_order_id>", methods=["POST"])
def invite(task_order_id):
task_order = TaskOrders.get(task_order_id)
flash("task_order_submitted", task_order=task_order)
return redirect(
url_for("workspaces.workspace_members", workspace_id=task_order.workspace.id)
)

View File

@@ -113,6 +113,7 @@ def new(screen, task_order_id=None):
workflow.template,
current=screen,
task_order_id=task_order_id,
task_order=workflow.task_order,
screens=workflow.display_screens,
form=workflow.form,
)

57
atst/utils/docx.py Normal file
View File

@@ -0,0 +1,57 @@
import os
from zipfile import ZipFile
from flask import render_template, current_app as app
class Docx:
DOCUMENT_FILE = "word/document.xml"
@classmethod
def _template_path(cls, docx_file):
return os.path.join(app.root_path, "..", "templates", docx_file)
@classmethod
def _template(cls, docx_file):
return ZipFile(Docx._template_path(docx_file), mode="r")
@classmethod
def _write(cls, docx_template, docx_file, document_content):
"""
This method takes an existing docx as its starting
point and copies over every file from it to a new zip
file, overwriting the document.xml file with new
document content.
zipfile.ZipFile does not provide a way to replace file
contents in a zip in-place, so we copy over the entire
zip archive instead.
docx_template: The source docx file we harvest from.
docx_file: A ZipFile instance that content from the docx_template is copied to
document_content: The new content for the document.xml file
"""
with docx_template as template:
for item in template.infolist():
if item.filename != Docx.DOCUMENT_FILE:
content = template.read(item.filename).decode()
else:
content = document_content
docx_file.writestr(item, content)
return docx_file
@classmethod
def render(
cls,
file_like,
doc_template="docx/document.xml",
file_template="docx/template.docx",
**args,
):
document = render_template(doc_template, **args)
with ZipFile(file_like, mode="w") as docx_file:
docx_template = Docx._template(file_template)
Docx._write(docx_template, docx_file, document)
file_like.seek(0)
return file_like

View File

@@ -101,6 +101,13 @@ MESSAGES = {
"message_template": "",
"category": "success",
},
"task_order_submitted": {
"title_template": "Task Order Form Submitted",
"message_template": """
Your task order form for {{ task_order.portfolio_name }} has been submitted.
""",
"category": "success",
},
}