diff --git a/crc/services/lookup_service.py b/crc/services/lookup_service.py index 7681c81f..067a26a2 100644 --- a/crc/services/lookup_service.py +++ b/crc/services/lookup_service.py @@ -86,10 +86,11 @@ class LookupService(object): processor = WorkflowProcessor(workflow_model) # VERY expensive, Ludicrous for lookup / type ahead spiff_task, field = processor.find_task_and_field_by_field_id(field_id) + # Use the contents of a file to populate enum field options if field.has_property(Task.PROP_OPTIONS_FILE_NAME): if not (field.has_property(Task.PROP_OPTIONS_FILE_VALUE_COLUMN) or field.has_property(Task.PROP_OPTIONS_FILE_LABEL_COLUMN)): - raise ApiError.from_task("invalid_emum", + raise ApiError.from_task("invalid_enum", "For enumerations based on an xls file, you must include 3 properties: %s, " "%s, and %s" % (Task.PROP_OPTIONS_FILE_NAME, Task.PROP_OPTIONS_FILE_VALUE_COLUMN, @@ -111,30 +112,49 @@ class LookupService(object): lookup_model = LookupService.build_lookup_table(data_model, value_column, label_column, workflow_model.workflow_spec_id, field_id) + # Use the value of some other task data field to populate enum field options elif field.has_property(Task.PROP_OPTIONS_DATA_NAME): if not (field.has_property(Task.PROP_OPTIONS_DATA_VALUE_COLUMN) or field.has_property(Task.PROP_OPTIONS_DATA_LABEL_COLUMN)): - raise ApiError.from_task("invalid_emum", + raise ApiError.from_task("invalid_enum", "For enumerations based on task data, you must include 3 properties: %s, " "%s, and %s" % (Task.PROP_OPTIONS_DATA_NAME, Task.PROP_OPTIONS_DATA_VALUE_COLUMN, Task.PROP_OPTIONS_DATA_LABEL_COLUMN), task=spiff_task) + prop = field.get_property(Task.PROP_OPTIONS_DATA_NAME) + if prop not in spiff_task.data: + raise ApiError.from_task("invalid_enum", "For enumerations based on task data, task data must have " + "a property called '%s'" % prop, + task=spiff_task) + # Get the enum options from the task data - data_model = spiff_task.data.__getattribute__(Task.PROP_OPTIONS_DATA_NAME) + data_model = spiff_task.data[prop] value_column = field.get_property(Task.PROP_OPTIONS_DATA_VALUE_COLUMN) label_column = field.get_property(Task.PROP_OPTIONS_DATA_LABEL_COLUMN) - lookup_model = LookupService.build_lookup_table(data_model, value_column, label_column, - workflow_model.workflow_spec_id, field_id) + lookup_model = LookupFileModel(workflow_spec_id=workflow_model.workflow_spec_id, + field_id=field_id, + is_ldap=False) + db.session.add(lookup_model) + items = data_model.items() if isinstance(data_model, dict) else data_model + for item in items: + lookup_data = LookupDataModel(lookup_file_model=lookup_model, + value=item[value_column], + label=item[label_column], + data=item) + db.session.add(lookup_data) + db.session.commit() + # Use the results of an LDAP request to populate enum field options elif field.has_property(Task.PROP_LDAP_LOOKUP): lookup_model = LookupFileModel(workflow_spec_id=workflow_model.workflow_spec_id, field_id=field_id, is_ldap=True) else: raise ApiError("unknown_lookup_option", - "Lookup supports using spreadsheet options or ldap options, and neither was provided.") + "Lookup supports using spreadsheet, task data, or LDAP options, " + "and none of those was provided.") db.session.add(lookup_model) db.session.commit() return lookup_model @@ -149,11 +169,11 @@ class LookupService(object): df = xls.parse(xls.sheet_names[0]) # Currently we only look at the fist sheet. df = pd.DataFrame(df).replace({np.nan: None}) if value_column not in df: - raise ApiError("invalid_emum", + raise ApiError("invalid_enum", "The file %s does not contain a column named % s" % (data_model.file_model.name, value_column)) if label_column not in df: - raise ApiError("invalid_emum", + raise ApiError("invalid_enum", "The file %s does not contain a column named % s" % (data_model.file_model.name, label_column)) diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 04781f52..3a82aa24 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -394,7 +394,7 @@ class WorkflowService(object): # 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.PROP_OPTIONS_FILE_NAME): + elif field.has_property(Task.PROP_OPTIONS_FILE_NAME) or field.has_property(Task.PROP_OPTIONS_DATA_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'): 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 new file mode 100644 index 00000000..5be4401a --- /dev/null +++ b/tests/data/enum_options_from_task_data/enum_options_from_task_data.bpmn @@ -0,0 +1,100 @@ + + + + + SequenceFlow_0lvudp8 + + + + SequenceFlow_02vev7n + + + + + + + + + + + + + + + Flow_1yet4a9 + SequenceFlow_02vev7n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFlow_0lvudp8 + Flow_1yet4a9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index ebe99d93..7f5cc4da 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -403,6 +403,42 @@ class TestTasksApi(BaseTest): self.assert_options_populated(results, ['CUSTOMER_NUMBER', 'CUSTOMER_NAME', 'CUSTOMER_CLASS_MEANING']) self.assertIsInstance(results[0]['data'], dict) + def test_lookup_endpoint_enum_in_task_data(self): + self.load_example_data() + workflow = self.create_workflow('enum_options_from_task_data') + # get the first form in the two form workflow. + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + workflow_api = self.complete_form(workflow, task, {'invitees': [ + {'first_name': 'Alistair', 'last_name': 'Aardvark', 'age': 43, 'likes_pie': True, 'num_lumps': 21, 'secret_id': 'Antimony', 'display_name': 'Professor Alistair A. Aardvark'}, + {'first_name': 'Berthilda', 'last_name': 'Binturong', 'age': 12, 'likes_pie': False, 'num_lumps': 34, 'secret_id': 'Beryllium', 'display_name': 'Dr. Berthilda B. Binturong'}, + {'first_name': 'Chesterfield', 'last_name': 'Capybara', 'age': 32, 'likes_pie': True, 'num_lumps': 1, 'secret_id': 'Cadmium', 'display_name': 'The Honorable C. C. Capybara'}, + ]}) + task = workflow_api.next_task + + field_id = task.form['fields'][0]['id'] + options = task.form['fields'][0]['options'] + self.assertEqual(3, len(options)) + option_id = options[0]['id'] + self.assertEqual('Professor Alistair A. Aardvark', options[0]['name']) + self.assertEqual('Dr. Berthilda B. Binturong', options[1]['name']) + self.assertEqual('The Honorable C. C. Capybara', options[2]['name']) + self.assertEqual('Alistair', options[0]['data']['first_name']) + self.assertEqual('Berthilda', options[1]['data']['first_name']) + self.assertEqual('Chesterfield', options[2]['data']['first_name']) + + rv = self.app.get('/v1.0/workflow/%i/lookup/%s?value=%s' % + (workflow.id, field_id, option_id), + headers=self.logged_in_headers(), + content_type="application/json") + self.assert_success(rv) + results = json.loads(rv.get_data(as_text=True)) + self.assertEqual(1, len(results)) + self.assert_options_populated(results, ['first_name', 'last_name', 'age', 'likes_pie', 'num_lumps', + 'secret_id', 'display_name']) + self.assertIsInstance(results[0]['data'], dict) + def test_lookup_endpoint_for_task_ldap_field_lookup(self): self.load_example_data() workflow = self.create_workflow('ldap_lookup')