2020-04-20 20:02:13 +00:00
|
|
|
from ldap3.core.exceptions import LDAPSocketOpenError
|
|
|
|
|
|
|
|
from crc import session, app
|
2020-03-03 18:50:22 +00:00
|
|
|
from crc.api.common import ApiError
|
Created a "StudyService" and moved all complex logic around study manipulation out of the study api, and this service, as things were getting complicated. The Workflow Processor no longer creates the WorkflowModel, the study object handles that, and only passes the model into the workflow processor when it is ready to start the workflow.
Created a Study object (seperate from the StudyModel) that can cronstructed on request, and contains a different data structure than we store in the DB. This allows us to return underlying Categories and Workflows in a clean way.
Added a new status to workflows called "not_started", meaning we have not yet instantiated a processor or created a BPMN, they have no version yet and no stored data, just the possiblity of being started.
The Top Level Workflow or "Master" workflow is now a part of the sample data, and loaded at all times.
Removed the ability to "add a workflow to a study" and "remove a workflow from a study", a study contains all possible workflows by definition.
Example data no longer creates users or studies, it just creates the specs.
2020-03-30 12:00:16 +00:00
|
|
|
from crc.models.study import StudyModel, StudySchema
|
2020-04-23 23:25:01 +00:00
|
|
|
from crc.models.workflow import WorkflowStatus
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
from crc.scripts.script import Script, ScriptValidationError
|
|
|
|
from crc.services.file_service import FileService
|
2020-04-20 20:02:13 +00:00
|
|
|
from crc.services.ldap_service import LdapService
|
2020-03-03 18:50:22 +00:00
|
|
|
from crc.services.protocol_builder import ProtocolBuilderService
|
2020-04-23 18:40:05 +00:00
|
|
|
from crc.services.study_service import StudyService
|
2020-03-27 12:29:31 +00:00
|
|
|
from crc.services.workflow_processor import WorkflowProcessor
|
2020-03-03 18:50:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
class StudyInfo(Script):
|
2020-04-20 20:02:13 +00:00
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
"""Just your basic class that can pull in data from a few api endpoints and do a basic task."""
|
|
|
|
pb = ProtocolBuilderService()
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
type_options = ['info', 'investigators', 'details', 'approvals', 'documents', 'protocol']
|
2020-03-03 18:50:22 +00:00
|
|
|
|
|
|
|
def get_description(self):
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
return """StudyInfo [TYPE], where TYPE is one of 'info', 'investigators', or 'details', 'approvals',
|
|
|
|
'documents' or 'protocol'.
|
2020-03-03 18:50:22 +00:00
|
|
|
Adds details about the current study to the Task Data. The type of information required should be
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
provided as an argument. 'info' returns the basic information such as the title. 'Investigators' provides
|
|
|
|
detailed information about each investigator in th study. 'Details' provides a large number
|
|
|
|
of details about the study, as gathered within the protocol builder, and 'documents',
|
|
|
|
lists all the documents that can be a part of the study, with documents from Protocol Builder
|
|
|
|
marked as required, and details about any files that were uploaded' .
|
2020-03-03 18:50:22 +00:00
|
|
|
"""
|
|
|
|
|
2020-03-27 12:29:31 +00:00
|
|
|
def do_task_validate_only(self, task, study_id, *args, **kwargs):
|
|
|
|
"""For validation only, pretend no results come back from pb"""
|
|
|
|
self.check_args(args)
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
# Assure the reference file exists (a bit hacky, but we want to raise this error early, and cleanly.)
|
|
|
|
FileService.get_file_reference_dictionary()
|
2020-04-06 20:56:00 +00:00
|
|
|
data = {
|
|
|
|
"study":{
|
|
|
|
"info": {
|
|
|
|
"id": 12,
|
|
|
|
"title": "test",
|
|
|
|
"primary_investigator_id":21,
|
|
|
|
"user_uid": "dif84",
|
|
|
|
"sponsor": "sponsor",
|
|
|
|
"ind_number": "1234",
|
|
|
|
"inactive": False
|
|
|
|
},
|
|
|
|
"investigators":
|
|
|
|
{
|
|
|
|
"INVESTIGATORTYPE": "PI",
|
|
|
|
"INVESTIGATORTYPEFULL": "Primary Investigator",
|
|
|
|
"NETBADGEID": "dhf8r"
|
|
|
|
},
|
|
|
|
"details":
|
2020-04-23 23:25:01 +00:00
|
|
|
{},
|
|
|
|
"approvals": {
|
2020-04-24 03:32:20 +00:00
|
|
|
"study_id": 12,
|
|
|
|
"workflow_id": 321,
|
2020-04-23 23:25:01 +00:00
|
|
|
"display_name": "IRB API Details",
|
|
|
|
"name": "irb_api_details",
|
|
|
|
"status": WorkflowStatus.not_started.value,
|
|
|
|
"workflow_spec_id": "irb_api_details",
|
|
|
|
},
|
2020-04-29 14:21:24 +00:00
|
|
|
'protocol': {
|
|
|
|
id: 0,
|
|
|
|
}
|
2020-04-06 20:56:00 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 18:09:21 +00:00
|
|
|
self.add_data_to_task(task=task, data=data["study"])
|
2020-05-06 15:25:50 +00:00
|
|
|
self.add_data_to_task(task, {"documents": StudyService().get_documents_status(study_id)})
|
2020-03-27 12:29:31 +00:00
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
def do_task(self, task, study_id, *args, **kwargs):
|
2020-03-27 12:29:31 +00:00
|
|
|
self.check_args(args)
|
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
cmd = args[0]
|
2020-03-09 16:41:35 +00:00
|
|
|
study_info = {}
|
2020-04-07 18:09:21 +00:00
|
|
|
if self.__class__.__name__ in task.data:
|
|
|
|
study_info = task.data[self.__class__.__name__]
|
2020-03-09 16:41:35 +00:00
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
if cmd == 'info':
|
|
|
|
study = session.query(StudyModel).filter_by(id=study_id).first()
|
Created a "StudyService" and moved all complex logic around study manipulation out of the study api, and this service, as things were getting complicated. The Workflow Processor no longer creates the WorkflowModel, the study object handles that, and only passes the model into the workflow processor when it is ready to start the workflow.
Created a Study object (seperate from the StudyModel) that can cronstructed on request, and contains a different data structure than we store in the DB. This allows us to return underlying Categories and Workflows in a clean way.
Added a new status to workflows called "not_started", meaning we have not yet instantiated a processor or created a BPMN, they have no version yet and no stored data, just the possiblity of being started.
The Top Level Workflow or "Master" workflow is now a part of the sample data, and loaded at all times.
Removed the ability to "add a workflow to a study" and "remove a workflow from a study", a study contains all possible workflows by definition.
Example data no longer creates users or studies, it just creates the specs.
2020-03-30 12:00:16 +00:00
|
|
|
schema = StudySchema()
|
2020-04-13 20:23:31 +00:00
|
|
|
self.add_data_to_task(task, {cmd: schema.dump(study)})
|
2020-03-03 18:50:22 +00:00
|
|
|
if cmd == 'investigators':
|
2020-04-13 20:23:31 +00:00
|
|
|
pb_response = self.pb.get_investigators(study_id)
|
|
|
|
self.add_data_to_task(task, {cmd: self.organize_investigators_by_type(pb_response)})
|
2020-03-03 18:50:22 +00:00
|
|
|
if cmd == 'details':
|
2020-04-13 20:23:31 +00:00
|
|
|
self.add_data_to_task(task, {cmd: self.pb.get_study_details(study_id)})
|
2020-04-23 18:40:05 +00:00
|
|
|
if cmd == 'approvals':
|
|
|
|
self.add_data_to_task(task, {cmd: StudyService().get_approvals(study_id)})
|
Refactor the document details scripts. Now there is one script, it returns data in a consistent format, and has all the details required. The script is located in StudyInfo, with the argument documents. Make note that it returns a dictionary of ALL the documents, with a field to mark which ones are required according to the protocol builder. Others may become required if a workflow determines such, in which case the workflow will enforce this, and the document will have a count > 0, and additional details in a list of files within the document. I modified the XLS file to use lower case variable names, because it disturbed me, and we have to reference them frequently. Removed devious "as_object" variable on get_required_docs, so it behaves like the other methods all the time, and returns a dictionary. All the core business logic for finding the documents list now resides in the StudyService.
Because this changes the endpoint for all existing document details, I've modified all the test and static bpmn files to use the new format.
Shorting up the SponsorsList.xls file makes for slightly faster tests. seems senseless to load 5000 everytime we reset the data.
Tried to test all of this carefully in the test_study_details_documents.py test.
2020-04-29 19:08:11 +00:00
|
|
|
if cmd == 'documents':
|
2020-04-23 23:25:01 +00:00
|
|
|
self.add_data_to_task(task, {cmd: StudyService().get_documents_status(study_id)})
|
2020-04-29 14:21:24 +00:00
|
|
|
if cmd == 'protocol':
|
|
|
|
self.add_data_to_task(task, {cmd: StudyService().get_protocol(study_id)})
|
2020-04-23 23:25:01 +00:00
|
|
|
|
2020-03-27 12:29:31 +00:00
|
|
|
|
|
|
|
def check_args(self, args):
|
|
|
|
if len(args) != 1 or (args[0] not in StudyInfo.type_options):
|
|
|
|
raise ApiError(code="missing_argument",
|
|
|
|
message="The StudyInfo script requires a single argument which must be "
|
|
|
|
"one of %s" % ",".join(StudyInfo.type_options))
|
2020-04-13 20:23:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
def organize_investigators_by_type(self, pb_investigators):
|
|
|
|
"""Convert array of investigators from protocol builder into a dictionary keyed on the type"""
|
|
|
|
output = {}
|
|
|
|
for i in pb_investigators:
|
2020-04-20 20:02:13 +00:00
|
|
|
dict = {"user_id": i["NETBADGEID"], "type_full": i["INVESTIGATORTYPEFULL"]}
|
|
|
|
dict.update(self.get_ldap_dict_if_available(i["NETBADGEID"]))
|
|
|
|
output[i["INVESTIGATORTYPE"]] = dict
|
2020-04-13 20:23:31 +00:00
|
|
|
return output
|
2020-04-20 20:02:13 +00:00
|
|
|
|
|
|
|
def get_ldap_dict_if_available(self, user_id):
|
|
|
|
try:
|
|
|
|
ldap_service = LdapService()
|
|
|
|
return ldap_service.user_info(user_id).__dict__
|
|
|
|
except ApiError:
|
2020-04-21 00:28:12 +00:00
|
|
|
app.logger.info(str(ApiError))
|
2020-04-20 20:02:13 +00:00
|
|
|
return {}
|
|
|
|
except LDAPSocketOpenError:
|
|
|
|
app.logger.info("Failed to connect to LDAP Server.")
|
2020-04-23 18:40:05 +00:00
|
|
|
return {}
|