From 5429e7da7d20c66a4bce349374a7312a6e8852e3 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 19 Oct 2021 10:13:43 -0400 Subject: [PATCH] All enumerated lists used in web forms should contain a single value, not a dictionary of value/labels. Removing the spreadsheet.value.column and data.value.column so we just have value.column for both. Improving the __str__ function in the ApiError class, to make debugging a little easier. Adding a "validate_all" flask command, to help us track down any issues with current workflows in production (use this in concert with sync_with_testing) Fixed logs of tests. removed fact_runner.py, a very early and crufty bit of code. --- crc/__init__.py | 33 ++++++++ crc/api/common.py | 10 +++ crc/api/workflow.py | 4 +- crc/models/api_models.py | 13 +-- crc/models/file.py | 2 +- crc/services/lookup_service.py | 39 ++++----- crc/services/workflow_service.py | 58 +++++++------ fact_runner.py | 79 ------------------ tests/data/enum_file_data/enum_file_data.bpmn | 6 +- .../enum_options_competing_files.bpmn | 68 +++++++-------- .../enum_options_from_file.bpmn | 6 +- .../enum_options_from_task_data.bpmn | 28 ++++--- .../enum_options_with_search.bpmn | 6 +- .../enum_options_with_search/sponsors.xlsx | Bin 21148 -> 21269 bytes tests/study/test_study_service.py | 2 +- tests/test_file_datastore.py | 2 +- tests/test_lookup_service.py | 46 +++++----- tests/test_tasks_api.py | 15 ++-- tests/test_verify_end_event.py | 2 +- ..._workflow_enum_default_value_expression.py | 4 +- .../workflow/test_workflow_enum_empty_list.py | 4 +- tests/workflow/test_workflow_service.py | 2 +- .../test_workflow_spec_validation_api.py | 23 +---- .../test_workflow_value_expression.py | 6 +- 24 files changed, 203 insertions(+), 255 deletions(-) delete mode 100644 fact_runner.py diff --git a/crc/__init__.py b/crc/__init__.py index dcb6cde8..44b59a9e 100644 --- a/crc/__init__.py +++ b/crc/__init__.py @@ -1,9 +1,12 @@ import json import logging import os + +import click import sentry_sdk import connexion +from SpiffWorkflow import WorkflowException from connexion import ProblemException from flask import Response from flask_cors import CORS @@ -15,6 +18,7 @@ from sentry_sdk.integrations.flask import FlaskIntegration from apscheduler.schedulers.background import BackgroundScheduler from werkzeug.middleware.proxy_fix import ProxyFix + logging.basicConfig(level=logging.INFO) connexion_app = connexion.FlaskApp(__name__) @@ -130,3 +134,32 @@ def sync_with_testing(): """Load all the workflows currently on testing into this system.""" from crc.api import workflow_sync workflow_sync.sync_all_changed_workflows("https://testing.crconnect.uvadcos.io/api") + +@app.cli.command() +@click.argument("study_id") +@click.argument("category") +@click.argument("spec_id") +def validate_all(study_id, category=None, spec_id=None): + """Step through all the local workflows and validate them, returning any errors. This make take forever. + Please provide a real study id to use for validation, an optional category can be specified to only validate + that category, and you can further specify a specific spec, if needed.""" + from crc.models.workflow import WorkflowSpecModel + from crc.services.workflow_service import WorkflowService + from crc.api.common import ApiError + + specs = session.query(WorkflowSpecModel).all() + for spec in specs: + if spec_id and spec_id != spec.id: + continue + if category and (not spec.category or spec.category.display_name != category): + continue + try: + WorkflowService.test_spec(spec.id, validate_study_id=study_id) + except ApiError as e: + print("Failed to validate workflow " + spec.id) + print(e) + return + except WorkflowException as e: + print("Failed to validate workflow " + spec.id) + print(e) + return diff --git a/crc/api/common.py b/crc/api/common.py index 73775e34..ea9c7842 100644 --- a/crc/api/common.py +++ b/crc/api/common.py @@ -35,6 +35,16 @@ class ApiError(Exception): sentry_sdk.set_context("User", {'user': user}) Exception.__init__(self, self.message) + def __str__(self): + msg = "ApiError: % s. " % self.message + if self.task_name: + msg += "Error in task '%s' (%s). " % (self.task_name, self.task_id) + if self.line_number: + msg += "Error is on line %i. " % self.line_number + if self.file_name: + msg += "In file %s. " % self.file_name + return msg + @classmethod def from_task(cls, code, message, task, status_code=400, line_number=0, offset=0, error_type="", error_line=""): """Constructs an API Error with details pulled from the current task.""" diff --git a/crc/api/workflow.py b/crc/api/workflow.py index 6636296e..08dcbf59 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -397,8 +397,8 @@ def lookup(workflow_id, task_spec_name, field_id, query=None, value=None, limit= """ workflow = session.query(WorkflowModel).filter(WorkflowModel.id == workflow_id).first() lookup_data = LookupService.lookup(workflow, task_spec_name, field_id, query, value, limit) - return LookupDataSchema(many=True).dump(lookup_data) - + # Just return the data + return lookup_data def _verify_user_and_role(processor, spiff_task): """Assures the currently logged in user can access the given workflow and task, or diff --git a/crc/models/api_models.py b/crc/models/api_models.py index b964c519..901f4813 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -56,15 +56,16 @@ class Task(object): FIELD_PROP_VALUE_EXPRESSION = "value_expression" FIELD_PROP_REPEAT_HIDE_EXPRESSION = "repeat_hide_expression" - # Enum field options values pulled from a spreadsheet + # Enum field options FIELD_PROP_SPREADSHEET_NAME = "spreadsheet.name" - FIELD_PROP_SPREADSHEET_VALUE_COLUMN = "spreadsheet.value.column" - FIELD_PROP_SPREADSHEET_LABEL_COLUMN = "spreadsheet.label.column" + FIELD_PROP_DATA_NAME = "data.name" + FIELD_PROP_VALUE_COLUMN = "value.column" + FIELD_PROP_LABEL_COLUMN = "label.column" + + #FIELD_PROP_SPREADSHEET_VALUE_COLUMN = "spreadsheet.value.column" + #FIELD_PROP_SPREADSHEET_LABEL_COLUMN = "spreadsheet.label.column" # Enum field options values pulled from task data - FIELD_PROP_DATA_NAME = "data.name" - FIELD_PROP_VALUE_COLUMN = "data.value.column" - FIELD_PROP_LABEL_COLUMN = "data.label.column" # Group and Repeat functions FIELD_PROP_GROUP = "group" diff --git a/crc/models/file.py b/crc/models/file.py index 5db25b2b..0ccdbddd 100644 --- a/crc/models/file.py +++ b/crc/models/file.py @@ -204,7 +204,7 @@ class LookupDataModel(db.Model): ) -class LookupDataSchema(SQLAlchemyAutoSchema): +class LookupDataSchema(ma.Schema): class Meta: model = LookupDataModel load_instance = True diff --git a/crc/services/lookup_service.py b/crc/services/lookup_service.py index 0d9d0d48..0c8825cf 100644 --- a/crc/services/lookup_service.py +++ b/crc/services/lookup_service.py @@ -13,6 +13,7 @@ from crc import db from crc.api.common import ApiError from crc.models.api_models import Task from crc.models.file import FileModel, FileDataModel, LookupFileModel, LookupDataModel +from crc.models.ldap import LdapSchema from crc.models.workflow import WorkflowModel, WorkflowSpecDependencyFile from crc.services.file_service import FileService from crc.services.ldap_service import LdapService @@ -88,10 +89,11 @@ class LookupService(object): lookup_model = LookupService.__get_lookup_model(workflow, task_spec_id, field_id) if lookup_model.is_ldap: - return LookupService._run_ldap_query(query, limit) + return LookupService._run_ldap_query(query, value, limit) else: return LookupService._run_lookup_query(lookup_model, query, value, limit) + @staticmethod def create_lookup_model(workflow_model, task_spec_id, field_id): """ @@ -115,19 +117,19 @@ class LookupService(object): # Use the contents of a file to populate enum field options if field.has_property(Task.FIELD_PROP_SPREADSHEET_NAME): - if not (field.has_property(Task.FIELD_PROP_SPREADSHEET_VALUE_COLUMN) or - field.has_property(Task.FIELD_PROP_SPREADSHEET_LABEL_COLUMN)): + if not (field.has_property(Task.FIELD_PROP_VALUE_COLUMN) or + field.has_property(Task.FIELD_PROP_LABEL_COLUMN)): raise ApiError.from_task_spec("invalid_enum", "For enumerations based on an xls file, you must include 3 properties: %s, " "%s, and %s" % (Task.FIELD_PROP_SPREADSHEET_NAME, - Task.FIELD_PROP_SPREADSHEET_VALUE_COLUMN, - Task.FIELD_PROP_SPREADSHEET_LABEL_COLUMN), + Task.FIELD_PROP_VALUE_COLUMN, + Task.FIELD_PROP_LABEL_COLUMN), task_spec=spec) # Get the file data from the File Service file_name = field.get_property(Task.FIELD_PROP_SPREADSHEET_NAME) - value_column = field.get_property(Task.FIELD_PROP_SPREADSHEET_VALUE_COLUMN) - label_column = field.get_property(Task.FIELD_PROP_SPREADSHEET_LABEL_COLUMN) + value_column = field.get_property(Task.FIELD_PROP_VALUE_COLUMN) + label_column = field.get_property(Task.FIELD_PROP_LABEL_COLUMN) latest_files = FileService.get_spec_data_files(workflow_spec_id=workflow_model.workflow_spec_id, workflow_id=workflow_model.id, name=file_name) @@ -227,20 +229,13 @@ class LookupService(object): logging.info(db_query) result = db_query.limit(limit).all() logging.getLogger('sqlalchemy.engine').setLevel(logging.ERROR) - return result + result_data = list(map(lambda lookup_item: lookup_item.data, result)) + return result_data @staticmethod - def _run_ldap_query(query, limit): - users = LdapService.search_users(query, limit) - - """Converts the user models into something akin to the - LookupModel in models/file.py, so this can be returned in the same way - we return a lookup data model.""" - user_list = [] - for user in users: - user_list.append({"value": user['uid'], - "label": user['display_name'] + " (" + user['uid'] + ")", - "data": user - }) - return user_list - + def _run_ldap_query(query, value, limit): + if value: + return [LdapSchema().dump(LdapService.user_info(value))] + else: + users = LdapService.search_users(query, limit) + return users diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 5af1d253..b1106fd0 100755 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -349,10 +349,7 @@ class WorkflowService(object): field.get_property(Task.FIELD_PROP_FILE_DATA) in data and \ field.id in data and data[field.id]: file_id = data[field.get_property(Task.FIELD_PROP_FILE_DATA)]["id"] - if field.type == 'enum': - data_args = (field.id, data[field.id]['label']) - else: - data_args = (field.id, data[field.id]) + data_args = (field.id, data[field.id]) DataStoreBase().set_data_common(task.id, None, None, None, None, None, file_id, *data_args) @staticmethod @@ -403,24 +400,21 @@ class WorkflowService(object): # Note: if default is False, we don't want to execute this code if default is None or (isinstance(default, str) and default.strip() == ''): if field.type == "enum" or field.type == "autocomplete": - # Return empty arrays for multi-select enums, otherwise do a value of None. - if field.has_property(Task.FIELD_PROP_ENUM_TYPE) and field.get_property(Task.FIELD_PROP_ENUM_TYPE) == "checkbox": + # Return empty arrays for multi-select + if field.has_property(Task.FIELD_PROP_ENUM_TYPE) and \ + field.get_property(Task.FIELD_PROP_ENUM_TYPE) == "checkbox": return [] else: - return {'value': None, 'label': None} + return None else: return None if field.type == "enum" and not has_lookup: - if hasattr(default, "value"): - default_option = next((obj for obj in field.options if obj.id == default.value), None) - else: - # Fixme: We should likely error out on this in validation, or remove this value/label alltogether. - default_option = next((obj for obj in field.options if obj.id == default), None) + default_option = next((obj for obj in field.options if obj.id == default), None) if not default_option: raise ApiError.from_task("invalid_default", "You specified a default value that does not exist in " "the enum options ", task) - return {'value': default_option.id, 'label': default_option.name} + return default elif field.type == "autocomplete" or field.type == "enum": lookup_model = LookupService.get_lookup_model(task, field) if field.has_property(Task.FIELD_PROP_LDAP_LOOKUP): # All ldap records get the same person. @@ -433,7 +427,7 @@ class WorkflowService(object): if not data: raise ApiError.from_task("invalid_default", "You specified a default value that does not exist in " "the enum options ", task) - return {"value": data.value, "label": data.label, "data": data.data} + return default else: raise ApiError.from_task("unknown_lookup_option", "The settings for this auto complete field " "are incorrect: %s " % field.id, task) @@ -461,10 +455,10 @@ class WorkflowService(object): if len(field.options) > 0: random_choice = random.choice(field.options) if isinstance(random_choice, dict): - random_value = {'value': random_choice['id'], 'label': random_choice['name'], 'data': random_choice['data']} + random_value = random_choice['id'] else: # fixme: why it is sometimes an EnumFormFieldOption, and other times not? - random_value = {'value': random_choice.id, 'label': random_choice.name} + random_value = random_choice.id if field.has_property(Task.FIELD_PROP_ENUM_TYPE) and field.get_property(Task.FIELD_PROP_ENUM_TYPE) == 'checkbox': return [random_value] else: @@ -484,7 +478,8 @@ class WorkflowService(object): LookupDataModel.lookup_file_model == lookup_model).limit(10).all() options = [{"value": d.value, "label": d.label, "data": d.data} for d in data] if len(options) > 0: - random_value = random.choice(options) + option = random.choice(options) + random_value = option['value'] else: raise ApiError.from_task("invalid enum", "You specified an enumeration field (%s)," " with no options" % field.id, task) @@ -755,11 +750,24 @@ class WorkflowService(object): @staticmethod def process_options(spiff_task, field): + if field.type != Task.FIELD_TYPE_ENUM: + return field - # If this is an auto-complete field, do not populate options, a lookup will happen later. - if field.type == Task.FIELD_TYPE_AUTO_COMPLETE: - pass - elif field.has_property(Task.FIELD_PROP_SPREADSHEET_NAME): + if hasattr(field, 'options') and len(field.options) > 1: + return field + elif not (field.has_property(Task.FIELD_PROP_VALUE_COLUMN) or + field.has_property(Task.FIELD_PROP_LABEL_COLUMN)): + raise ApiError.from_task("invalid_enum", + f"For enumerations, you must include options, or a way to generate options from" + f" a spreadsheet or data set. Please set either a spreadsheet name or data name," + f" along with the value and label columns to use from these sources. Valid params" + f" include: " + f"{Task.FIELD_PROP_SPREADSHEET_NAME}, " + f"{Task.FIELD_PROP_DATA_NAME}, " + f"{Task.FIELD_PROP_VALUE_COLUMN}, " + f"{Task.FIELD_PROP_LABEL_COLUMN}", task=spiff_task) + + if field.has_property(Task.FIELD_PROP_SPREADSHEET_NAME): lookup_model = LookupService.get_lookup_model(spiff_task, field) data = db.session.query(LookupDataModel).filter(LookupDataModel.lookup_file_model == lookup_model).all() if not hasattr(field, 'options'): @@ -768,16 +776,12 @@ class WorkflowService(object): field.options.append({"id": d.value, "name": d.label, "data": d.data}) elif field.has_property(Task.FIELD_PROP_DATA_NAME): field.options = WorkflowService.get_options_from_task_data(spiff_task, field) + + return field @staticmethod def get_options_from_task_data(spiff_task, field): - if not (field.has_property(Task.FIELD_PROP_VALUE_COLUMN) or - field.has_property(Task.FIELD_PROP_LABEL_COLUMN)): - raise ApiError.from_task("invalid_enum", - f"For enumerations based on task data, you must include 3 properties: " - f"{Task.FIELD_PROP_DATA_NAME}, {Task.FIELD_PROP_VALUE_COLUMN}, " - f"{Task.FIELD_PROP_LABEL_COLUMN}", task=spiff_task) prop = field.get_property(Task.FIELD_PROP_DATA_NAME) if prop not in spiff_task.data: raise ApiError.from_task("invalid_enum", f"For enumerations based on task data, task data must have " diff --git a/fact_runner.py b/fact_runner.py deleted file mode 100644 index d9b4ba7c..00000000 --- a/fact_runner.py +++ /dev/null @@ -1,79 +0,0 @@ -import pprint - -from SpiffWorkflow.bpmn.BpmnScriptEngine import BpmnScriptEngine -from SpiffWorkflow.bpmn.workflow import BpmnWorkflow -from SpiffWorkflow.camunda.serializer.CamundaSerializer import CamundaSerializer -from SpiffWorkflow.camunda.specs.UserTask import EnumFormField, UserTask - - -class CustomBpmnScriptEngine(BpmnScriptEngine): - """This is a custom script processor that can be easily injected into Spiff Workflow. - Rather than execute arbitrary code, this assumes the script references a fully qualified python class - such as myapp.RandomFact. """ - - def execute(self, task, script, **kwargs): - """ - Assume that the script read in from the BPMN file is a fully qualified python class. Instantiate - that class, pass in any data available to the current task so that it might act on it. - Assume that the class implements the "do_task" method. - - This allows us to reference custom code from the BPMN diagram. - """ - module_name = "app." + script - class_name = module_name.split(".")[-1] - mod = __import__(module_name, fromlist=[class_name]) - klass = getattr(mod, class_name) - klass().do_task(task.data) - -def main(): - print("Loading BPMN Specification.") - spec = bpmn_diagram_to_spec('app/static/bpmn/random_fact') - - print ("Creating a new workflow based on the specification.") - script_engine = CustomBpmnScriptEngine() - workflow = BpmnWorkflow(spec, script_engine=script_engine) - workflow.debug = False - - print ("Running automated tasks.") - workflow.do_engine_steps() - - while not workflow.is_completed(): - workflow.do_engine_steps() - ready_tasks = workflow.get_ready_user_tasks() - while len(ready_tasks) > 0: - for task in ready_tasks: - if isinstance(task.task_spec, UserTask): - show_form(task) - workflow.complete_next() - else: - raise("Unown Ready Task.") - workflow.do_engine_steps() - ready_tasks = workflow.get_ready_user_tasks() - - print("All tasks in the workflow are now complete.") - print("The following data was collected:") - pprint.pprint(workflow.last_task.data) - -def show_form(task): - model = {} - form = task.task_spec.form - for field in form.fields: - print("Please complete the following questions:") - prompt = field.label - if isinstance(field, EnumFormField): - prompt += "? (Options: " + ', '.join([str(option.id) for option in field.options]) + ")" - prompt += "? " - model[form.key + "." + field.id] = input(prompt) - if task.data is None: - task.data = {} - task.data.update(model) - - -def bpmn_diagram_to_spec(file_path): - """This loads up all BPMN diagrams in the BPMN folder.""" - workflowSpec = CamundaSerializer().deserialize_workflow_spec(file_path) - return workflowSpec - - -if __name__ == "__main__": - main() diff --git a/tests/data/enum_file_data/enum_file_data.bpmn b/tests/data/enum_file_data/enum_file_data.bpmn index 340e7d60..285e0ed4 100644 --- a/tests/data/enum_file_data/enum_file_data.bpmn +++ b/tests/data/enum_file_data/enum_file_data.bpmn @@ -1,5 +1,5 @@ - + Flow_1kvuzs1 @@ -12,8 +12,8 @@ - - + + diff --git a/tests/data/enum_options_competing_files/enum_options_competing_files.bpmn b/tests/data/enum_options_competing_files/enum_options_competing_files.bpmn index 41670c4d..f1773308 100644 --- a/tests/data/enum_options_competing_files/enum_options_competing_files.bpmn +++ b/tests/data/enum_options_competing_files/enum_options_competing_files.bpmn @@ -1,5 +1,5 @@ - + Flow_07vc55t @@ -33,8 +33,8 @@ - - + + @@ -48,8 +48,8 @@ - - + + @@ -80,41 +80,41 @@ - - - - - - + + + - - - - - - - - - - - - - - + + + - - - + + + + - - - + + + + + + + + + + + + + + + + @@ -130,12 +130,12 @@ - - - + + + diff --git a/tests/data/enum_options_from_file/enum_options_from_file.bpmn b/tests/data/enum_options_from_file/enum_options_from_file.bpmn index ff02a495..5db7a3e8 100644 --- a/tests/data/enum_options_from_file/enum_options_from_file.bpmn +++ b/tests/data/enum_options_from_file/enum_options_from_file.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_0lvudp8 @@ -15,8 +15,8 @@ - - + + diff --git a/tests/data/enum_options_from_task_data/enum_options_from_task_data.bpmn b/tests/data/enum_options_from_task_data/enum_options_from_task_data.bpmn index 5be4401a..7f52aa7b 100644 --- a/tests/data/enum_options_from_task_data/enum_options_from_task_data.bpmn +++ b/tests/data/enum_options_from_task_data/enum_options_from_task_data.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_0lvudp8 @@ -15,8 +15,8 @@ - - + + @@ -71,29 +71,33 @@ + + + + + + - - - - - + + + - - + + - - + + diff --git a/tests/data/enum_options_with_search/enum_options_with_search.bpmn b/tests/data/enum_options_with_search/enum_options_with_search.bpmn index 02e59697..4ecab273 100644 --- a/tests/data/enum_options_with_search/enum_options_with_search.bpmn +++ b/tests/data/enum_options_with_search/enum_options_with_search.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_0lvudp8 @@ -15,8 +15,8 @@ diff --git a/tests/data/enum_options_with_search/sponsors.xlsx b/tests/data/enum_options_with_search/sponsors.xlsx index d236ee1da5ac43885f2e49256e9a84e3e83ab8ef..21328aeaec9f100b4949813bbe101b0fc7f34804 100644 GIT binary patch delta 7077 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 A)&Kwi diff --git a/tests/study/test_study_service.py b/tests/study/test_study_service.py index f0e76a14..c094c9db 100644 --- a/tests/study/test_study_service.py +++ b/tests/study/test_study_service.py @@ -210,7 +210,7 @@ class TestStudyService(BaseTest): # asd3v is not in ldap, so an error should be returned. self.assertEqual("asd3v", investigators['DC']['user_id']) - self.assertEqual("Unable to locate a user with id asd3v in LDAP", investigators['DC']['error']) # Data from ldap + self.assertEqual("ApiError: Unable to locate a user with id asd3v in LDAP. ", investigators['DC']['error']) # Data from ldap # No value is provided for Department Chair self.assertIsNone(investigators['DEPT_CH']['user_id']) diff --git a/tests/test_file_datastore.py b/tests/test_file_datastore.py index cb108138..1b311e04 100644 --- a/tests/test_file_datastore.py +++ b/tests/test_file_datastore.py @@ -49,7 +49,7 @@ class TestFileDatastore(BaseTest): # process the form that sets the datastore values self.complete_form(workflow, task, {'Study_App_Doc': {'id': file_id}, - 'IRB_HSR_Application_Type': {'label': 'Expedited Application'}, + 'IRB_HSR_Application_Type': 'Expedited Application', 'my_test_field': 'some string', 'the_number': 8, 'a_boolean': True, diff --git a/tests/test_lookup_service.py b/tests/test_lookup_service.py index 33f5001d..dda8772c 100644 --- a/tests/test_lookup_service.py +++ b/tests/test_lookup_service.py @@ -70,8 +70,8 @@ class TestLookupService(BaseTest): processor.do_engine_steps() results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "", value="1000", limit=10) self.assertEqual(1, len(results), "It is possible to find an item based on the id, rather than as a search") - self.assertIsNotNone(results[0].data) - self.assertIsInstance(results[0].data, dict) + self.assertIsNotNone(results[0]) + self.assertIsInstance(results[0], dict) def test_lookup_with_two_spreadsheets_with_the_same_field_name_in_different_forms(self): @@ -87,7 +87,7 @@ class TestLookupService(BaseTest): task = processor.get_ready_user_tasks()[0] results = LookupService.lookup(workflow, task.task_spec.name, "selectedItem", "", value="pigs", limit=10) self.assertEqual(1, len(results), "It is possible to find an item based on the id, rather than as a search") - self.assertIsNotNone(results[0].data) + self.assertIsNotNone(results[0]) results = LookupService.lookup(workflow, task.task_spec.name, "selectedItem", "", value="apples", limit=10) self.assertEqual(0, len(results), "We shouldn't find our fruits mixed in with our animals.") @@ -100,7 +100,7 @@ class TestLookupService(BaseTest): task = processor.get_ready_user_tasks()[0] results = LookupService.lookup(workflow, task.task_spec.name, "selectedItem", "", value="apples", limit=10) self.assertEqual(1, len(results), "It is possible to find an item based on the id, rather than as a search") - self.assertIsNotNone(results[0].data) + self.assertIsNotNone(results[0]) results = LookupService.lookup(workflow, task.task_spec.name, "selectedItem", "", value="pigs", limit=10) self.assertEqual(0, len(results), "We shouldn't find our animals mixed in with our fruits.") @@ -116,55 +116,63 @@ class TestLookupService(BaseTest): results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "medicines", limit=10) self.assertEqual(1, len(results), "words in the middle of label are detected.") - self.assertEqual("The Medicines Company", results[0].label) + self.assertEqual("The Medicines Company", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow,"TaskEnumLookup", "AllTheNames", "UVA", limit=10) self.assertEqual(1, len(results), "Beginning of label is found.") - self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0].label) + self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup","AllTheNames", "uva", limit=10) self.assertEqual(1, len(results), "case does not matter.") - self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0].label) + self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "medici", limit=10) self.assertEqual(1, len(results), "partial words are picked up.") - self.assertEqual("The Medicines Company", results[0].label) + self.assertEqual("The Medicines Company", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "Genetics Savings", limit=10) self.assertEqual(1, len(results), "multiple terms are picked up..") - self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label) + self.assertEqual("Genetics Savings & Clone, Inc.", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "Genetics Sav", limit=10) self.assertEqual(1, len(results), "prefix queries still work with partial terms") - self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label) + self.assertEqual("Genetics Savings & Clone, Inc.", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "Gen Sav", limit=10) self.assertEqual(1, len(results), "prefix queries still work with ALL the partial terms") - self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label) + self.assertEqual("Genetics Savings & Clone, Inc.", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "Inc", limit=10) self.assertEqual(7, len(results), "short terms get multiple correct results.") - self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label) + self.assertEqual("Genetics Savings & Clone, Inc.", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "reaction design", limit=10) self.assertEqual(3, len(results), "all results come back for two terms.") - self.assertEqual("Reaction Design", results[0].label, "Exact matches come first.") + self.assertEqual("Reaction Design", results[0]['CUSTOMER_NAME'], "Exact matches come first.") results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "1 Something", limit=10) - self.assertEqual("1 Something", results[0].label, "Exact matches are preferred") + self.assertEqual("1 Something", results[0]['CUSTOMER_NAME'], "Exact matches are preferred") results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "1 (!-Something", limit=10) - self.assertEqual("1 Something", results[0].label, "special characters don't flake out") + self.assertEqual("1 Something", results[0]['CUSTOMER_NAME'], "special characters don't flake out") results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "1 Something", limit=10) - self.assertEqual("1 Something", results[0].label, "double spaces should not be an issue.") + self.assertEqual("1 Something", results[0]['CUSTOMER_NAME'], "double spaces should not be an issue.") results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "in", limit=10) self.assertEqual(10, len(results), "stop words are not removed.") - self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label) + self.assertEqual("Genetics Savings & Clone, Inc.", results[0]['CUSTOMER_NAME']) results = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", "other", limit=10) - self.assertEqual("Other", results[0].label, "Can't find the word 'other', which is an english stop word") - + self.assertEqual("Other", results[0]['CUSTOMER_NAME'], "Can't find the word 'other', which is an english stop word") + def test_find_by_id(self): + spec = BaseTest.load_test_spec('enum_options_from_file') + workflow = self.create_workflow('enum_options_from_file') + processor = WorkflowProcessor(workflow) + processor.do_engine_steps() + result = LookupService.lookup(workflow, "TaskEnumLookup", "AllTheNames", None, value="1000") + first_result = result[0] + self.assertEquals(1000, first_result['CUSTOMER_NUMBER']) + self.assertEquals('UVA - INTERNAL - GM USE ONLY', first_result['CUSTOMER_NAME']) diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index 9c6f0b41..48dd2fef 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -15,16 +15,10 @@ from SpiffWorkflow.bpmn.PythonScriptEngine import Box class TestTasksApi(BaseTest): def assert_options_populated(self, results, lookup_data_keys): - option_keys = ['value', 'label', 'data'] - self.assertIsInstance(results, list) for result in results: - for option_key in option_keys: - self.assertTrue(option_key in result, 'should have value, label, and data properties populated') - self.assertIsNotNone(result[option_key], '%s should not be None' % option_key) - - self.assertIsInstance(result['data'], dict) + self.assertIsInstance(result, dict) for lookup_data_key in lookup_data_keys: - self.assertTrue(lookup_data_key in result['data'], 'should have all lookup data columns populated') + self.assertTrue(lookup_data_key in result, 'should have all lookup data columns populated') def test_get_current_user_tasks(self): self.load_example_data() @@ -287,8 +281,9 @@ class TestTasksApi(BaseTest): self.assertEqual(5, len(results)) self.assert_options_populated(results, ['CUSTOMER_NUMBER', 'CUSTOMER_NAME', 'CUSTOMER_CLASS_MEANING']) + # Use the lookup to find a specific record, rather than running a search. rv = self.app.get('/v1.0/workflow/%i/lookup/%s/%s?value=%s' % - (workflow.id, task.name, field_id, results[0]['value']), # All records with a word that starts with 'c' + (workflow.id, task.name, field_id, results[0]['CUSTOMER_NUMBER']), headers=self.logged_in_headers(), content_type="application/json") results = json.loads(rv.get_data(as_text=True)) @@ -316,7 +311,7 @@ class TestTasksApi(BaseTest): results = json.loads(rv.get_data(as_text=True)) self.assertEqual(1, len(results)) self.assert_options_populated(results, ['CUSTOMER_NUMBER', 'CUSTOMER_NAME', 'CUSTOMER_CLASS_MEANING']) - self.assertIsInstance(results[0]['data'], dict) + self.assertIsInstance(results[0], dict) def test_enum_from_task_data(self): workflow = self.create_workflow('enum_options_from_task_data') diff --git a/tests/test_verify_end_event.py b/tests/test_verify_end_event.py index 5954d545..28199eb7 100644 --- a/tests/test_verify_end_event.py +++ b/tests/test_verify_end_event.py @@ -13,7 +13,7 @@ class TestValidateEndEvent(BaseTest): mock_get.return_value.ok = True mock_get.return_value.text = self.protocol_builder_response('study_details.json') - error_string = """Error processing template for task EndEvent_1qvyxg7: expected token 'end of statement block', got '='""" + error_string = """ApiError: Error processing template for task EndEvent_1qvyxg7: expected token 'end of statement block', got '='. In file verify_end_event.bpmn. """ self.load_example_data() spec_model = self.load_test_spec('verify_end_event') diff --git a/tests/workflow/test_workflow_enum_default_value_expression.py b/tests/workflow/test_workflow_enum_default_value_expression.py index 644d52f8..e3c5456c 100644 --- a/tests/workflow/test_workflow_enum_default_value_expression.py +++ b/tests/workflow/test_workflow_enum_default_value_expression.py @@ -19,7 +19,7 @@ class TestWorkflowEnumDefault(BaseTest): workflow_api = self.get_workflow_api(workflow) self.assertEqual('Activity_PickColor', workflow_api.next_task.name) - self.assertEqual({'value': 'black', 'label': 'Black'}, workflow_api.next_task.data['color_select']) + self.assertEqual('black', workflow_api.next_task.data['color_select']) # workflow = self.create_workflow('enum_value_expression') @@ -36,7 +36,7 @@ class TestWorkflowEnumDefault(BaseTest): workflow_api = self.get_workflow_api(workflow) self.assertEqual('Activity_PickColor', workflow_api.next_task.name) - self.assertEqual({'value': 'white', 'label': 'White'}, workflow_api.next_task.data['color_select']) + self.assertEqual('white', workflow_api.next_task.data['color_select']) def test_enum_value_expression_and_default(self): spec_model = self.load_test_spec('enum_value_expression_fail') diff --git a/tests/workflow/test_workflow_enum_empty_list.py b/tests/workflow/test_workflow_enum_empty_list.py index 0effdf74..b28c3e99 100644 --- a/tests/workflow/test_workflow_enum_empty_list.py +++ b/tests/workflow/test_workflow_enum_empty_list.py @@ -13,7 +13,7 @@ class TestEmptyEnumList(BaseTest): rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) json_data = json.loads(rv.get_data(as_text=True)) - self.assertEqual(json_data[0]['code'], 'invalid enum') + self.assertEqual(json_data[0]['code'], 'invalid_enum') def test_default_values_for_enum_as_checkbox(self): self.load_test_spec('enum_results') @@ -25,4 +25,4 @@ class TestEmptyEnumList(BaseTest): checkbox_enum_field = task.task_spec.form.fields[0] radio_enum_field = task.task_spec.form.fields[1] self.assertEqual([], service.get_default_value(checkbox_enum_field, task)) - self.assertEqual({'label': None, 'value': None}, service.get_default_value(radio_enum_field, task)) + self.assertEqual(None, service.get_default_value(radio_enum_field, task)) diff --git a/tests/workflow/test_workflow_service.py b/tests/workflow/test_workflow_service.py index cb4da9c2..84e6ccda 100644 --- a/tests/workflow/test_workflow_service.py +++ b/tests/workflow/test_workflow_service.py @@ -88,7 +88,7 @@ class TestWorkflowService(BaseTest): task = processor.next_task() task_api = WorkflowService.spiff_task_to_api_task(task, add_docs_and_forms=True) WorkflowService.populate_form_with_random_data(task, task_api, required_only=False) - self.assertTrue(isinstance(task.data["sponsor"], dict)) + self.assertTrue(isinstance(task.data["sponsor"], str)) def test_dmn_evaluation_errors_in_oncomplete_raise_api_errors_during_validation(self): workflow_spec_model = self.load_test_spec("decision_table_invalid") diff --git a/tests/workflow/test_workflow_spec_validation_api.py b/tests/workflow/test_workflow_spec_validation_api.py index d158c9dd..795e88bd 100644 --- a/tests/workflow/test_workflow_spec_validation_api.py +++ b/tests/workflow/test_workflow_spec_validation_api.py @@ -39,27 +39,6 @@ class TestWorkflowSpecValidation(BaseTest): self.assertEqual(0, len(self.validate_workflow("two_forms"))) self.assertEqual(0, len(self.validate_workflow("ldap_lookup"))) - @unittest.skip("Major changes to operators, pushing up with broken crc workflows so we can progress together") - @patch('crc.services.protocol_builder.ProtocolBuilderService.get_investigators') # mock_studies - @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_successful_validation_of_crc_workflows(self, mock_studies, mock_details, mock_docs, mock_investigators): - - # Mock Protocol Builder responses - studies_response = self.protocol_builder_response('user_studies.json') - 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) - investigators_response = self.protocol_builder_response('investigators.json') - mock_investigators.return_value = json.loads(investigators_response) - - self.load_example_data(use_crc_data=True) - app.config['PB_ENABLED'] = True - self.validate_all_loaded_workflows() - def validate_all_loaded_workflows(self): workflows = session.query(WorkflowSpecModel).all() errors = [] @@ -146,7 +125,7 @@ class TestWorkflowSpecValidation(BaseTest): final_data = WorkflowService.test_spec(spec_model.id, required_only=True) self.assertIsNotNone(final_data) self.assertIn('enum_with_default', final_data) - self.assertEqual('maybe', final_data['enum_with_default']['value']) + self.assertEqual('maybe', final_data['enum_with_default']) def test_invalid_custom_field(self): self.load_example_data() diff --git a/tests/workflow/test_workflow_value_expression.py b/tests/workflow/test_workflow_value_expression.py index bdb8cfe8..1be42451 100644 --- a/tests/workflow/test_workflow_value_expression.py +++ b/tests/workflow/test_workflow_value_expression.py @@ -14,9 +14,7 @@ class TestValueExpression(BaseTest): workflow_api = self.get_workflow_api(workflow) second_task = workflow_api.next_task self.assertEqual('', second_task.data['value_expression_value']) - # self.assertNotIn('color', second_task.data) - self.assertIn('color', second_task.data) - self.assertIsNone(second_task.data['color']['value']) + self.assertNotIn('color', second_task.data) @@ -32,7 +30,7 @@ class TestValueExpression(BaseTest): second_task = workflow_api.next_task self.assertEqual('black', second_task.data['value_expression_value']) self.assertIn('color', second_task.data) - self.assertEqual('black', second_task.data['color']['value']) + self.assertEqual('black', second_task.data['color']) def test_validate_task_with_both_default_and_expression(self): # This actually fails validation.