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-03-19 21:13:30 +00:00
|
|
|
from crc.scripts.script import Script
|
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-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()
|
2020-03-19 21:13:30 +00:00
|
|
|
type_options = ['info', 'investigators', 'details']
|
2020-03-03 18:50:22 +00:00
|
|
|
|
|
|
|
def get_description(self):
|
2020-03-19 21:13:30 +00:00
|
|
|
return """StudyInfo [TYPE], where TYPE is one of 'info', 'investigators', or 'details'
|
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
|
|
|
|
provided as an argument. Basic 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 'required_docs',
|
|
|
|
lists all the documents the Protocol Builder has determined will be required as a part of
|
|
|
|
this study.
|
|
|
|
"""
|
|
|
|
|
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)
|
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-07 18:09:21 +00:00
|
|
|
self.add_data_to_task(task=task, data=data["study"])
|
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-03-09 16:41:35 +00:00
|
|
|
task.data["study"] = study_info
|
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:
|
|
|
|
app.logger.info(ApiError.message)
|
|
|
|
return {}
|
|
|
|
except LDAPSocketOpenError:
|
|
|
|
app.logger.info("Failed to connect to LDAP Server.")
|
|
|
|
return {}
|