diff --git a/crc/scripts/study_info.py b/crc/scripts/study_info.py index e336685d..94e35249 100644 --- a/crc/scripts/study_info.py +++ b/crc/scripts/study_info.py @@ -14,7 +14,7 @@ class StudyInfo(Script): """Please see the detailed description that is provided below. """ pb = ProtocolBuilderService() - type_options = ['info', 'investigators', 'details', 'approvals', 'documents', 'protocol'] + type_options = ['info', 'investigators', 'roles', 'details', 'approvals', 'documents', 'protocol'] # This is used for test/workflow validation, as well as documentation. example_data = { @@ -106,11 +106,20 @@ Returns the basic information such as the id and title ### Investigators ### Returns detailed information about related personnel. The order returned is guaranteed to match the order provided in the investigators.xslx reference file. -If possible, detailed information is added in from LDAP about each personnel based on their user_id. +Detailed information is added in from LDAP about each personnel based on their user_id. ``` {investigators_example} ``` +### Investigator Roles ### +Returns a list of all investigator roles, populating any roles with additional information available from +the Protocol Builder and LDAP. Its basically just like Investigators, but it includes all the roles, rather +that just those that were set in Protocol Builder. +``` +{investigators_example} +``` + + ### Details ### Returns detailed information about variable keys read in from the Protocol Builder. @@ -161,6 +170,12 @@ Returns information specific to the protocol. "INVESTIGATORTYPEFULL": "Primary Investigator", "NETBADGEID": "dhf8r" }, + "roles": + { + "INVESTIGATORTYPE": "PI", + "INVESTIGATORTYPEFULL": "Primary Investigator", + "NETBADGEID": "dhf8r" + }, "details": { "IS_IND": 0, @@ -198,6 +213,8 @@ Returns information specific to the protocol. self.add_data_to_task(task, {cmd: schema.dump(study)}) if cmd == 'investigators': self.add_data_to_task(task, {cmd: StudyService().get_investigators(study_id)}) + if cmd == 'roles': + self.add_data_to_task(task, {cmd: StudyService().get_investigators(study_id, all=True)}) if cmd == 'details': self.add_data_to_task(task, {cmd: self.pb.get_study_details(study_id)}) if cmd == 'approvals': diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 142d6166..1f7429cb 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -182,7 +182,7 @@ class StudyService(object): return documents @staticmethod - def get_investigators(study_id): + def get_investigators(study_id, all=False): # Loop through all known investigator types as set in the reference file inv_dictionary = FileService.get_reference_data(FileService.INVESTIGATOR_LIST, 'code') @@ -199,6 +199,8 @@ class StudyService(object): else: inv_dictionary[i_type]['user_id'] = None + if not all: + inv_dictionary = dict(filter(lambda elem: elem[1]['user_id'] is not None, inv_dictionary.items())) return inv_dictionary @staticmethod diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 0faf3b76..09368610 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -207,8 +207,10 @@ class WorkflowService(object): if spiff_task: nav_item['task'] = WorkflowService.spiff_task_to_api_task(spiff_task, add_docs_and_forms=False) nav_item['title'] = nav_item['task'].title # Prefer the task title. + else: nav_item['task'] = None + if not 'is_decision' in nav_item: nav_item['is_decision'] = False @@ -333,10 +335,12 @@ class WorkflowService(object): # otherwise strip off the first word of the task, as that should be following # a BPMN standard, and should not be included in the display. if task.properties and "display_name" in task.properties: - task.title = task.properties['display_name'] + try: + task.title = spiff_task.workflow.script_engine.evaluate_expression(spiff_task, task.properties['display_name']) + except Exception as e: + app.logger.info("Failed to set title on task due to type error." + str(e)) elif task.title and ' ' in task.title: task.title = task.title.partition(' ')[2] - return task @staticmethod diff --git a/tests/data/multi_instance_parallel/multi_instance_parallel.bpmn b/tests/data/multi_instance_parallel/multi_instance_parallel.bpmn index dd6215ed..9e53323f 100644 --- a/tests/data/multi_instance_parallel/multi_instance_parallel.bpmn +++ b/tests/data/multi_instance_parallel/multi_instance_parallel.bpmn @@ -17,6 +17,9 @@ + + + SequenceFlow_1p568pp Flow_0ugjw69 diff --git a/tests/study/test_study_api.py b/tests/study/test_study_api.py index cdae21c5..fdf64239 100644 --- a/tests/study/test_study_api.py +++ b/tests/study/test_study_api.py @@ -182,8 +182,8 @@ class TestStudyApi(BaseTest): self.assertGreater(num_db_studies_after, num_db_studies_before) self.assertEqual(num_abandoned, 1) self.assertEqual(num_open, 1) - self.assertEqual(num_active, 1) - self.assertEqual(num_incomplete, 1) + self.assertEqual(num_active, 2) + self.assertEqual(num_incomplete, 0) self.assertEqual(len(json_data), num_db_studies_after) self.assertEqual(num_open + num_active + num_incomplete + num_abandoned, num_db_studies_after) diff --git a/tests/study/test_study_service.py b/tests/study/test_study_service.py index 1c482bcb..1eb020fa 100644 --- a/tests/study/test_study_service.py +++ b/tests/study/test_study_service.py @@ -183,7 +183,7 @@ class TestStudyService(BaseTest): @patch('crc.services.protocol_builder.ProtocolBuilderService.get_investigators') # mock_docs - def test_get_personnel(self, mock_docs): + def test_get_personnel_roles(self, mock_docs): self.load_example_data() # mock out the protocol builder @@ -191,7 +191,7 @@ class TestStudyService(BaseTest): mock_docs.return_value = json.loads(docs_response) workflow = self.create_workflow('docx') # The workflow really doesnt matter in this case. - investigators = StudyService().get_investigators(workflow.study_id) + investigators = StudyService().get_investigators(workflow.study_id, all=True) self.assertEqual(9, len(investigators)) @@ -207,3 +207,22 @@ class TestStudyService(BaseTest): # No value is provided for Department Chair self.assertIsNone(investigators['DEPT_CH']['user_id']) + + @patch('crc.services.protocol_builder.ProtocolBuilderService.get_investigators') # mock_docs + def test_get_study_personnel(self, mock_docs): + self.load_example_data() + + # mock out the protocol builder + docs_response = self.protocol_builder_response('investigators.json') + mock_docs.return_value = json.loads(docs_response) + + workflow = self.create_workflow('docx') # The workflow really doesnt matter in this case. + investigators = StudyService().get_investigators(workflow.study_id, all=False) + + self.assertEqual(3, len(investigators)) + + # dhf8r is in the ldap mock data. + self.assertEqual("dhf8r", investigators['PI']['user_id']) + self.assertEqual("Dan Funk", investigators['PI']['display_name']) # Data from ldap + self.assertEqual("Primary Investigator", investigators['PI']['label']) # Data from xls file. + self.assertEqual("Always", investigators['PI']['display']) # Data from xls file. diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index 236defdc..abbf8707 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -322,7 +322,7 @@ class TestTasksApi(BaseTest): self.assertEqual(4, len(navigation)) # Start task, form_task, multi_task, end task self.assertEqual("UserTask", workflow.next_task.type) self.assertEqual(MultiInstanceType.sequential.value, workflow.next_task.multi_instance_type) - self.assertEqual(9, workflow.next_task.multi_instance_count) + self.assertEqual(3, workflow.next_task.multi_instance_count) # Assure that the names for each task are properly updated, so they aren't all the same. self.assertEqual("Primary Investigator", workflow.next_task.properties['display_name']) @@ -480,17 +480,23 @@ class TestTasksApi(BaseTest): workflow = self.create_workflow('multi_instance_parallel') workflow_api = self.get_workflow_api(workflow) - self.assertEqual(12, len(workflow_api.navigation)) + self.assertEqual(6, len(workflow_api.navigation)) ready_items = [nav for nav in workflow_api.navigation if nav['state'] == "READY"] - self.assertEqual(9, len(ready_items)) + self.assertEqual(3, len(ready_items)) self.assertEqual("UserTask", workflow_api.next_task.type) self.assertEqual("MultiInstanceTask",workflow_api.next_task.name) - self.assertEqual("more information", workflow_api.next_task.title) + self.assertEqual("Primary Investigator", workflow_api.next_task.title) - for i in random.sample(range(9), 9): + for i in random.sample(range(3), 3): task = TaskSchema().load(ready_items[i]['task']) - data = workflow_api.next_task.data + rv = self.app.put('/v1.0/workflow/%i/task/%s/set_token' % (workflow.id, task.id), + headers=self.logged_in_headers(), + content_type="application/json") + self.assert_success(rv) + json_data = json.loads(rv.get_data(as_text=True)) + workflow = WorkflowApiSchema().load(json_data) + data = workflow.next_task.data data['investigator']['email'] = "dhf8r@virginia.edu" self.complete_form(workflow, task, data) #tasks = self.get_workflow_api(workflow).user_tasks diff --git a/tests/workflow/test_workflow_processor_multi_instance.py b/tests/workflow/test_workflow_processor_multi_instance.py index 76821fed..e1011223 100644 --- a/tests/workflow/test_workflow_processor_multi_instance.py +++ b/tests/workflow/test_workflow_processor_multi_instance.py @@ -146,6 +146,11 @@ class TestWorkflowProcessorMultiInstance(BaseTest): api_task = WorkflowService.spiff_task_to_api_task(task) self.assertEqual(MultiInstanceType.parallel, api_task.multi_instance_type) + + # Assure navigation picks up the label of the current element variable. + nav = WorkflowService.processor_to_workflow_api(processor, task).navigation + self.assertEquals("Primary Investigator", nav[2].title) + task.update_data({"investigator": {"email": "dhf8r@virginia.edu"}}) processor.complete_task(task) processor.do_engine_steps()