Loads of bug fixes.

Modifed the request_approval to take a list of arguments, which works better for us... today.
UpdateStudy correctly handles validation.
WorkflowService correctly populates random values from lookup tables.
And several fixes down in Spiffworkflow, including a big bug where only the last item in a decision table made it through.
This commit is contained in:
Dan Funk 2020-05-26 20:06:50 -04:00
parent e9645fa3fd
commit ccbf374b40
5 changed files with 70 additions and 37 deletions

14
Pipfile.lock generated
View File

@ -296,10 +296,10 @@
}, },
"flask-sqlalchemy": { "flask-sqlalchemy": {
"hashes": [ "hashes": [
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", "sha256:0b656fbf87c5f24109d859bafa791d29751fabbda2302b606881ae5485b557a5",
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" "sha256:fcfe6df52cd2ed8a63008ca36b86a51fa7a4b70cef1c39e5625f722fca32308e"
], ],
"version": "==2.4.1" "version": "==2.4.3"
}, },
"future": { "future": {
"hashes": [ "hashes": [
@ -727,11 +727,11 @@
}, },
"sphinx": { "sphinx": {
"hashes": [ "hashes": [
"sha256:62edfd92d955b868d6c124c0942eba966d54b5f3dcb4ded39e65f74abac3f572", "sha256:779a519adbd3a70fc7c468af08c5e74829868b0a5b34587b33340e010291856c",
"sha256:f5505d74cf9592f3b997380f9bdb2d2d0320ed74dd69691e3ee0644b956b8d83" "sha256:ea64df287958ee5aac46be7ac2b7277305b0381d213728c3a49d8bb9b8415807"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.0.3" "version": "==3.0.4"
}, },
"sphinxcontrib-applehelp": { "sphinxcontrib-applehelp": {
"hashes": [ "hashes": [
@ -778,7 +778,7 @@
"spiffworkflow": { "spiffworkflow": {
"editable": true, "editable": true,
"git": "https://github.com/sartography/SpiffWorkflow.git", "git": "https://github.com/sartography/SpiffWorkflow.git",
"ref": "cb098ee6d55b85bf7795997f4ad5f78c27d15381" "ref": "c8d87826d496af825a184bdc3f0a751e603cfe44"
}, },
"sqlalchemy": { "sqlalchemy": {
"hashes": [ "hashes": [

View File

@ -10,11 +10,11 @@ class RequestApproval(Script):
def get_description(self): def get_description(self):
return """ return """
Creates an approval request on this workflow, by the given approver_uid(s)," Creates an approval request on this workflow, by the given approver_uid(s),"
Takes one argument, which should point to data located in current task. Takes multiple arguments, which should point to data located in current task
or be quoted strings.
Example: Example:
RequestApproval approver1 "dhf8r"
RequestApproval required_approvals.uids
""" """
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
@ -29,17 +29,18 @@ RequestApproval required_approvals.uids
ApprovalService.add_approval(study_id, workflow_id, id) ApprovalService.add_approval(study_id, workflow_id, id)
def get_uids(self, task, args): def get_uids(self, task, args):
if len(args) != 1: if len(args) < 1:
raise ApiError(code="missing_argument", raise ApiError(code="missing_argument",
message="The RequestApproval script requires 1 argument. The " message="The RequestApproval script requires at least one argument. The "
"the name of the variable in the task data that contains user" "the name of the variable in the task data that contains user"
"ids to process.") "id to process. Multiple arguments are accepted.")
uids = []
uids = task.workflow.script_engine.evaluate_expression(task, args[0]) for arg in args:
id = task.workflow.script_engine.evaluate_expression(task, arg)
if not isinstance(uids, str) and not isinstance(uids, list): uids.append(id)
raise ApiError(code="invalid_argument", if not isinstance(id, str):
message="The RequestApproval script requires 1 argument. The " raise ApiError(code="invalid_argument",
message="The RequestApproval script requires 1 argument. The "
"the name of the variable in the task data that contains user" "the name of the variable in the task data that contains user"
"ids to process. This must point to an array or a string, but " "ids to process. This must point to an array or a string, but "
"it currently points to a %s " % uids.__class__.__name__) "it currently points to a %s " % uids.__class__.__name__)

View File

@ -6,6 +6,12 @@ from crc.models.study import StudyModel
from crc.scripts.script import Script from crc.scripts.script import Script
class mock_study:
def __init__(self):
self.title = ""
self.principle_investigator_id = ""
class UpdateStudy(Script): class UpdateStudy(Script):
argument_error_message = "You must supply at least one argument to the " \ argument_error_message = "You must supply at least one argument to the " \
@ -21,11 +27,15 @@ Example:
UpdateStudy title:PIComputingID.label pi:PIComputingID.value UpdateStudy title:PIComputingID.label pi:PIComputingID.value
""" """
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.do_task(task, study_id, workflow_id, *args, **kwargs) study = mock_study
self.__update_study(task, study, *args)
def do_task(self, task, study_id, workflow_id, *args, **kwargs): def do_task(self, task, study_id, workflow_id, *args, **kwargs):
study = db.session.query(StudyModel).filter(StudyModel.id == study_id).first() study = db.session.query(StudyModel).filter(StudyModel.id == study_id).first()
self.__update_study(task, study, *args)
db.session.add(study)
def __update_study(self, task, study, *args):
if len(args) < 1: if len(args) < 1:
raise ApiError.from_task("missing_argument", self.argument_error_message, raise ApiError.from_task("missing_argument", self.argument_error_message,
task=task) task=task)
@ -46,4 +56,3 @@ UpdateStudy title:PIComputingID.label pi:PIComputingID.value
else: else:
raise ApiError.from_task("invalid_argument", self.argument_error_message, raise ApiError.from_task("invalid_argument", self.argument_error_message,
task=task) task=task)
db.session.add(study)

View File

@ -61,7 +61,6 @@ class WorkflowService(object):
raise ApiError.from_task_spec("workflow_execution_exception", str(we), raise ApiError.from_task_spec("workflow_execution_exception", str(we),
we.sender) we.sender)
@staticmethod @staticmethod
def populate_form_with_random_data(task, task_api): def populate_form_with_random_data(task, task_api):
"""populates a task with random data - useful for testing a spec.""" """populates a task with random data - useful for testing a spec."""
@ -72,22 +71,42 @@ class WorkflowService(object):
for field in task_api.form.fields: for field in task_api.form.fields:
if field.type == "enum": if field.type == "enum":
if len(field.options) > 0: if len(field.options) > 0:
form_data[field.id] = random.choice(field.options) random_choice = random.choice(field.options)
if isinstance(random_choice, dict):
form_data[field.id] = random.choice(field.options)['id']
else:
# fixme: why it is sometimes an EnumFormFieldOption, and other times not?
form_data[field.id] = random_choice.id ## Assume it is an EnumFormFieldOption
else: else:
raise ApiError.from_task("invalid_enum", "You specified an enumeration field (%s)," raise ApiError.from_task("invalid_enum", "You specified an enumeration field (%s),"
" with no options" % field.id, " with no options" % field.id,
task) task)
if field.type == "autocomplete": elif field.type == "autocomplete":
lookup_model = LookupService.get_lookup_table(task, field) lookup_model = LookupService.get_lookup_table(task, field)
if not lookup_model: if field.has_property(Task.PROP_LDAP_LOOKUP):
form_data[field.id] = {
"label": "dhf8r",
"value": "Dan Funk",
"data": {
"uid": "dhf8r",
"display_name": "Dan Funk",
"given_name": "Dan",
"email_address": "dhf8r@virginia.edu",
"department": "Depertment of Psychocosmographictology",
"affiliation": "Rousabout",
"sponsor_type": "Staff"
}
}
elif lookup_model:
data = db.session.query(LookupDataModel).filter(
LookupDataModel.lookup_file_model == lookup_model).limit(10).all()
options = []
for d in data:
options.append({"id": d.value, "name": d.label})
form_data[field.id] = random.choice(options)
else:
raise ApiError.from_task("invalid_autocomplete", "The settings for this auto complete field " raise ApiError.from_task("invalid_autocomplete", "The settings for this auto complete field "
"(%s) are incorrect: " % field.id) "are incorrect: %s " % field.id, task)
data = db.session.query(LookupDataModel).filter(LookupDataModel.lookup_file_model == lookup_model).limit(10).all()
options = []
for d in data:
options.append({"id": d.value, "name": d.label})
form_data[field.id] = random.choice(options)
elif field.type == "long": elif field.type == "long":
form_data[field.id] = random.randint(1, 1000) form_data[field.id] = random.randint(1, 1000)
elif field.type == 'boolean': elif field.type == 'boolean':
@ -102,6 +121,10 @@ class WorkflowService(object):
task.data = {} task.data = {}
task.data.update(form_data) task.data.update(form_data)
def __get_options(self):
pass
@staticmethod @staticmethod
def _random_string(string_length=10): def _random_string(string_length=10):
"""Generate a random string of fixed length """ """Generate a random string of fixed length """

View File

@ -16,10 +16,10 @@ class TestRequestApprovalScript(BaseTest):
workflow = self.create_workflow('empty_workflow') workflow = self.create_workflow('empty_workflow')
processor = WorkflowProcessor(workflow) processor = WorkflowProcessor(workflow)
task = processor.next_task() task = processor.next_task()
task.data = {"study": {"approvals": ['dhf8r', 'lb3dp']}} task.data = {"study": {"approval1": "dhf8r", 'approval2':'lb3dp'}}
script = RequestApproval() script = RequestApproval()
script.do_task(task, workflow.study_id, workflow.id, "study.approvals") script.do_task(task, workflow.study_id, workflow.id, "study.approval1", "study.approval2")
self.assertEquals(2, db.session.query(ApprovalModel).count()) self.assertEquals(2, db.session.query(ApprovalModel).count())
def test_do_task_with_incorrect_argument(self): def test_do_task_with_incorrect_argument(self):
@ -29,7 +29,7 @@ class TestRequestApprovalScript(BaseTest):
workflow = self.create_workflow('empty_workflow') workflow = self.create_workflow('empty_workflow')
processor = WorkflowProcessor(workflow) processor = WorkflowProcessor(workflow)
task = processor.next_task() task = processor.next_task()
task.data = {"approvals": {'dhf8r':"invalid", 'lb3dp':"invalid"}} task.data = {"approvals": {'dhf8r':["invalid"], 'lb3dp':"invalid"}}
script = RequestApproval() script = RequestApproval()
with self.assertRaises(ApiError): with self.assertRaises(ApiError):
script.do_task(task, workflow.study_id, workflow.id, "approvals") script.do_task(task, workflow.study_id, workflow.id, "approvals")
@ -40,9 +40,9 @@ class TestRequestApprovalScript(BaseTest):
workflow = self.create_workflow('empty_workflow') workflow = self.create_workflow('empty_workflow')
processor = WorkflowProcessor(workflow) processor = WorkflowProcessor(workflow)
task = processor.next_task() task = processor.next_task()
task.data = {"approvals": ['dhf8r', 'lb3dp']} task.data = {"study": {"approval1": "dhf8r", 'approval2':'lb3dp'}}
script = RequestApproval() script = RequestApproval()
script.do_task_validate_only(task, workflow.study_id, workflow.id, "approvals") script.do_task_validate_only(task, workflow.study_id, workflow.id, "study.approval1")
self.assertEquals(0, db.session.query(ApprovalModel).count()) self.assertEquals(0, db.session.query(ApprovalModel).count())