From d91f69038894915a403982b0264dc62b2219ee36 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Thu, 23 Apr 2020 19:25:01 -0400 Subject: [PATCH] Adds documents_status StudyInfo script. Adds Documents & Approvals workflow spec. --- crc/scripts/documents.py | 2 +- crc/scripts/study_info.py | 28 +++++- crc/services/study_service.py | 54 ++++++++++-- .../documents_approvals.bpmn | 85 +++++++++++++++++++ example_data.py | 6 ++ 5 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 crc/static/bpmn/documents_approvals/documents_approvals.bpmn diff --git a/crc/scripts/documents.py b/crc/scripts/documents.py index 223d7e22..051b1c4d 100644 --- a/crc/scripts/documents.py +++ b/crc/scripts/documents.py @@ -48,7 +48,7 @@ For example: def get_documents(self, study_id, pb_docs): """Takes data from the protocol builder, and merges it with data from the IRB Pro Categories spreadsheet to return - pertinant details about the required documents.""" + pertinent details about the required documents.""" doc_dictionary = FileService.get_file_reference_dictionary() required_docs = {} diff --git a/crc/scripts/study_info.py b/crc/scripts/study_info.py index d82c485c..5fcca820 100644 --- a/crc/scripts/study_info.py +++ b/crc/scripts/study_info.py @@ -3,6 +3,7 @@ from ldap3.core.exceptions import LDAPSocketOpenError from crc import session, app from crc.api.common import ApiError from crc.models.study import StudyModel, StudySchema +from crc.models.workflow import WorkflowStatus from crc.scripts.script import Script from crc.services.ldap_service import LdapService from crc.services.protocol_builder import ProtocolBuilderService @@ -14,7 +15,7 @@ class StudyInfo(Script): """Just your basic class that can pull in data from a few api endpoints and do a basic task.""" pb = ProtocolBuilderService() - type_options = ['info', 'investigators', 'details', 'approvals'] + type_options = ['info', 'investigators', 'details', 'approvals', 'documents_status'] def get_description(self): return """StudyInfo [TYPE], where TYPE is one of 'info', 'investigators', or 'details' @@ -47,7 +48,27 @@ class StudyInfo(Script): "NETBADGEID": "dhf8r" }, "details": - {} + {}, + "approvals": { + "id": 321, + "display_name": "IRB API Details", + "name": "irb_api_details", + "status": WorkflowStatus.not_started.value, + "workflow_spec_id": "irb_api_details", + }, + "documents_status": [ + { + 'category1': 'Ancillary Document', + 'category2': 'CoC Application', + 'category3': '', + 'Who Uploads?': 'CRC', + 'Id': '12', + 'Name': 'Certificate of Confidentiality Application', + 'count': 0, + 'required': False, + 'code': 'AD_CoCApp' + } + ] } } self.add_data_to_task(task=task, data=data["study"]) @@ -71,6 +92,9 @@ class StudyInfo(Script): self.add_data_to_task(task, {cmd: self.pb.get_study_details(study_id)}) if cmd == 'approvals': self.add_data_to_task(task, {cmd: StudyService().get_approvals(study_id)}) + if cmd == 'documents_status': + self.add_data_to_task(task, {cmd: StudyService().get_documents_status(study_id)}) + task.data["study"] = study_info diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 41dce476..013f64b5 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -4,11 +4,14 @@ from SpiffWorkflow import WorkflowException from crc import db, session from crc.api.common import ApiError +from crc.models.file import FileModel from crc.models.protocol_builder import ProtocolBuilderStudy, ProtocolBuilderStatus from crc.models.stats import WorkflowStatsModel, TaskEventModel from crc.models.study import StudyModel, Study, Category, WorkflowMetadata from crc.models.workflow import WorkflowSpecCategoryModel, WorkflowModel, WorkflowSpecModel, WorkflowState, \ WorkflowStatus +from crc.scripts.documents import Documents +from crc.services.file_service import FileService from crc.services.protocol_builder import ProtocolBuilderService from crc.services.workflow_processor import WorkflowProcessor @@ -64,13 +67,13 @@ class StudyService(object): @staticmethod def get_approvals(study_id): - """Returns a list of category objects, in the correct order.""" + """Returns a list of approval workflows.""" cat = session.query(WorkflowSpecCategoryModel).filter_by(name="approvals").first() specs = session.query(WorkflowSpecModel).filter_by(category_id=cat.id).all() spec_ids = [spec.id for spec in specs] - workflows = session.query(WorkflowModel)\ - .filter(WorkflowModel.study_id == study_id)\ - .filter(WorkflowModel.workflow_spec_id.in_(spec_ids))\ + workflows = session.query(WorkflowModel) \ + .filter(WorkflowModel.study_id == study_id) \ + .filter(WorkflowModel.workflow_spec_id.in_(spec_ids)) \ .all() approvals = [] @@ -80,11 +83,44 @@ class StudyService(object): 'id': workflow.id, 'display_name': workflow.workflow_spec.display_name, 'name': workflow.workflow_spec.display_name, - 'status': workflow.status, + 'status': workflow.status.value, 'workflow_spec_id': workflow.workflow_spec_id, }) return approvals + @staticmethod + def get_documents_status(study_id): + """Returns a list of required documents and related workflow status.""" + doc_service = Documents() + + # Get PB required docs + pb_docs = ProtocolBuilderService.get_required_docs(study_id) + + # Get required docs for study + study_docs = doc_service.get_documents(study_id=study_id, pb_docs=pb_docs) + + # Container for results + documents = [] + + # For each required doc, get file(s) + for code, doc in study_docs.items(): + doc['study_id'] = study_id + doc['code'] = code + doc_files = FileService.get_files(study_id=study_id, irb_doc_code=code) + + # For each file, get associated workflow status + for file in doc_files: + doc['file_id'] = file.id + doc['workflow_id'] = file.workflow_id + + if doc['status'] is None: + workflow: WorkflowModel = session.query(WorkflowModel).filter_by(id=file.workflow_id).first() + doc['status'] = workflow.status.value + + documents.append(doc) + + return documents + @staticmethod def synch_all_studies_with_protocol_builder(user): """Assures that the studies we have locally for the given user are @@ -135,10 +171,10 @@ class StudyService(object): @staticmethod def __get_workflow_metas(study_id): # Add in the Workflows for each category - workflow_models = db.session.query(WorkflowModel).\ - join(WorkflowSpecModel).\ - filter(WorkflowSpecModel.is_master_spec == False).\ - filter(WorkflowModel.study_id == study_id).\ + workflow_models = db.session.query(WorkflowModel). \ + join(WorkflowSpecModel). \ + filter(WorkflowSpecModel.is_master_spec == False). \ + filter(WorkflowModel.study_id == study_id). \ all() workflow_metas = [] for workflow in workflow_models: diff --git a/crc/static/bpmn/documents_approvals/documents_approvals.bpmn b/crc/static/bpmn/documents_approvals/documents_approvals.bpmn new file mode 100644 index 00000000..95f648a0 --- /dev/null +++ b/crc/static/bpmn/documents_approvals/documents_approvals.bpmn @@ -0,0 +1,85 @@ + + + + + + Flow_1k3su2q + + + Flow_0m7unlb + + + + ## Approvals +| Name | Status | Help | +|:-------------- |:-------- |:------ | +{% for approval in StudyInfo.approvals -%} +| [{{approval.display_name}}](/workflow/{{approval.id}}) | {{approval.status}} | [Context here](/help/{{approval.workflow_spec_id}}) | +{% endfor -%} + + +## Documents +| Code | Status | Help | +|:-------------- |:-------- |:------ | +{% for doc in study.documents_status -%} +| [{{doc.code}}](/study/{{doc.study_id}}/workflow/{{doc.workflow_id}}) | {{doc.status}} | [Context here](/help/{{doc.workflow_spec_id}}) | +{% endfor -%} + + + + + + + Flow_142jtxs + Flow_0m7unlb + + + Flow_0c7ryff + Flow_142jtxs + StudyInfo approvals + + + Flow_1k3su2q + Flow_0c7ryff + StudyInfo documents_status + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example_data.py b/example_data.py index e9dc35d5..cdf202c1 100644 --- a/example_data.py +++ b/example_data.py @@ -79,6 +79,12 @@ class ExampleDataLoader: description="TBD", category_id=0, display_order=1) + self.create_spec(id="documents_approvals", + name="documents_approvals", + display_name="Documents & Approvals", + description="TBD", + category_id=0, + display_order=2) # Core Info self.create_spec(id="core_info",