2020-02-10 21:19:23 +00:00
|
|
|
from io import StringIO, BytesIO
|
|
|
|
|
|
|
|
from jinja2 import UndefinedError
|
|
|
|
|
|
|
|
from crc import session
|
|
|
|
from crc.api.common import ApiError
|
2020-03-04 18:40:25 +00:00
|
|
|
from crc.models.file import FileModel, FileDataModel, CONTENT_TYPES
|
2020-02-10 21:19:23 +00:00
|
|
|
from crc.models.workflow import WorkflowSpecModel
|
|
|
|
from docxtpl import DocxTemplate
|
|
|
|
import jinja2
|
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
from crc.scripts.script import Script
|
2020-02-28 16:54:11 +00:00
|
|
|
from crc.services.file_service import FileService
|
2020-02-10 21:19:23 +00:00
|
|
|
from crc.services.workflow_processor import WorkflowProcessor
|
|
|
|
|
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
class CompleteTemplate(Script):
|
|
|
|
|
|
|
|
def get_description(self):
|
2020-03-03 20:30:42 +00:00
|
|
|
return """Takes one argument, which is the name of a MS Word docx file to use as a template.
|
|
|
|
All data currently collected up to this Task will be available for use in the template."""
|
2020-02-10 21:19:23 +00:00
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
def do_task(self, task, study_id, *args, **kwargs):
|
2020-02-10 21:19:23 +00:00
|
|
|
"""Entry point, mostly worried about wiring it all up."""
|
|
|
|
if len(args) != 1:
|
|
|
|
raise ApiError(code="missing_argument",
|
|
|
|
message="The CompleteTask script requires a single argument with "
|
|
|
|
"the name of the docx template to use.")
|
|
|
|
file_name = args[0]
|
|
|
|
workflow_spec_model = self.find_spec_model_in_db(task.workflow)
|
|
|
|
|
|
|
|
if workflow_spec_model is None:
|
|
|
|
raise ApiError(code="workflow_model_error",
|
|
|
|
message="Something is wrong. I can't find the workflow you are using.")
|
|
|
|
|
|
|
|
file_data_model = session.query(FileDataModel) \
|
|
|
|
.join(FileModel) \
|
|
|
|
.filter(FileModel.name == file_name) \
|
|
|
|
.filter(FileModel.workflow_spec_id == workflow_spec_model.id).first()
|
|
|
|
|
|
|
|
if file_data_model is None:
|
|
|
|
raise ApiError(code="file_missing",
|
|
|
|
message="Can not find a file called '%s' "
|
|
|
|
"within workflow specification '%s'") % (args[0], workflow_spec_model.id)
|
|
|
|
|
2020-02-29 22:22:38 +00:00
|
|
|
final_document_stream = self.make_template(BytesIO(file_data_model.data), task.data)
|
2020-02-10 21:19:23 +00:00
|
|
|
study_id = task.workflow.data[WorkflowProcessor.STUDY_ID_KEY]
|
|
|
|
workflow_id = task.workflow.data[WorkflowProcessor.WORKFLOW_ID_KEY]
|
|
|
|
FileService.add_task_file(study_id=study_id, workflow_id=workflow_id, task_id=task.id,
|
|
|
|
name=file_name,
|
2020-03-04 18:40:25 +00:00
|
|
|
content_type=CONTENT_TYPES['docx'],
|
2020-02-10 21:19:23 +00:00
|
|
|
binary_data=final_document_stream.read())
|
|
|
|
|
|
|
|
print("Complete Task was called with %s" % str(args))
|
|
|
|
|
2020-02-29 22:22:38 +00:00
|
|
|
def make_template(self, binary_stream, context):
|
|
|
|
doc = DocxTemplate(binary_stream)
|
2020-02-12 16:07:01 +00:00
|
|
|
jinja_env = jinja2.Environment(autoescape=True)
|
2020-02-10 21:19:23 +00:00
|
|
|
doc.render(context, jinja_env)
|
|
|
|
target_stream = BytesIO()
|
|
|
|
doc.save(target_stream)
|
|
|
|
target_stream.seek(0) # move to the beginning of the stream.
|
|
|
|
return target_stream
|
|
|
|
|
|
|
|
def find_spec_model_in_db(self, workflow):
|
|
|
|
""" Search for the workflow """
|
|
|
|
# When the workflow spec model is created, we record the primary process id,
|
|
|
|
# then we can look it up. As there is the potential for sub-workflows, we
|
|
|
|
# may need to travel up to locate the primary process.
|
|
|
|
spec = workflow.spec
|
|
|
|
workflow_model = session.query(WorkflowSpecModel). \
|
|
|
|
filter(WorkflowSpecModel.primary_process_id == spec.name).first()
|
|
|
|
if workflow_model is None and workflow != workflow.outer_workflow:
|
|
|
|
return self.find_spec_model_in_db(workflow.outer_workflow)
|
|
|
|
|
|
|
|
return workflow_model
|