diff --git a/Pipfile.lock b/Pipfile.lock index 76f7471c..33d23b19 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -957,7 +957,7 @@ }, "spiffworkflow": { "git": "https://github.com/sartography/SpiffWorkflow.git", - "ref": "ce939de158246e9d10e7e154c92230669354bc64" + "ref": "da79e8b0f66df1cb8372435bdff3b294e5f3a336" }, "sqlalchemy": { "hashes": [ diff --git a/crc/api/tools.py b/crc/api/tools.py index 43c01b0c..7904dfe4 100644 --- a/crc/api/tools.py +++ b/crc/api/tools.py @@ -1,3 +1,4 @@ +import hashlib import io import json @@ -78,11 +79,13 @@ def send_email(subject, address, body, data=None): def evaluate_python_expression(body): """Evaluate the given python expression, returning its result. This is useful if the front end application needs to do real-time processing on task data. If for instance - there is a hide expression that is based on a previous value in the same form.""" + there is a hide expression that is based on a previous value in the same form. + The response includes both the result, and a hash of the original query, subsequent calls + of the same hash are unnecessary. """ try: script_engine = CustomBpmnScriptEngine() result = script_engine.eval(body['expression'], body['data']) - return {"result": result} + return {"result": result, "expression": body['expression'], "data": body['data']} except Exception as e: raise ApiError("expression_error", f"Failed to evaluate the expression '%s'. %s" % (body['expression'], str(e)), diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 395048b4..e3924064 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -259,19 +259,34 @@ class WorkflowService(object): """Looks through the fields in a submitted form, acting on any properties.""" if not hasattr(task.task_spec, 'form'): return for field in task.task_spec.form.fields: - if field.has_property(Task.FIELD_PROP_DOC_CODE) and \ - field.type == Task.FIELD_TYPE_FILE: - file_id = task.data[field.id] - file = db.session.query(FileModel).filter(FileModel.id == file_id).first() - doc_code = WorkflowService.evaluate_property(Task.FIELD_PROP_DOC_CODE, field, task) + data = task.data + if field.has_property(Task.FIELD_PROP_REPEAT): + repeat_array = task.data[field.get_property(Task.FIELD_PROP_REPEAT)] + for repeat_data in repeat_array: + WorkflowService.__post_process_field(task, field, repeat_data) + else: + WorkflowService.__post_process_field(task, field, data) + + @staticmethod + def __post_process_field(task, field, data): + if field.has_property(Task.FIELD_PROP_DOC_CODE) and field.id in data: + # This is generally handled by the front end, but it is possible that the file was uploaded BEFORE + # the doc_code was correctly set, so this is a stop gap measure to assure we still hit it correctly. + file_id = data[field.id] + doc_code = task.workflow.script_engine.eval(field.get_property(Task.FIELD_PROP_DOC_CODE), data) + file = db.session.query(FileModel).filter(FileModel.id == file_id).first() + if(file): file.irb_doc_code = doc_code db.session.commit() - # Set the doc code on the file. - if field.has_property(Task.FIELD_PROP_FILE_DATA) and \ - field.get_property(Task.FIELD_PROP_FILE_DATA) in task.data: - file_id = task.data[field.get_property(Task.FIELD_PROP_FILE_DATA)] - data_store = DataStoreModel(file_id=file_id, key=field.id, value=task.data[field.id]) - db.session.add(data_store) + else: + # We have a problem, the file doesn't exist, and was removed, but it is still referenced in the data + # At least attempt to clear out the data. + data = {} + if field.has_property(Task.FIELD_PROP_FILE_DATA) and \ + field.get_property(Task.FIELD_PROP_FILE_DATA) in data: + file_id = data[field.get_property(Task.FIELD_PROP_FILE_DATA)] + data_store = DataStoreModel(file_id=file_id, key=field.id, value=data[field.id]) + db.session.add(data_store) @staticmethod def evaluate_property(property_name, field, task): diff --git a/tests/test_tools_api.py b/tests/test_tools_api.py index 5e7f6c8c..a918e17f 100644 --- a/tests/test_tools_api.py +++ b/tests/test_tools_api.py @@ -48,6 +48,20 @@ class TestStudyApi(BaseTest): response = json.loads(rv.get_data(as_text=True)) self.assertEqual(True, response['result']) + def test_eval_returns_query(self): + """Assures that along with the result, we get the original data and expression. + This can be useful if the calling client is caching results and needs to hash the expression and data + when it gets returned.""" + data = '{"expression": "x.y==2", "data": {"x":{"y":2}}}' + rv = self.app.put('/v1.0/eval', + data=data, follow_redirects=True, + content_type='application/json', + headers=self.logged_in_headers()) + self.assert_success(rv) + response = json.loads(rv.get_data(as_text=True)) + self.assertEqual("x.y==2", response['expression']) + self.assertEqual({'x': {'y': 2}}, response['data']) + def test_eval_expression_with_strings(self): """Assures we can use python to process a value expression from the front end"""