Adds documents_status StudyInfo script. Adds Documents & Approvals workflow spec.

This commit is contained in:
Aaron Louie 2020-04-23 19:25:01 -04:00
parent 2e601719ad
commit d91f690388
5 changed files with 163 additions and 12 deletions

View File

@ -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 = {}

View File

@ -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

View File

@ -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:

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_00j2iu5" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<bpmn:process id="Process_1gmf4la" isExecutable="true">
<bpmn:documentation />
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1k3su2q</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="EndEvent_1qvyxg7">
<bpmn:incoming>Flow_0m7unlb</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0m7unlb" sourceRef="Activity_DisplayDocsAndApprovals" targetRef="EndEvent_1qvyxg7" />
<bpmn:manualTask id="Activity_DisplayDocsAndApprovals" name="Display Documents and Approvals">
<bpmn:documentation>## 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 -%}
</bpmn:documentation>
<bpmn:extensionElements>
<camunda:properties>
<camunda:property name="display_name" value="Documents and Approvals" />
</camunda:properties>
</bpmn:extensionElements>
<bpmn:incoming>Flow_142jtxs</bpmn:incoming>
<bpmn:outgoing>Flow_0m7unlb</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:scriptTask id="Activity_0a14x7j" name="Load Approvals">
<bpmn:incoming>Flow_0c7ryff</bpmn:incoming>
<bpmn:outgoing>Flow_142jtxs</bpmn:outgoing>
<bpmn:script>StudyInfo approvals</bpmn:script>
</bpmn:scriptTask>
<bpmn:scriptTask id="Activity_1aju60t" name="Load Documents">
<bpmn:incoming>Flow_1k3su2q</bpmn:incoming>
<bpmn:outgoing>Flow_0c7ryff</bpmn:outgoing>
<bpmn:script>StudyInfo documents_status</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_142jtxs" sourceRef="Activity_0a14x7j" targetRef="Activity_DisplayDocsAndApprovals" />
<bpmn:sequenceFlow id="Flow_0c7ryff" sourceRef="Activity_1aju60t" targetRef="Activity_0a14x7j" />
<bpmn:sequenceFlow id="Flow_1k3su2q" sourceRef="StartEvent_1" targetRef="Activity_1aju60t" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1gmf4la">
<bpmndi:BPMNEdge id="Flow_0m7unlb_di" bpmnElement="Flow_0m7unlb">
<di:waypoint x="710" y="117" />
<di:waypoint x="782" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="192" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_1qvyxg7_di" bpmnElement="EndEvent_1qvyxg7">
<dc:Bounds x="782" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_19nawos_di" bpmnElement="Activity_DisplayDocsAndApprovals">
<dc:Bounds x="610" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1bxk8h3_di" bpmnElement="Activity_0a14x7j">
<dc:Bounds x="440" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_07ytvmv_di" bpmnElement="Activity_1aju60t">
<dc:Bounds x="290" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_142jtxs_di" bpmnElement="Flow_142jtxs">
<di:waypoint x="540" y="117" />
<di:waypoint x="610" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0c7ryff_di" bpmnElement="Flow_0c7ryff">
<di:waypoint x="390" y="117" />
<di:waypoint x="440" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1k3su2q_di" bpmnElement="Flow_1k3su2q">
<di:waypoint x="228" y="117" />
<di:waypoint x="290" y="117" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -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",