diff --git a/crc/models/study.py b/crc/models/study.py index 854ce62f..669ca535 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -41,8 +41,10 @@ class StudyModel(db.Model): class WorkflowMetadata(object): - def __init__(self, id, name, display_name, description, spec_version, category_id, category_display_name, state: WorkflowState, status: WorkflowStatus, - total_tasks, completed_tasks, display_order): + def __init__(self, id, name = None, display_name = None, description = None, spec_version = None, + category_id = None, category_display_name = None, state: WorkflowState = None, + status: WorkflowStatus = None, total_tasks = None, completed_tasks = None, + display_order = None): self.id = id self.name = name self.display_name = display_name @@ -157,6 +159,7 @@ class StudySchema(ma.Schema): files = fields.List(fields.Nested(FileSchema), dump_only=True) approvals = fields.List(fields.Nested('ApprovalSchema'), dump_only=True) enrollment_date = fields.Date(allow_none=True) + events = fields.List(fields.Nested('TaskEventSchema'), dump_only=True) class Meta: model = Study diff --git a/crc/services/study_service.py b/crc/services/study_service.py index fbc62d01..cbf3434d 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -1,6 +1,5 @@ from copy import copy from datetime import datetime -import json from typing import List import requests @@ -13,16 +12,15 @@ from crc.api.common import ApiError from crc.models.file import FileModel, FileModelSchema, File from crc.models.ldap import LdapSchema from crc.models.protocol_builder import ProtocolBuilderStudy, ProtocolBuilderStatus -from crc.models.task_event import TaskEventModel from crc.models.study import StudyModel, Study, Category, WorkflowMetadata +from crc.models.task_event import TaskEventModel, TaskEvent from crc.models.workflow import WorkflowSpecCategoryModel, WorkflowModel, WorkflowSpecModel, WorkflowState, \ WorkflowStatus +from crc.services.approval_service import ApprovalService from crc.services.file_service import FileService from crc.services.ldap_service import LdapService from crc.services.protocol_builder import ProtocolBuilderService from crc.services.workflow_processor import WorkflowProcessor -from crc.services.approval_service import ApprovalService -from crc.models.approval import Approval class StudyService(object): @@ -63,7 +61,7 @@ class StudyService(object): files = (File.from_models(model, FileService.get_file_data(model.id), FileService.get_doc_dictionary()) for model in files) study.files = list(files) - + study.events = StudyService.get_events(study_id) # Calling this line repeatedly is very very slow. It creates the # master spec and runs it. Don't execute this for Abandoned studies, as # we don't have the information to process them. @@ -77,6 +75,14 @@ class StudyService(object): return study + @staticmethod + def get_events(study_id): + event_models = db.session.query(TaskEventModel).filter(TaskEventModel.study_id == study_id).all() + events = [] + for event_model in event_models: + events.append(TaskEvent(event_model, None, WorkflowMetadata(id=event_model.workflow_id))) + return events + @staticmethod def delete_study(study_id): session.query(TaskEventModel).filter_by(study_id=study_id).delete() diff --git a/tests/study/test_study_api.py b/tests/study/test_study_api.py index 3b781f50..9ed7bb2c 100644 --- a/tests/study/test_study_api.py +++ b/tests/study/test_study_api.py @@ -1,4 +1,5 @@ import json + from tests.base_test import BaseTest from datetime import datetime, timezone @@ -13,6 +14,7 @@ from crc.models.study import StudyModel, StudySchema from crc.models.workflow import WorkflowSpecModel, WorkflowModel from crc.services.file_service import FileService from crc.services.workflow_processor import WorkflowProcessor +from crc.services.workflow_service import WorkflowService class TestStudyApi(BaseTest): @@ -112,6 +114,21 @@ class TestStudyApi(BaseTest): for approval in study.approvals: self.assertEqual(full_study['study'].title, approval['title']) + def test_get_study_has_details_about_events(self): + # Set up the study and attach a file to it. + self.load_example_data() + workflow = self.create_workflow('file_upload_form') + processor = WorkflowProcessor(workflow) + task = processor.next_task() + WorkflowService.log_task_action('dhf8r', processor, task, 'my_action') + api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, + headers=self.logged_in_headers(), + content_type="application/json") + self.assert_success(api_response) + study = json.loads(api_response.get_data(as_text=True)) + self.assertEqual(1, len(study['events'])) + self.assertEqual('my_action', study['events'][0]['action']) + def test_add_study(self): self.load_example_data() study = self.add_test_study() diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index 8284313d..9b8b5d68 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -69,7 +69,6 @@ class TestTasksApi(BaseTest): self.assertIsNotNone(val) def test_error_message_on_bad_gateway_expression(self): - self.load_example_data() workflow = self.create_workflow('exclusive_gateway') # get the first form in the two form workflow. @@ -77,7 +76,6 @@ class TestTasksApi(BaseTest): self.complete_form(workflow, task, {"has_bananas": True}) def test_workflow_with_parallel_forms(self): - self.load_example_data() workflow = self.create_workflow('exclusive_gateway') # get the first form in the two form workflow. @@ -89,7 +87,6 @@ class TestTasksApi(BaseTest): self.assertEqual("Task_Num_Bananas", workflow_api.next_task.name) def test_navigation_with_parallel_forms(self): - self.load_example_data() workflow = self.create_workflow('exclusive_gateway') # get the first form in the two form workflow. @@ -107,7 +104,6 @@ class TestTasksApi(BaseTest): self.assertEqual("NOOP", nav[3]['state']) def test_navigation_with_exclusive_gateway(self): - self.load_example_data() workflow = self.create_workflow('exclusive_gateway_2') # get the first form in the two form workflow. @@ -124,7 +120,6 @@ class TestTasksApi(BaseTest): self.assertEqual("Task 3", nav[6]['title']) def test_document_added_to_workflow_shows_up_in_file_list(self): - self.load_example_data() self.create_reference_document() workflow = self.create_workflow('docx') @@ -153,7 +148,6 @@ class TestTasksApi(BaseTest): def test_get_documentation_populated_in_end(self): - self.load_example_data() workflow = self.create_workflow('random_fact') workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task @@ -167,9 +161,7 @@ class TestTasksApi(BaseTest): self.assertTrue("norris" in workflow_api.next_task.documentation) def test_load_workflow_from_outdated_spec(self): - # Start the basic two_forms workflow and complete a task. - self.load_example_data() workflow = self.create_workflow('two_forms') workflow_api = self.get_workflow_api(workflow) self.complete_form(workflow, workflow_api.next_task, {"color": "blue"}) @@ -194,9 +186,7 @@ class TestTasksApi(BaseTest): self.assertTrue(workflow_api.is_latest_spec) def test_soft_reset_errors_out_and_next_result_is_on_original_version(self): - # Start the basic two_forms workflow and complete a task. - self.load_example_data() workflow = self.create_workflow('two_forms') workflow_api = self.get_workflow_api(workflow) self.complete_form(workflow, workflow_api.next_task, {"color": "blue"}) @@ -221,7 +211,6 @@ class TestTasksApi(BaseTest): def test_manual_task_with_external_documentation(self): - self.load_example_data() workflow = self.create_workflow('manual_task_with_external_documentation') # get the first form in the two form workflow. @@ -235,7 +224,6 @@ class TestTasksApi(BaseTest): self.assertTrue('Dan' in workflow_api.next_task.documentation) def test_bpmn_extension_properties_are_populated(self): - self.load_example_data() workflow = self.create_workflow('manual_task_with_external_documentation') # get the first form in the two form workflow. @@ -268,9 +256,7 @@ class TestTasksApi(BaseTest): # 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']) - def test_lookup_endpoint_for_task_field_enumerations(self): - self.load_example_data() workflow = self.create_workflow('enum_options_with_search') # get the first form in the two form workflow. workflow = self.get_workflow_api(workflow) @@ -286,7 +272,6 @@ class TestTasksApi(BaseTest): self.assert_options_populated(results, ['CUSTOMER_NUMBER', 'CUSTOMER_NAME', 'CUSTOMER_CLASS_MEANING']) def test_lookup_endpoint_for_task_field_using_lookup_entry_id(self): - self.load_example_data() workflow = self.create_workflow('enum_options_with_search') # get the first form in the two form workflow. workflow = self.get_workflow_api(workflow) @@ -316,7 +301,6 @@ class TestTasksApi(BaseTest): # the key/values from the spreadsheet are added directly to the form and it shows up as # a dropdown. This tests the case of wanting to get additional data when a user selects # something from a dropdown. - self.load_example_data() workflow = self.create_workflow('enum_options_from_file') # get the first form in the two form workflow. workflow = self.get_workflow_api(workflow) @@ -334,7 +318,6 @@ class TestTasksApi(BaseTest): self.assertIsInstance(results[0]['data'], dict) 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. workflow_api = self.get_workflow_api(workflow) @@ -359,7 +342,6 @@ class TestTasksApi(BaseTest): self.assertEqual('Chesterfield', options[2]['data']['first_name']) def test_lookup_endpoint_for_task_ldap_field_lookup(self): - self.load_example_data() workflow = self.create_workflow('ldap_lookup') # get the first form workflow = self.get_workflow_api(workflow) @@ -378,7 +360,6 @@ class TestTasksApi(BaseTest): self.assertEqual(1, len(results)) def test_sub_process(self): - self.load_example_data() workflow = self.create_workflow('subprocess') workflow_api = self.get_workflow_api(workflow) @@ -399,7 +380,6 @@ class TestTasksApi(BaseTest): self.assertEqual(WorkflowStatus.complete, workflow_api.status) def test_update_task_resets_token(self): - self.load_example_data() workflow = self.create_workflow('exclusive_gateway') # Start the workflow.