From 9077ff3ebfe26601bec8bdaaf5af47ca4790313d Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Tue, 14 Jul 2020 11:38:48 -0400 Subject: [PATCH] It is not possible to use task_data for an auto-complete field. It's too expensive an operation to provide that feature on the backend, and the data already fully resides on the front end anyway. Task-data can be used to populate enum fields if needed, so it can populate dropdowns, radios and checkboxes, just not auto-complete. --- crc/models/file.py | 1 - crc/services/lookup_service.py | 41 ++++---------------------------- crc/services/workflow_service.py | 27 +++++++++++++++++++-- tests/test_tasks_api.py | 13 +--------- 4 files changed, 30 insertions(+), 52 deletions(-) diff --git a/crc/models/file.py b/crc/models/file.py index 5eb50d4e..8afed6cd 100644 --- a/crc/models/file.py +++ b/crc/models/file.py @@ -144,7 +144,6 @@ class LookupFileModel(db.Model): """Gives us a quick way to tell what kind of lookup is set on a form field. Connected to the file data model, so that if a new version of the same file is created, we can update the listing.""" - #fixme: What happens if they change the file associated with a lookup field? __tablename__ = 'lookup_file' id = db.Column(db.Integer, primary_key=True) workflow_spec_id = db.Column(db.String) diff --git a/crc/services/lookup_service.py b/crc/services/lookup_service.py index 65680f34..c9eb1dd8 100644 --- a/crc/services/lookup_service.py +++ b/crc/services/lookup_service.py @@ -4,7 +4,7 @@ from collections import OrderedDict import pandas as pd from pandas import ExcelFile, np -from sqlalchemy import func, desc +from sqlalchemy import desc from sqlalchemy.sql.functions import GenericFunction from crc import db @@ -119,40 +119,6 @@ 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_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[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 = 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, @@ -160,8 +126,8 @@ class LookupService(object): is_ldap=True) else: raise ApiError("unknown_lookup_option", - "Lookup supports using spreadsheet, task data, or LDAP options, " - "and none of those was provided.") + "Lookup supports using spreadsheet or LDAP options, " + "and neither of those was provided.") db.session.add(lookup_model) db.session.commit() return lookup_model @@ -247,3 +213,4 @@ class LookupService(object): "data": user }) return user_list + diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 3a82aa24..e212179b 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -394,16 +394,39 @@ 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) or field.has_property(Task.PROP_OPTIONS_DATA_NAME): + elif field.has_property(Task.PROP_OPTIONS_FILE_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'): field.options = [] for d in data: field.options.append({"id": d.value, "name": d.label, "data": d.data}) - + elif field.has_property(Task.PROP_OPTIONS_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.PROP_OPTIONS_DATA_VALUE_COLUMN) or + field.has_property(Task.PROP_OPTIONS_DATA_LABEL_COLUMN)): + raise ApiError.from_task("invalid_enum", + f"For enumerations based on task data, you must include 3 properties: " + f"{Task.PROP_OPTIONS_DATA_NAME}, {Task.PROP_OPTIONS_DATA_VALUE_COLUMN}, " + f"{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", f"For enumerations based on task data, task data must have " + f"a property called {prop}", task=spiff_task) + # Get the enum options from the task data + 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) + items = data_model.items() if isinstance(data_model, dict) else data_model + options = [] + for item in items: + options.append({"id": item[value_column], "name": item[label_column], "data": item}) + return options + @staticmethod def log_task_action(user_uid, workflow_model, spiff_task, action, version): task = WorkflowService.spiff_task_to_api_task(spiff_task) diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index 7f5cc4da..09690058 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -403,7 +403,7 @@ 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): + def test_enum_from_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. @@ -428,17 +428,6 @@ class TestTasksApi(BaseTest): 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')