Merge branch 'master' into feature_personnel_multi_instance
This commit is contained in:
commit
678a45fa76
|
@ -241,11 +241,11 @@
|
|||
},
|
||||
"docxtpl": {
|
||||
"hashes": [
|
||||
"sha256:216af2580b9f697c2f748faf06c0bfbf47a782f2dd10ad87824a4c5ecbd37008",
|
||||
"sha256:f5fed6ff724d802f1b151c86ee6141b17cc6fc2fe1979b7840b11db4bd633e48"
|
||||
"sha256:a46c9cd6ea6d7350a8f16b467c3b1cd09767c83e1da5753f306cc550a7b04959",
|
||||
"sha256:ab92c5710b6774eff52a90529fb96af29aacfc2d14c0986b6f58ac5bfe403bdf"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.8.0"
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"et-xmlfile": {
|
||||
"hashes": [
|
||||
|
@ -768,7 +768,7 @@
|
|||
"spiffworkflow": {
|
||||
"editable": true,
|
||||
"git": "https://github.com/sartography/SpiffWorkflow.git",
|
||||
"ref": "618be41e7e6b20f87865cf9fdd96a79c3cbee065"
|
||||
"ref": "d46213c9c20859b42ff26c12f852fd32a58d3280"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import os
|
||||
from os import environ
|
||||
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
NAME = "CR Connect Workflow"
|
||||
CORS_ENABLED = False
|
||||
DEVELOPMENT = True
|
||||
SQLALCHEMY_DATABASE_URI = "postgresql://crc_user:crc_pass@localhost:5432/crc_dev"
|
||||
DEVELOPMENT = environ.get('DEVELOPMENT', default="True")
|
||||
|
||||
DB_HOST = environ.get('DB_HOST', default="localhost")
|
||||
DB_PORT = environ.get('DB_PORT', default="5432")
|
||||
DB_NAME = environ.get('DB_NAME', default="crc_dev")
|
||||
DB_USER = environ.get('DB_USER', default="crc_user")
|
||||
DB_PASSWORD = environ.get('DB_PASSWORD', default="crc_pass")
|
||||
SQLALCHEMY_DATABASE_URI = environ.get('SQLALCHEMY_DATABASE_URI', default="postgresql://%s:%s@%s:%s/%s" % (DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME))
|
||||
TOKEN_AUTH_TTL_HOURS = 2
|
||||
TOKEN_AUTH_SECRET_KEY = "Shhhh!!! This is secret! And better darn well not show up in prod."
|
||||
FRONTEND_AUTH_CALLBACK = "http://localhost:4200/session"
|
||||
SWAGGER_AUTH_KEY = "SWAGGER"
|
||||
TOKEN_AUTH_SECRET_KEY = environ.get('TOKEN_AUTH_SECRET_KEY', default="Shhhh!!! This is secret! And better darn well not show up in prod.")
|
||||
FRONTEND_AUTH_CALLBACK = environ.get('FRONTEND_AUTH_CALLBACK', default="http://localhost:4200/session")
|
||||
SWAGGER_AUTH_KEY = environ.get('SWAGGER_AUTH_KEY', default="SWAGGER")
|
||||
|
||||
#: Default attribute map for single signon.
|
||||
SSO_ATTRIBUTE_MAP = {
|
||||
|
@ -24,7 +31,8 @@ SSO_ATTRIBUTE_MAP = {
|
|||
}
|
||||
|
||||
# %s/%i placeholders expected for uva_id and study_id in various calls.
|
||||
PB_USER_STUDIES_URL = "http://workflow.sartography.com:5001/pb/user_studies?uva_id=%s"
|
||||
PB_INVESTIGATORS_URL = "http://workflow.sartography.com:5001/pb/investigators?studyid=%i"
|
||||
PB_REQUIRED_DOCS_URL = "http://workflow.sartography.com:5001/pb/required_docs?studyid=%i"
|
||||
PB_STUDY_DETAILS_URL = "http://workflow.sartography.com:5001/pb/study?studyid=%i"
|
||||
PB_BASE_URL = environ.get('PB_BASE_URL', default="http://localhost:5001/pb/")
|
||||
PB_USER_STUDIES_URL = environ.get('PB_USER_STUDIES_URL', default=PB_BASE_URL + "user_studies?uva_id=%s")
|
||||
PB_INVESTIGATORS_URL = environ.get('PB_INVESTIGATORS_URL', default=PB_BASE_URL + "investigators?studyid=%i")
|
||||
PB_REQUIRED_DOCS_URL = environ.get('PB_REQUIRED_DOCS_URL', default=PB_BASE_URL + "required_docs?studyid=%i")
|
||||
PB_STUDY_DETAILS_URL = environ.get('PB_STUDY_DETAILS_URL', default=PB_BASE_URL + "study?studyid=%i")
|
||||
|
|
|
@ -4,7 +4,7 @@ import connexion
|
|||
from flask import send_file
|
||||
|
||||
from crc import session
|
||||
from crc.api.common import ApiErrorSchema, ApiError
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileModelSchema, FileModel, FileDataModel
|
||||
from crc.models.workflow import WorkflowSpecModel
|
||||
from crc.services.file_service import FileService
|
||||
|
|
|
@ -10,6 +10,7 @@ from crc.models.stats import WorkflowStatsModel, TaskEventModel
|
|||
from crc.models.workflow import WorkflowModel, WorkflowSpecModelSchema, WorkflowSpecModel, WorkflowSpecCategoryModel, \
|
||||
WorkflowSpecCategoryModelSchema
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
|
||||
|
||||
def all_specifications():
|
||||
|
@ -40,7 +41,7 @@ def validate_workflow_specification(spec_id):
|
|||
|
||||
errors = []
|
||||
try:
|
||||
WorkflowProcessor.test_spec(spec_id)
|
||||
WorkflowService.test_spec(spec_id)
|
||||
except ApiError as ae:
|
||||
errors.append(ae)
|
||||
return ApiErrorSchema(many=True).dump(errors)
|
||||
|
@ -85,7 +86,7 @@ def delete_workflow_specification(spec_id):
|
|||
|
||||
def __get_workflow_api_model(processor: WorkflowProcessor, status_data=None):
|
||||
spiff_tasks = processor.get_all_user_tasks()
|
||||
user_tasks = list(map(Task.from_spiff, spiff_tasks))
|
||||
user_tasks = list(map(WorkflowService.spiff_task_to_api_task, spiff_tasks))
|
||||
is_active = True
|
||||
|
||||
if status_data is not None and processor.workflow_spec_id in status_data:
|
||||
|
@ -94,7 +95,7 @@ def __get_workflow_api_model(processor: WorkflowProcessor, status_data=None):
|
|||
workflow_api = WorkflowApi(
|
||||
id=processor.get_workflow_id(),
|
||||
status=processor.get_status(),
|
||||
last_task=Task.from_spiff(processor.bpmn_workflow.last_task),
|
||||
last_task=WorkflowService.spiff_task_to_api_task(processor.bpmn_workflow.last_task),
|
||||
next_task=None,
|
||||
user_tasks=user_tasks,
|
||||
workflow_spec_id=processor.workflow_spec_id,
|
||||
|
@ -102,7 +103,7 @@ def __get_workflow_api_model(processor: WorkflowProcessor, status_data=None):
|
|||
is_latest_spec=processor.get_spec_version() == processor.get_latest_version_string(processor.workflow_spec_id),
|
||||
)
|
||||
if processor.next_task():
|
||||
workflow_api.next_task = Task.from_spiff(processor.next_task())
|
||||
workflow_api.next_task = WorkflowService.spiff_task_to_api_task(processor.next_task())
|
||||
return workflow_api
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import jinja2
|
||||
import marshmallow
|
||||
from jinja2 import Template
|
||||
from marshmallow import INCLUDE
|
||||
from marshmallow_enum import EnumField
|
||||
|
||||
from crc import ma
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.workflow import WorkflowStatus
|
||||
|
||||
|
||||
class Task(object):
|
||||
|
||||
ENUM_OPTIONS_FILE_PROP = "enum.options.file"
|
||||
EMUM_OPTIONS_VALUE_COL_PROP = "enum.options.value.column"
|
||||
EMUM_OPTIONS_LABEL_COL_PROP = "enum.options.label.column"
|
||||
|
||||
def __init__(self, id, name, title, type, state, form, documentation, data):
|
||||
self.id = id
|
||||
self.name = name
|
||||
|
@ -20,35 +22,6 @@ class Task(object):
|
|||
self.documentation = documentation
|
||||
self.data = data
|
||||
|
||||
@classmethod
|
||||
def from_spiff(cls, spiff_task):
|
||||
documentation = spiff_task.task_spec.documentation if hasattr(spiff_task.task_spec, "documentation") else ""
|
||||
instance = cls(spiff_task.id,
|
||||
spiff_task.task_spec.name,
|
||||
spiff_task.task_spec.description,
|
||||
spiff_task.task_spec.__class__.__name__,
|
||||
spiff_task.get_state_name(),
|
||||
None,
|
||||
documentation,
|
||||
spiff_task.data)
|
||||
if hasattr(spiff_task.task_spec, "form"):
|
||||
instance.form = spiff_task.task_spec.form
|
||||
if documentation != "" and documentation is not None:
|
||||
|
||||
instance.process_documentation(documentation)
|
||||
return instance
|
||||
|
||||
def process_documentation(self, documentation):
|
||||
'''Runs markdown documentation through the Jinja2 processor to inject data
|
||||
create loops, etc...'''
|
||||
|
||||
try:
|
||||
template = Template(documentation)
|
||||
self.documentation = template.render(**self.data)
|
||||
except jinja2.exceptions.TemplateError as ue:
|
||||
raise ApiError(code="template_error", message="Error processing template for task %s: %s" %
|
||||
(self.name, str(ue)), status_code=500)
|
||||
# TODO: Catch additional errors and report back.
|
||||
|
||||
class OptionSchema(ma.Schema):
|
||||
class Meta:
|
||||
|
|
|
@ -50,7 +50,6 @@ Takes two arguments:
|
|||
"the name of the docx template to use. The second "
|
||||
"argument is a code for the document, as "
|
||||
"set in the reference document %s. " % FileService.IRB_PRO_CATEGORIES_FILE)
|
||||
workflow_spec_model = self.find_spec_model_in_db(task.workflow)
|
||||
task_study_id = task.workflow.data[WorkflowProcessor.STUDY_ID_KEY]
|
||||
file_name = args[0]
|
||||
|
||||
|
@ -58,21 +57,7 @@ Takes two arguments:
|
|||
raise ApiError(code="invalid_argument",
|
||||
message="The given task does not match the given study.")
|
||||
|
||||
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))
|
||||
|
||||
|
||||
file_data_model = FileService.get_workflow_file_data(task.workflow, file_name)
|
||||
return self.make_template(BytesIO(file_data_model.data), task.data)
|
||||
|
||||
|
||||
|
@ -85,15 +70,4 @@ Takes two arguments:
|
|||
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
|
||||
|
|
|
@ -192,3 +192,40 @@ class FileService(object):
|
|||
if not file_model:
|
||||
raise ApiError("file_not_found", "There is no reference file with the name '%s'" % file_name)
|
||||
return FileService.get_file_data(file_model.id, file_model)
|
||||
|
||||
@staticmethod
|
||||
def get_workflow_file_data(workflow, file_name):
|
||||
"""Given a SPIFF Workflow Model, tracks down a file with the given name in the datbase and returns it's data"""
|
||||
workflow_spec_model = FileService.__find_spec_model_in_db(workflow)
|
||||
study_id = workflow.data[WorkflowProcessor.STUDY_ID_KEY]
|
||||
|
||||
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'"
|
||||
% (file_name, workflow_spec_model.id))
|
||||
|
||||
return file_data_model
|
||||
|
||||
@staticmethod
|
||||
def __find_spec_model_in_db(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 FileService.__find_spec_model_in_db(workflow.outer_workflow)
|
||||
|
||||
return workflow_model
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ from SpiffWorkflow.specs import WorkflowSpec
|
|||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.api_models import Task
|
||||
from crc.models.file import FileDataModel, FileModel, FileType
|
||||
from crc.models.workflow import WorkflowStatus, WorkflowModel
|
||||
from crc.scripts.script import Script
|
||||
|
@ -271,26 +270,7 @@ class WorkflowProcessor(object):
|
|||
spec.description = version
|
||||
return spec
|
||||
|
||||
@classmethod
|
||||
def test_spec(cls, spec_id):
|
||||
|
||||
spec = WorkflowProcessor.get_spec(spec_id)
|
||||
bpmn_workflow = BpmnWorkflow(spec, script_engine=cls._script_engine)
|
||||
bpmn_workflow.data[WorkflowProcessor.STUDY_ID_KEY] = 1
|
||||
bpmn_workflow.data[WorkflowProcessor.WORKFLOW_ID_KEY] = spec_id
|
||||
bpmn_workflow.data[WorkflowProcessor.VALIDATION_PROCESS_KEY] = True
|
||||
|
||||
while not bpmn_workflow.is_completed():
|
||||
try:
|
||||
bpmn_workflow.do_engine_steps()
|
||||
tasks = bpmn_workflow.get_tasks(SpiffTask.READY)
|
||||
for task in tasks:
|
||||
task_api = Task.from_spiff(task) # Assure we try to process the documenation, and raise those errors.
|
||||
WorkflowProcessor.populate_form_with_random_data(task)
|
||||
task.complete()
|
||||
except WorkflowException as we:
|
||||
raise ApiError.from_task_spec("workflow_execution_exception", str(we),
|
||||
we.sender)
|
||||
|
||||
@staticmethod
|
||||
def populate_form_with_random_data(task):
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
|
||||
from pandas import ExcelFile
|
||||
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.api_models import Task
|
||||
import jinja2
|
||||
from jinja2 import Template
|
||||
|
||||
from crc.services.file_service import FileService
|
||||
from crc.services.workflow_processor import WorkflowProcessor, CustomBpmnScriptEngine
|
||||
from SpiffWorkflow import Task as SpiffTask, WorkflowException
|
||||
|
||||
|
||||
class WorkflowService(object):
|
||||
"""Provides tools for processing workflows and tasks. This
|
||||
should at some point, be the only way to work with Workflows, and
|
||||
the workflow Processor should be hidden behind this service.
|
||||
This will help maintain a structure that avoids circular dependencies.
|
||||
But for now, this contains tools for converting spiff-workflow models into our
|
||||
own API models with additional information and capabilities."""
|
||||
|
||||
@classmethod
|
||||
def test_spec(cls, spec_id):
|
||||
"""Runs a spec through it's paces to see if it results in any errors. Not full proof, but a good
|
||||
sanity check."""
|
||||
|
||||
spec = WorkflowProcessor.get_spec(spec_id)
|
||||
bpmn_workflow = BpmnWorkflow(spec, script_engine=CustomBpmnScriptEngine())
|
||||
bpmn_workflow.data[WorkflowProcessor.STUDY_ID_KEY] = 1
|
||||
bpmn_workflow.data[WorkflowProcessor.WORKFLOW_ID_KEY] = spec_id
|
||||
bpmn_workflow.data[WorkflowProcessor.VALIDATION_PROCESS_KEY] = True
|
||||
|
||||
while not bpmn_workflow.is_completed():
|
||||
try:
|
||||
bpmn_workflow.do_engine_steps()
|
||||
tasks = bpmn_workflow.get_tasks(SpiffTask.READY)
|
||||
for task in tasks:
|
||||
task_api = WorkflowService.spiff_task_to_api_task(
|
||||
task) # Assure we try to process the documenation, and raise those errors.
|
||||
WorkflowProcessor.populate_form_with_random_data(task)
|
||||
task.complete()
|
||||
except WorkflowException as we:
|
||||
raise ApiError.from_task_spec("workflow_execution_exception", str(we),
|
||||
we.sender)
|
||||
|
||||
@staticmethod
|
||||
def spiff_task_to_api_task(spiff_task):
|
||||
documentation = spiff_task.task_spec.documentation if hasattr(spiff_task.task_spec, "documentation") else ""
|
||||
task = Task(spiff_task.id,
|
||||
spiff_task.task_spec.name,
|
||||
spiff_task.task_spec.description,
|
||||
spiff_task.task_spec.__class__.__name__,
|
||||
spiff_task.get_state_name(),
|
||||
None,
|
||||
documentation,
|
||||
spiff_task.data)
|
||||
|
||||
# Only process the form and documentation if this is something that is ready or completed.
|
||||
if not (spiff_task._is_predicted()):
|
||||
if hasattr(spiff_task.task_spec, "form"):
|
||||
task.form = spiff_task.task_spec.form
|
||||
for field in task.form.fields:
|
||||
WorkflowService._process_options(spiff_task, field)
|
||||
|
||||
if documentation != "" and documentation is not None:
|
||||
WorkflowService._process_documentation(task, documentation)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def _process_documentation(task, documentation):
|
||||
"""Runs the given documentation string through the Jinja2 processor to inject data
|
||||
create loops, etc..."""
|
||||
|
||||
try:
|
||||
template = Template(documentation)
|
||||
task.documentation = template.render(**task.data)
|
||||
except jinja2.exceptions.TemplateError as ue:
|
||||
raise ApiError(code="template_error", message="Error processing template for task %s: %s" %
|
||||
(task.name, str(ue)), status_code=500)
|
||||
# TODO: Catch additional errors and report back.
|
||||
|
||||
@staticmethod
|
||||
def _process_options(spiff_task, field):
|
||||
""" Checks to see if the options are provided in a separate lookup table associated with the
|
||||
workflow, and populates these if possible. """
|
||||
if field.has_property(Task.ENUM_OPTIONS_FILE_PROP):
|
||||
if not field.has_property(Task.EMUM_OPTIONS_VALUE_COL_PROP) or \
|
||||
not field.has_property(Task.EMUM_OPTIONS_LABEL_COL_PROP):
|
||||
raise ApiError.from_task("invalid_emum",
|
||||
"For emumerations based on an xls file, you must include 3 properties: %s, "
|
||||
"%s, and %s, you supplied %s" % (Task.ENUM_OPTIONS_FILE_PROP,
|
||||
Task.EMUM_OPTIONS_VALUE_COL_PROP,
|
||||
Task.EMUM_OPTIONS_LABEL_COL_PROP),
|
||||
task=spiff_task)
|
||||
|
||||
# Get the file data from the File Service
|
||||
file_name = field.get_property(Task.ENUM_OPTIONS_FILE_PROP)
|
||||
value_column = field.get_property(Task.EMUM_OPTIONS_VALUE_COL_PROP)
|
||||
label_column = field.get_property(Task.EMUM_OPTIONS_LABEL_COL_PROP)
|
||||
data_model = FileService.get_workflow_file_data(spiff_task.workflow, file_name)
|
||||
xls = ExcelFile(data_model.data)
|
||||
df = xls.parse(xls.sheet_names[0])
|
||||
if value_column not in df:
|
||||
raise ApiError("invalid_emum",
|
||||
"The file %s does not contain a column named % s" % (file_name, value_column))
|
||||
if label_column not in df:
|
||||
raise ApiError("invalid_emum",
|
||||
"The file %s does not contain a column named % s" % (file_name, label_column))
|
||||
|
||||
for index, row in df.iterrows():
|
||||
field.options.append({"id": row[value_column],
|
||||
"name": row[label_column]})
|
|
@ -14,13 +14,13 @@
|
|||
</camunda:formField>
|
||||
<camunda:formField id="FormField_BudgetDraft" label="Draft Budget" type="file">
|
||||
<camunda:properties>
|
||||
<camunda:property id="hide_expression" value="!model.FormField_isBudget" />
|
||||
<camunda:property id="hide_expression" value="!(model.FormField_isBudget) | FormField_isBudget == null" />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
<camunda:formField id="FormField_Budget Final" label="Final Budget" type="file">
|
||||
<camunda:properties>
|
||||
<camunda:property id="description" value="This is the budget that you will negotiate with your funding source." />
|
||||
<camunda:property id="hide_expression" value="!model.FormField_isBudget" />
|
||||
<camunda:property id="hide_expression" value="!(model.FormField_isBudget) | FormField_isBudget == null" />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<decisionTable id="decisionTable_1">
|
||||
<input id="input_1" label="Investigator's Brochure Form Upload Count">
|
||||
<inputExpression id="inputExpression_1" typeRef="integer">
|
||||
<text>Documents["DrugDevDoc_InvestBrochure"]["count"]</text>
|
||||
<text>Documents.DrugDevDoc_InvestBrochure.count</text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="output_1" label="Investigator's Brochure(s) Uploaded?" name="isInvestigatorsBrochure" typeRef="boolean" />
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<decisionTable id="decisionTable_1">
|
||||
<input id="input_1" label="IVRS-IWRS-IXRS Manual Count">
|
||||
<inputExpression id="inputExpression_1" typeRef="integer">
|
||||
<text>Documents["DrugDevDoc_IVRSIWRSIXRSMan"]["count"]</text>
|
||||
<text>Documents.DrugDevDoc_IVRSIWRSIXRSMan.count</text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="output_1" label="IVRS-IWRS-IXRS Manual Uploaded?" name="isIVRS-IWRS-IXRS" typeRef="boolean" />
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_65a4c07" name="DRD" namespace="http://camunda.org/schema/1.0/dmn">
|
||||
<decision id="Decision_Coordinator" name="Coordinator Status">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="200" y="140" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="decisionTable_1">
|
||||
<input id="input_1" label="Coordinator Status">
|
||||
<inputExpression id="inputExpression_1" typeRef="boolean">
|
||||
<text></text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="output_1" label="Coordinator Form Banner" name="ElementDoc_Coordinator" typeRef="string" />
|
||||
<rule id="DecisionRule_0sfkgkh">
|
||||
<inputEntry id="UnaryTests_160rjry">
|
||||
<text></text>
|
||||
</inputEntry>
|
||||
<outputEntry id="LiteralExpression_1lxmr3n">
|
||||
<text>"Placeholder"</text>
|
||||
</outputEntry>
|
||||
</rule>
|
||||
</decisionTable>
|
||||
</decision>
|
||||
</definitions>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_305b1db" name="DRD" namespace="http://camunda.org/schema/1.0/dmn">
|
||||
<decision id="Decision_DepartmentChair" name="Department Chair Status">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="200" y="150" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="decisionTable_1">
|
||||
<input id="input_1" label="Department Chair Status">
|
||||
<inputExpression id="inputExpression_1" typeRef="boolean">
|
||||
<text></text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="output_1" label="Department Chair Form Banner" name="ElementDoc_DepartmentChair" typeRef="string" />
|
||||
<rule id="DecisionRule_1by29jj">
|
||||
<inputEntry id="UnaryTests_1gr92h8">
|
||||
<text></text>
|
||||
</inputEntry>
|
||||
<outputEntry id="LiteralExpression_0xadafz">
|
||||
<text>"DC Placeholder"</text>
|
||||
</outputEntry>
|
||||
</rule>
|
||||
</decisionTable>
|
||||
</decision>
|
||||
</definitions>
|
|
@ -10,14 +10,7 @@
|
|||
<bpmn:script>StudyInfo investigators</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:userTask id="UserTask_EditPrimaryInvestigator" name="Edit Primary Investigator" camunda:formKey="PrimaryInvestigator">
|
||||
<bpmn:documentation>### From Protocol Builder
|
||||
{% for personnel in study.investigators|selectattr("INVESTIGATORTYPE", "equalto", "PI") %}
|
||||
#### {{ personnel.INVESTIGATORTYPEFULL }}
|
||||
{{ personnel.NETBADGEID }}
|
||||
{% else %}
|
||||
#### No Primary Investigator Entered in Protocol Builder
|
||||
The PI is needed for many required steps. Please enter this information in Protocol Builder as soon as possible.
|
||||
{% endfor %}</bpmn:documentation>
|
||||
<bpmn:documentation>{{ElementDoc_PrimaryInvestigator}}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="PI_Experience" label="Investigator's Experience" type="textarea">
|
||||
|
@ -44,7 +37,7 @@ The PI is needed for many required steps. Please enter this information in Prot
|
|||
<bpmn:sequenceFlow id="SequenceFlow_122pp0f" sourceRef="UserTask_EditPrimaryInvestigator" targetRef="Gateway_19lvya6" />
|
||||
<bpmn:sequenceFlow id="Flow_0g0o593" sourceRef="Gateway_13kno0x" targetRef="UserTask_EditPrimaryInvestigator" />
|
||||
<bpmn:parallelGateway id="Gateway_13kno0x">
|
||||
<bpmn:incoming>Flow_05aywbq</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_19i1d30</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0g0o593</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_1nudg96</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_18ix81l</bpmn:outgoing>
|
||||
|
@ -58,13 +51,6 @@ The PI is needed for many required steps. Please enter this information in Prot
|
|||
</bpmn:parallelGateway>
|
||||
<bpmn:sequenceFlow id="Flow_1nudg96" sourceRef="Gateway_13kno0x" targetRef="Activity_EditCoordinator" />
|
||||
<bpmn:userTask id="Activity_EditCoordinator" name="Edit Coordinator" camunda:formKey="Coordinator">
|
||||
<bpmn:documentation>### From Protocol Builder
|
||||
{% for personnel in study.investigators|selectattr("INVESTIGATORTYPE", "equalto", "SC_I") %}
|
||||
#### {{ personnel.INVESTIGATORTYPEFULL }}
|
||||
{{ personnel.NETBADGEID }}
|
||||
{% else %}
|
||||
#### No Primary Coordinator Entered in Protocol Builder
|
||||
{% endfor %}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="CoordinatorI_EditAccess" label="Should this Coordinator have Study Team editing access in the system?" type="boolean" defaultValue="true" />
|
||||
|
@ -77,13 +63,6 @@ The PI is needed for many required steps. Please enter this information in Prot
|
|||
<bpmn:sequenceFlow id="Flow_00lv37g" sourceRef="Activity_EditCoordinator" targetRef="Gateway_19lvya6" />
|
||||
<bpmn:sequenceFlow id="Flow_18ix81l" sourceRef="Gateway_13kno0x" targetRef="Activity_EditDepartmentChair" />
|
||||
<bpmn:userTask id="Activity_EditDepartmentChair" name="Edit Department Chair" camunda:formKey="DeptmentChair">
|
||||
<bpmn:documentation>### From Protocol Builder
|
||||
{% for personnel in study.investigators|selectattr("INVESTIGATORTYPE", "equalto", "DEPT_CH") %}
|
||||
#### {{ personnel.INVESTIGATORTYPEFULL }}
|
||||
{{ personnel.NETBADGEID }}
|
||||
{% else %}
|
||||
#### No Department Chair Entered in Protocol Builder
|
||||
{% endfor %}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="DepartmentChair_EditAccess" label="Should the Department Chair have Study Team editing access in the system?" type="boolean" defaultValue="false" />
|
||||
|
@ -94,74 +73,110 @@ The PI is needed for many required steps. Please enter this information in Prot
|
|||
<bpmn:outgoing>Flow_0y1jvdw</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_0y1jvdw" sourceRef="Activity_EditDepartmentChair" targetRef="Gateway_19lvya6" />
|
||||
<bpmn:sequenceFlow id="Flow_05aywbq" sourceRef="ScriptTask_LoadPersonnel" targetRef="Gateway_13kno0x" />
|
||||
<bpmn:sequenceFlow id="Flow_05aywbq" sourceRef="ScriptTask_LoadPersonnel" targetRef="Activity_PI-Satus" />
|
||||
<bpmn:sequenceFlow id="Flow_0kcrx5l" sourceRef="StartEvent_1" targetRef="ScriptTask_LoadPersonnel" />
|
||||
<bpmn:sequenceFlow id="Flow_12rh5aj" sourceRef="Activity_PI-Satus" targetRef="Activity_CoordinatorStatus" />
|
||||
<bpmn:businessRuleTask id="Activity_PI-Satus" name="Primary Investigator Status" camunda:decisionRef="Decision_PrimaryInvestigator">
|
||||
<bpmn:incoming>Flow_05aywbq</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_12rh5aj</bpmn:outgoing>
|
||||
</bpmn:businessRuleTask>
|
||||
<bpmn:sequenceFlow id="Flow_04nzqn8" sourceRef="Activity_CoordinatorStatus" targetRef="Activity_DepartmentChairStatus" />
|
||||
<bpmn:businessRuleTask id="Activity_CoordinatorStatus" name="Coordinator Status" camunda:decisionRef="Decision_Coordinator">
|
||||
<bpmn:incoming>Flow_12rh5aj</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_04nzqn8</bpmn:outgoing>
|
||||
</bpmn:businessRuleTask>
|
||||
<bpmn:sequenceFlow id="Flow_19i1d30" sourceRef="Activity_DepartmentChairStatus" targetRef="Gateway_13kno0x" />
|
||||
<bpmn:businessRuleTask id="Activity_DepartmentChairStatus" name="Department Chair Status" camunda:decisionRef="Decision_DepartmentChair">
|
||||
<bpmn:incoming>Flow_04nzqn8</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_19i1d30</bpmn:outgoing>
|
||||
</bpmn:businessRuleTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_01143nb">
|
||||
<bpmndi:BPMNEdge id="Flow_19i1d30_di" bpmnElement="Flow_19i1d30">
|
||||
<di:waypoint x="580" y="170" />
|
||||
<di:waypoint x="645" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_04nzqn8_di" bpmnElement="Flow_04nzqn8">
|
||||
<di:waypoint x="440" y="170" />
|
||||
<di:waypoint x="480" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_12rh5aj_di" bpmnElement="Flow_12rh5aj">
|
||||
<di:waypoint x="290" y="170" />
|
||||
<di:waypoint x="340" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0kcrx5l_di" bpmnElement="Flow_0kcrx5l">
|
||||
<di:waypoint x="168" y="170" />
|
||||
<di:waypoint x="270" y="170" />
|
||||
<di:waypoint x="-52" y="170" />
|
||||
<di:waypoint x="30" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_05aywbq_di" bpmnElement="Flow_05aywbq">
|
||||
<di:waypoint x="370" y="170" />
|
||||
<di:waypoint x="475" y="170" />
|
||||
<di:waypoint x="130" y="170" />
|
||||
<di:waypoint x="190" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0y1jvdw_di" bpmnElement="Flow_0y1jvdw">
|
||||
<di:waypoint x="710" y="300" />
|
||||
<di:waypoint x="810" y="300" />
|
||||
<di:waypoint x="810" y="195" />
|
||||
<di:waypoint x="880" y="300" />
|
||||
<di:waypoint x="980" y="300" />
|
||||
<di:waypoint x="980" y="195" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_18ix81l_di" bpmnElement="Flow_18ix81l">
|
||||
<di:waypoint x="500" y="195" />
|
||||
<di:waypoint x="500" y="300" />
|
||||
<di:waypoint x="610" y="300" />
|
||||
<di:waypoint x="670" y="195" />
|
||||
<di:waypoint x="670" y="300" />
|
||||
<di:waypoint x="780" y="300" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_00lv37g_di" bpmnElement="Flow_00lv37g">
|
||||
<di:waypoint x="710" y="170" />
|
||||
<di:waypoint x="785" y="170" />
|
||||
<di:waypoint x="880" y="170" />
|
||||
<di:waypoint x="955" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1nudg96_di" bpmnElement="Flow_1nudg96">
|
||||
<di:waypoint x="525" y="170" />
|
||||
<di:waypoint x="610" y="170" />
|
||||
<di:waypoint x="695" y="170" />
|
||||
<di:waypoint x="780" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_14469u8_di" bpmnElement="Flow_14469u8">
|
||||
<di:waypoint x="835" y="170" />
|
||||
<di:waypoint x="932" y="170" />
|
||||
<di:waypoint x="1005" y="170" />
|
||||
<di:waypoint x="1102" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0g0o593_di" bpmnElement="Flow_0g0o593">
|
||||
<di:waypoint x="500" y="145" />
|
||||
<di:waypoint x="500" y="30" />
|
||||
<di:waypoint x="610" y="30" />
|
||||
<di:waypoint x="670" y="145" />
|
||||
<di:waypoint x="670" y="30" />
|
||||
<di:waypoint x="780" y="30" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_122pp0f_di" bpmnElement="SequenceFlow_122pp0f">
|
||||
<di:waypoint x="710" y="30" />
|
||||
<di:waypoint x="810" y="30" />
|
||||
<di:waypoint x="810" y="145" />
|
||||
<di:waypoint x="880" y="30" />
|
||||
<di:waypoint x="980" y="30" />
|
||||
<di:waypoint x="980" y="145" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="132" y="152" width="36" height="36" />
|
||||
<dc:Bounds x="-88" y="152" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ScriptTask_0h49cmf_di" bpmnElement="ScriptTask_LoadPersonnel">
|
||||
<dc:Bounds x="270" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_1qor16n_di" bpmnElement="EndEvent_1qor16n">
|
||||
<dc:Bounds x="932" y="152" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1lvmp6i_di" bpmnElement="Gateway_13kno0x">
|
||||
<dc:Bounds x="475" y="145" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0k9iptd_di" bpmnElement="Gateway_19lvya6">
|
||||
<dc:Bounds x="785" y="145" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0d622qi_di" bpmnElement="Activity_EditDepartmentChair">
|
||||
<dc:Bounds x="610" y="260" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0fl86y3_di" bpmnElement="Activity_EditCoordinator">
|
||||
<dc:Bounds x="610" y="130" width="100" height="80" />
|
||||
<dc:Bounds x="30" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="UserTask_0hpnrm9_di" bpmnElement="UserTask_EditPrimaryInvestigator">
|
||||
<dc:Bounds x="610" y="-10" width="100" height="80" />
|
||||
<dc:Bounds x="780" y="-10" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_1qor16n_di" bpmnElement="EndEvent_1qor16n">
|
||||
<dc:Bounds x="1102" y="152" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1lvmp6i_di" bpmnElement="Gateway_13kno0x">
|
||||
<dc:Bounds x="645" y="145" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0k9iptd_di" bpmnElement="Gateway_19lvya6">
|
||||
<dc:Bounds x="955" y="145" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0fl86y3_di" bpmnElement="Activity_EditCoordinator">
|
||||
<dc:Bounds x="780" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0d622qi_di" bpmnElement="Activity_EditDepartmentChair">
|
||||
<dc:Bounds x="780" y="260" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1yggezg_di" bpmnElement="Activity_PI-Satus">
|
||||
<dc:Bounds x="190" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_10gqxcp_di" bpmnElement="Activity_CoordinatorStatus">
|
||||
<dc:Bounds x="340" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1g0yuo4_di" bpmnElement="Activity_DepartmentChairStatus">
|
||||
<dc:Bounds x="480" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_cc48f13" name="DRD" namespace="http://camunda.org/schema/1.0/dmn">
|
||||
<decision id="Decision_PrimaryInvestigator" name="Primary Investigator Status">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="200" y="160" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="decisionTable_1">
|
||||
<input id="input_1" label="Primary Investogator Status">
|
||||
<inputExpression id="inputExpression_1" typeRef="boolean" expressionLanguage="feel">
|
||||
<text>list contains( for i in [study.investigators[0].INVESTIGATORTYPE, study.investigators[1].INVESTIGATORTYPE, study.investigators[2].INVESTIGATORTYPE] return i, "PI")</text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="output_1" label="Primary Investigator Form Banner" name="ElementDoc_PrimaryInvestigator" typeRef="string" />
|
||||
<rule id="DecisionRule_19gl4re">
|
||||
<inputEntry id="UnaryTests_14311bk">
|
||||
<text>true</text>
|
||||
</inputEntry>
|
||||
<outputEntry id="LiteralExpression_1d3eboq">
|
||||
<text>"Placeholder - True"</text>
|
||||
</outputEntry>
|
||||
</rule>
|
||||
<rule id="DecisionRule_1crmfau">
|
||||
<inputEntry id="UnaryTests_110jbd6">
|
||||
<text>false</text>
|
||||
</inputEntry>
|
||||
<outputEntry id="LiteralExpression_107i08h">
|
||||
<text>"Placeholder - False"</text>
|
||||
</outputEntry>
|
||||
</rule>
|
||||
</decisionTable>
|
||||
</decision>
|
||||
</definitions>
|
|
@ -2,12 +2,12 @@
|
|||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.4.1">
|
||||
<decision id="data_security_plan" name="Data Security Plan">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="190" y="80" width="180" height="80" />
|
||||
<biodi:bounds x="180" y="80" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="DecisionTable_1mjqwlv">
|
||||
<input id="InputClause_18pwfqu" label="Data Plan Required in PB?">
|
||||
<input id="InputClause_18pwfqu" label="Required Doc Keys">
|
||||
<inputExpression id="LiteralExpression_1y84stb" typeRef="boolean" expressionLanguage="feel">
|
||||
<text>Documents['Study_DataSecurityPlan']['required']</text>
|
||||
<text>Documents['UVACompl_PRCAppr']['required']</text>
|
||||
</inputExpression>
|
||||
</input>
|
||||
<output id="OutputClause_05y0j7c" label="data_security_plan" name="data_security_plan" typeRef="string" />
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.4.1">
|
||||
<decision id="enter_core_info" name="Enter Core Info">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="170" y="60" width="180" height="80" />
|
||||
<biodi:bounds x="160" y="60" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="decisionTable_1">
|
||||
<input id="InputClause_1ki80j6" label="required doc ids">
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.4.1">
|
||||
<decision id="sponsor_funding_source" name="Sponsor Funding Source">
|
||||
<extensionElements>
|
||||
<biodi:bounds x="190" y="70" width="180" height="80" />
|
||||
<biodi:bounds x="190" y="80" width="180" height="80" />
|
||||
</extensionElements>
|
||||
<decisionTable id="DecisionTable_00zdxg0">
|
||||
<input id="InputClause_02n3ccs" label="Sponsor Document Required in PB?">
|
||||
<input id="InputClause_02n3ccs" label="CoCApplication Required?">
|
||||
<inputExpression id="LiteralExpression_1ju4o1o" typeRef="boolean" expressionLanguage="feel">
|
||||
<text>Documents['AD_LabManual']['required']</text>
|
||||
</inputExpression>
|
||||
|
|
|
@ -60,13 +60,52 @@
|
|||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0jhpidf">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="192" y="421" width="36" height="36" />
|
||||
<bpmndi:BPMNShape id="TextAnnotation_0ydnva4_di" bpmnElement="TextAnnotation_0ydnva4">
|
||||
<dc:Bounds x="155" y="210" width="110" height="82" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0x9580l_di" bpmnElement="Flow_0x9580l">
|
||||
<di:waypoint x="740" y="550" />
|
||||
<di:waypoint x="800" y="550" />
|
||||
<di:waypoint x="800" y="464" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1txrak2_di" bpmnElement="Flow_1txrak2">
|
||||
<di:waypoint x="740" y="439" />
|
||||
<di:waypoint x="775" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1nimppb_di" bpmnElement="Flow_1nimppb">
|
||||
<di:waypoint x="608" y="439" />
|
||||
<di:waypoint x="640" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_18pl92p_di" bpmnElement="Flow_18pl92p">
|
||||
<di:waypoint x="583" y="464" />
|
||||
<di:waypoint x="583" y="550" />
|
||||
<di:waypoint x="640" y="550" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_17ct47v_di" bpmnElement="SequenceFlow_17ct47v">
|
||||
<di:waypoint x="400" y="439" />
|
||||
<di:waypoint x="558" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1m8285h_di" bpmnElement="Flow_1m8285h">
|
||||
<di:waypoint x="583" y="414" />
|
||||
<di:waypoint x="583" y="330" />
|
||||
<di:waypoint x="640" y="330" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0pwtiqm_di" bpmnElement="Flow_0pwtiqm">
|
||||
<di:waypoint x="825" y="439" />
|
||||
<di:waypoint x="862" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1sggkit_di" bpmnElement="Flow_1sggkit">
|
||||
<di:waypoint x="740" y="330" />
|
||||
<di:waypoint x="800" y="330" />
|
||||
<di:waypoint x="800" y="414" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_1ees8ka_di" bpmnElement="SequenceFlow_1ees8ka">
|
||||
<di:waypoint x="228" y="439" />
|
||||
<di:waypoint x="300" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="192" y="421" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_135x8jg_di" bpmnElement="Event_135x8jg">
|
||||
<dc:Bounds x="862" y="421" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
|
@ -76,74 +115,35 @@
|
|||
<bpmndi:BPMNShape id="Activity_1yqy50i_di" bpmnElement="Activity_1yqy50i">
|
||||
<dc:Bounds x="640" y="290" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1kk6x70_di" bpmnElement="Gateway_12tpgcy">
|
||||
<dc:Bounds x="775" y="414" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1m22g4p_di" bpmnElement="Gateway_1nta7st">
|
||||
<dc:Bounds x="558" y="414" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1k5eeun_di" bpmnElement="Activity_1k5eeun">
|
||||
<dc:Bounds x="640" y="399" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_16cm213_di" bpmnElement="Activity_16cm213">
|
||||
<dc:Bounds x="640" y="510" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1pv8ygy_di" bpmnElement="TextAnnotation_1pv8ygy">
|
||||
<dc:Bounds x="300" y="247" width="100" height="68" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1f52jro_di" bpmnElement="TextAnnotation_1f52jro">
|
||||
<dc:Bounds x="461" y="80" width="243" height="124" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Association_0w69z3w_di" bpmnElement="Association_0w69z3w">
|
||||
<di:waypoint x="350" y="399" />
|
||||
<di:waypoint x="350" y="315" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_0ydnva4_di" bpmnElement="TextAnnotation_0ydnva4">
|
||||
<dc:Bounds x="155" y="220" width="110" height="82" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Association_0a41ixa_di" bpmnElement="Association_0a41ixa">
|
||||
<di:waypoint x="210" y="421" />
|
||||
<di:waypoint x="210" y="302" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1f52jro_di" bpmnElement="TextAnnotation_1f52jro">
|
||||
<dc:Bounds x="461" y="80" width="243" height="124" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1sggkit_di" bpmnElement="Flow_1sggkit">
|
||||
<di:waypoint x="740" y="330" />
|
||||
<di:waypoint x="800" y="330" />
|
||||
<di:waypoint x="800" y="414" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Gateway_1kk6x70_di" bpmnElement="Gateway_12tpgcy">
|
||||
<dc:Bounds x="775" y="414" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0pwtiqm_di" bpmnElement="Flow_0pwtiqm">
|
||||
<di:waypoint x="825" y="439" />
|
||||
<di:waypoint x="862" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Gateway_1m22g4p_di" bpmnElement="Gateway_1nta7st">
|
||||
<dc:Bounds x="558" y="414" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Association_1mzqzwj_di" bpmnElement="Association_1mzqzwj">
|
||||
<di:waypoint x="583" y="414" />
|
||||
<di:waypoint x="583" y="204" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1m8285h_di" bpmnElement="Flow_1m8285h">
|
||||
<di:waypoint x="583" y="414" />
|
||||
<di:waypoint x="583" y="330" />
|
||||
<di:waypoint x="640" y="330" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_17ct47v_di" bpmnElement="SequenceFlow_17ct47v">
|
||||
<di:waypoint x="400" y="439" />
|
||||
<di:waypoint x="558" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_18pl92p_di" bpmnElement="Flow_18pl92p">
|
||||
<di:waypoint x="583" y="464" />
|
||||
<di:waypoint x="583" y="550" />
|
||||
<di:waypoint x="640" y="550" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1nimppb_di" bpmnElement="Flow_1nimppb">
|
||||
<di:waypoint x="608" y="439" />
|
||||
<di:waypoint x="640" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Activity_1k5eeun_di" bpmnElement="Activity_1k5eeun">
|
||||
<dc:Bounds x="640" y="399" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1txrak2_di" bpmnElement="Flow_1txrak2">
|
||||
<di:waypoint x="740" y="439" />
|
||||
<di:waypoint x="775" y="439" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Activity_16cm213_di" bpmnElement="Activity_16cm213">
|
||||
<dc:Bounds x="640" y="510" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0x9580l_di" bpmnElement="Flow_0x9580l">
|
||||
<di:waypoint x="740" y="550" />
|
||||
<di:waypoint x="800" y="550" />
|
||||
<di:waypoint x="800" y="464" />
|
||||
<bpmndi:BPMNEdge id="Association_0a41ixa_di" bpmnElement="Association_0a41ixa">
|
||||
<di:waypoint x="210" y="421" />
|
||||
<di:waypoint x="210" y="292" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
|
|
|
@ -65,13 +65,6 @@ class ExampleDataLoader:
|
|||
]
|
||||
db.session.add_all(categories)
|
||||
db.session.commit()
|
||||
self.create_spec(id="top_level_workflow",
|
||||
name="top_level_workflow",
|
||||
display_name="Top Level Workflow",
|
||||
description="Determines the status of other workflows in a study",
|
||||
category_id=None,
|
||||
master_spec=True
|
||||
)
|
||||
|
||||
# Pass IRB Review
|
||||
self.create_spec(id="irb_api_personnel",
|
||||
|
@ -163,6 +156,14 @@ class ExampleDataLoader:
|
|||
category_id=5,
|
||||
display_order=0)
|
||||
|
||||
# Top Level (Master Status) Workflow
|
||||
self.create_spec(id="top_level_workflow",
|
||||
name="top_level_workflow",
|
||||
display_name="Top Level Workflow",
|
||||
description="Determines the status of other workflows in a study",
|
||||
category_id=None,
|
||||
master_spec=True)
|
||||
|
||||
|
||||
def create_spec(self, id, name, display_name="", description="", filepath=None, master_spec=False, category_id=None, display_order=None):
|
||||
"""Assumes that a directory exists in static/bpmn with the same name as the given id.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
,dan,lilmaker,15.04.2020 11:05,file:///home/dan/.config/libreoffice/4;
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1v1rp1q" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.4.1">
|
||||
<bpmn:process id="Process_1vu5nxl" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>SequenceFlow_0lvudp8</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0lvudp8" sourceRef="StartEvent_1" targetRef="Task_14svgcu" />
|
||||
<bpmn:endEvent id="EndEvent_0q4qzl9">
|
||||
<bpmn:incoming>SequenceFlow_02vev7n</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_02vev7n" sourceRef="Task_14svgcu" targetRef="EndEvent_0q4qzl9" />
|
||||
<bpmn:userTask id="Task_14svgcu" name="Enum Lookup Form" camunda:formKey="EnumForm">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="AllTheNames" label="Select a value" type="enum">
|
||||
<camunda:properties>
|
||||
<camunda:property id="enum.options.file" value="customer_list.xls" />
|
||||
<camunda:property id="enum.options.value.column" value="CUSTOMER_NUMBER" />
|
||||
<camunda:property id="enum.options.label.column" value="CUSTOMER_NAME" />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>SequenceFlow_0lvudp8</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_02vev7n</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1vu5nxl">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0lvudp8_di" bpmnElement="SequenceFlow_0lvudp8">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="EndEvent_0q4qzl9_di" bpmnElement="EndEvent_0q4qzl9">
|
||||
<dc:Bounds x="432" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_02vev7n_di" bpmnElement="SequenceFlow_02vev7n">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="432" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="UserTask_18ly1yq_di" bpmnElement="Task_14svgcu">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -14,8 +14,9 @@ class TestFilesApi(BaseTest):
|
|||
|
||||
def test_list_files_for_workflow_spec(self):
|
||||
self.load_example_data()
|
||||
spec = session.query(WorkflowSpecModel).first()
|
||||
rv = self.app.get('/v1.0/file?workflow_spec_id=%s' % spec.id,
|
||||
spec_id = 'core_info'
|
||||
spec = session.query(WorkflowSpecModel).filter_by(id=spec_id).first()
|
||||
rv = self.app.get('/v1.0/file?workflow_spec_id=%s' % spec_id,
|
||||
follow_redirects=True,
|
||||
content_type="application/json", headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
|
|
|
@ -41,9 +41,14 @@ class TestStudyApi(BaseTest):
|
|||
study = session.query(StudyModel).first()
|
||||
self.assertIsNotNone(study)
|
||||
|
||||
def test_get_study(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
def test_get_study(self, mock_docs):
|
||||
"""Generic test, but pretty detailed, in that the study should return a categorized list of workflows
|
||||
This starts with out loading the example data, to show that all the bases are covered from ground 0."""
|
||||
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
|
||||
new_study = self.add_test_study()
|
||||
new_study = session.query(StudyModel).filter_by(id=new_study["id"]).first()
|
||||
# Add a category
|
||||
|
@ -109,9 +114,10 @@ class TestStudyApi(BaseTest):
|
|||
self.assertEqual(study.title, json_data['title'])
|
||||
self.assertEqual(study.protocol_builder_status.name, json_data['protocol_builder_status'])
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_studies') # mock_studies
|
||||
def test_get_all_studies(self, mock_studies, mock_details):
|
||||
def test_get_all_studies(self, mock_studies, mock_details, mock_docs):
|
||||
self.load_example_data()
|
||||
s = StudyModel(
|
||||
id=54321, # This matches one of the ids from the study_details_json data.
|
||||
|
@ -128,6 +134,8 @@ class TestStudyApi(BaseTest):
|
|||
mock_studies.return_value = ProtocolBuilderStudySchema(many=True).loads(studies_response)
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
|
||||
# Make the api call to get all studies
|
||||
api_response = self.app.get('/v1.0/study', headers=self.logged_in_headers(), content_type="application/json")
|
||||
|
@ -155,7 +163,12 @@ class TestStudyApi(BaseTest):
|
|||
test_study = session.query(StudyModel).filter_by(id=54321).first()
|
||||
self.assertFalse(test_study.inactive)
|
||||
|
||||
def test_get_single_study(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
def test_get_single_study(self, mock_docs):
|
||||
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
|
||||
self.load_example_data()
|
||||
study = session.query(StudyModel).first()
|
||||
rv = self.app.get('/v1.0/study/%i' % study.id,
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from crc import db
|
||||
from crc.models.protocol_builder import ProtocolBuilderStatus
|
||||
from crc.models.study import StudyModel
|
||||
|
@ -13,10 +16,13 @@ from tests.base_test import BaseTest
|
|||
class TestStudyService(BaseTest):
|
||||
"""Largely tested via the test_study_api, and time is tight, but adding new tests here."""
|
||||
|
||||
|
||||
def test_total_tasks_updated(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
def test_total_tasks_updated(self, mock_docs):
|
||||
"""Assure that as a users progress is available when getting a list of studies for that user."""
|
||||
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
|
||||
# Assure some basic models are in place, This is a damn mess. Our database models need an overhaul to make
|
||||
# this easier - better relationship modeling is now critical.
|
||||
self.load_test_spec("top_level_workflow", master_spec=True)
|
||||
|
|
|
@ -2,18 +2,15 @@ import json
|
|||
import os
|
||||
|
||||
from crc import session, app
|
||||
from crc.models.api_models import WorkflowApiSchema, Task
|
||||
from crc.models.api_models import WorkflowApiSchema
|
||||
from crc.models.file import FileModelSchema
|
||||
from crc.models.stats import WorkflowStatsModel, TaskEventModel
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.workflow import WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.models.workflow import WorkflowStatus
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestTasksApi(BaseTest):
|
||||
|
||||
|
||||
def get_workflow_api(self, workflow, soft_reset=False, hard_reset=False):
|
||||
rv = self.app.get('/v1.0/workflow/%i?soft_reset=%s&hard_reset=%s' %
|
||||
(workflow.id, str(soft_reset), str(hard_reset)),
|
||||
|
@ -158,47 +155,6 @@ class TestTasksApi(BaseTest):
|
|||
files = FileModelSchema(many=True).load(json_data, session=session)
|
||||
self.assertTrue(len(files) == 1)
|
||||
|
||||
def test_documentation_processing_handles_replacements(self):
|
||||
|
||||
docs = "Some simple docs"
|
||||
task = Task(1, "bill", "bill", "", "started", {}, docs, {})
|
||||
task.process_documentation(docs)
|
||||
self.assertEqual(docs, task.documentation)
|
||||
|
||||
task.data = {"replace_me": "new_thing"}
|
||||
task.process_documentation("{{replace_me}}")
|
||||
self.assertEqual("new_thing", task.documentation)
|
||||
|
||||
documentation = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has {{replace_me}}
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
expected = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has new_thing
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
task.process_documentation(documentation)
|
||||
self.assertEqual(expected, task.documentation)
|
||||
|
||||
def test_documentation_processing_handles_conditionals(self):
|
||||
|
||||
docs = "This test {% if works == 'yes' %}works{% endif %}"
|
||||
task = Task(1, "bill", "bill", "", "started", {}, docs, {})
|
||||
task.process_documentation(docs)
|
||||
self.assertEqual("This test ", task.documentation)
|
||||
|
||||
task.data = {"works": 'yes'}
|
||||
task.process_documentation(docs)
|
||||
self.assertEqual("This test works", task.documentation)
|
||||
|
||||
def test_get_documentation_populated_in_end(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('random_fact')
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
from crc import session, app
|
||||
from crc.models.api_models import WorkflowApiSchema, Task
|
||||
from crc.models.file import FileModelSchema
|
||||
from crc.models.stats import WorkflowStatsModel, TaskEventModel
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.workflow import WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestWorkflowService(BaseTest):
|
||||
|
||||
def test_documentation_processing_handles_replacements(self):
|
||||
|
||||
docs = "Some simple docs"
|
||||
task = Task(1, "bill", "bill", "", "started", {}, docs, {})
|
||||
WorkflowService._process_documentation(task, docs)
|
||||
self.assertEqual(docs, task.documentation)
|
||||
|
||||
task.data = {"replace_me": "new_thing"}
|
||||
WorkflowService._process_documentation(task, "{{replace_me}}")
|
||||
self.assertEqual("new_thing", task.documentation)
|
||||
|
||||
documentation = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has {{replace_me}}
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
expected = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has new_thing
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
WorkflowService._process_documentation(task,(documentation))
|
||||
self.assertEqual(expected, task.documentation)
|
||||
|
||||
def test_documentation_processing_handles_conditionals(self):
|
||||
|
||||
docs = "This test {% if works == 'yes' %}works{% endif %}"
|
||||
task = Task(1, "bill", "bill", "", "started", {}, docs, {})
|
||||
WorkflowService._process_documentation(task, docs)
|
||||
self.assertEqual("This test ", task.documentation)
|
||||
|
||||
task.data = {"works": 'yes'}
|
||||
WorkflowService._process_documentation(task, docs)
|
||||
self.assertEqual("This test works", task.documentation)
|
||||
|
||||
def test_enum_options_from_file(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('enum_options_from_file')
|
||||
processor = WorkflowProcessor(workflow)
|
||||
processor.do_engine_steps()
|
||||
task = processor.next_task()
|
||||
WorkflowService._process_options(task, task.task_spec.form.fields[0])
|
||||
options = task.task_spec.form.fields[0].options
|
||||
self.assertEquals(19, len(options))
|
||||
self.assertEquals(1000, options[0]['id'])
|
||||
self.assertEquals("UVA - INTERNAL - GM USE ONLY", options[0]['name'])
|
Loading…
Reference in New Issue