Exemplo n.º 1
0
    def synch_with_protocol_builder_if_enabled(user):
        """Assures that the studies we have locally for the given user are
        in sync with the studies available in protocol builder. """

        if ProtocolBuilderService.is_enabled():

            app.logger.info("The Protocol Builder is enabled. app.config['PB_ENABLED'] = " +
                            str(app.config['PB_ENABLED']))

            # Get studies matching this user from Protocol Builder
            pb_studies: List[ProtocolBuilderStudy] = ProtocolBuilderService.get_studies(user.uid)

            # Get studies from the database
            db_studies = session.query(StudyModel).filter_by(user_uid=user.uid).all()

            # Update all studies from the protocol builder, create new studies as needed.
            # Futher assures that every active study (that does exist in the protocol builder)
            # has a reference to every available workflow (though some may not have started yet)
            for pb_study in pb_studies:
                db_study = next((s for s in db_studies if s.id == pb_study.STUDYID), None)
                if not db_study:
                    db_study = StudyModel(id=pb_study.STUDYID)
                    session.add(db_study)
                    db_studies.append(db_study)
                db_study.update_from_protocol_builder(pb_study)
                StudyService._add_all_workflow_specs_to_study(db_study)

            # Mark studies as inactive that are no longer in Protocol Builder
            for study in db_studies:
                pb_study = next((pbs for pbs in pb_studies if pbs.STUDYID == study.id), None)
                if not pb_study:
                    study.protocol_builder_status = ProtocolBuilderStatus.ABANDONED

            db.session.commit()
Exemplo n.º 2
0
    def get_documents_status(study_id):
        """Returns a list of documents related to the study, and any file information
        that is available.."""

        # Get PB required docs, if Protocol Builder Service is enabled.
        if ProtocolBuilderService.is_enabled() and study_id is not None:
            try:
                pb_docs = ProtocolBuilderService.get_required_docs(study_id=study_id)
            except requests.exceptions.ConnectionError as ce:
                app.logger.error(f'Failed to connect to the Protocol Builder - {str(ce)}', exc_info=True)
                pb_docs = []
        else:
            pb_docs = []

        # Loop through all known document types, get the counts for those files,
        # and use pb_docs to mark those as required.
        doc_dictionary = FileService.get_reference_data(FileService.DOCUMENT_LIST, 'code', ['id'])

        documents = {}
        for code, doc in doc_dictionary.items():

            if ProtocolBuilderService.is_enabled():
                pb_data = next((item for item in pb_docs if int(item['AUXDOCID']) == int(doc['id'])), None)
                doc['required'] = False
                if pb_data:
                    doc['required'] = True

            doc['study_id'] = study_id
            doc['code'] = code

            # Make a display name out of categories
            name_list = []
            for cat_key in ['category1', 'category2', 'category3']:
                if doc[cat_key] not in ['', 'NULL']:
                    name_list.append(doc[cat_key])
            doc['display_name'] = ' / '.join(name_list)

            # For each file, get associated workflow status
            doc_files = FileService.get_files_for_study(study_id=study_id, irb_doc_code=code)
            doc['count'] = len(doc_files)
            doc['files'] = []
            for file in doc_files:
                doc['files'].append({'file_id': file.id,
                                     'workflow_id': file.workflow_id})

                # update the document status to match the status of the workflow it is in.
                if 'status' not in doc or doc['status'] is None:
                    workflow: WorkflowModel = session.query(WorkflowModel).filter_by(id=file.workflow_id).first()
                    doc['status'] = workflow.status.value

            documents[code] = doc
        return documents
 def test_get_studies(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'user_studies.json')
     response = ProtocolBuilderService.get_studies(self.test_uid)
     self.assertIsNotNone(response)
Exemplo n.º 4
0
    def get_investigators(study_id, all=False):
        """Convert array of investigators from protocol builder into a dictionary keyed on the type. """

        # Loop through all known investigator types as set in the reference file
        inv_dictionary = StudyService.get_investigator_dictionary()

        # Get PB required docs
        pb_investigators = ProtocolBuilderService.get_investigators(
            study_id=study_id)

        # It is possible for the same type to show up more than once in some circumstances, in those events
        # append a counter to the name.
        investigators = {}
        for i_type in inv_dictionary:
            pb_data_entries = list(item for item in pb_investigators
                                   if item['INVESTIGATORTYPE'] == i_type)
            entry_count = 0
            investigators[i_type] = copy(inv_dictionary[i_type])
            investigators[i_type]['user_id'] = None
            for pb_data in pb_data_entries:
                entry_count += 1
                if entry_count == 1:
                    t = i_type
                else:
                    t = i_type + "_" + str(entry_count)
                investigators[t] = copy(inv_dictionary[i_type])
                investigators[t]['user_id'] = pb_data["NETBADGEID"]
                investigators[t].update(
                    StudyService.get_ldap_dict_if_available(
                        pb_data["NETBADGEID"]))
        if not all:
            investigators = dict(
                filter(lambda elem: elem[1]['user_id'] is not None,
                       investigators.items()))
        return investigators
    def test_info_script_documents(self, mock_get):
        app.config['PB_ENABLED'] = True
        mock_get.return_value.ok = True
        mock_get.return_value.text = self.protocol_builder_response(
            'required_docs.json')
        response = ProtocolBuilderService.get_required_docs(self.test_study_id)
        study_info, second_task = self.do_work(info_type='documents')
        self.assertEqual(study_info, second_task.data['info'])
        self.assertEqual(0, len(study_info['Grant_App']['files']),
                         "Grant_App has not files yet.")
        # Add a grant app file
        data = {'file': (io.BytesIO(b"abcdef"), 'random_fact.svg')}
        rv = self.app.post(
            '/v1.0/file?study_id=%i&workflow_id=%s&task_spec_name=%s&form_field_key=%s'
            % (self.workflow.study_id, self.workflow.id, second_task.name,
               'Grant_App'),
            data=data,
            follow_redirects=True,
            content_type='multipart/form-data',
            headers=self.logged_in_headers())
        self.assert_success(rv)
        file_data = json.loads(rv.get_data(as_text=True))

        # Now get the study info again.
        study_info = StudyInfo().do_task(self.workflow_api.study_id,
                                         self.workflow.study.id,
                                         self.workflow.id, 'documents')
        # The data should contain a file.
        self.assertEqual(1, len(study_info['Grant_App']['files']),
                         "Grant_App has exactly one file.")

        # This file data returned should be the same as what we get back about the file when we uploaded it,
        # but the details on the document should be removed, because that would be recursive.
        del file_data['document']
        self.assertEqual(file_data, study_info['Grant_App']['files'][0])
Exemplo n.º 6
0
class CheckStudy(Script):

    pb = ProtocolBuilderService()

    def get_description(self):
        return """Returns the Check Study data for a Study"""

    def do_task_validate_only(self, task, study_id, workflow_id, *args,
                              **kwargs):
        study = StudyService.get_study(study_id)
        if study:
            return {"DETAIL": "Passed validation.", "STATUS": "No Error"}
        else:
            raise ApiError.from_task(
                code='bad_study',
                message=f'No study for study_id {study_id}',
                task=task)

    def do_task(self, task, study_id, workflow_id, *args, **kwargs):
        check_study = self.pb.check_study(study_id)
        if check_study:
            return check_study
        else:
            raise ApiError.from_task(
                code='missing_check_study',
                message=
                'There was a problem checking information for this study.',
                task=task)
Exemplo n.º 7
0
 def test_get_required_docs(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response('required_docs.json')
     response = ProtocolBuilderService.get_required_docs(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertEqual(5, len(response))
     self.assertEqual(6, response[0]['AUXDOCID'])
Exemplo n.º 8
0
 def test_get_details(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response('study_details.json')
     response = ProtocolBuilderService.get_study_details(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertEqual(64, len(response))
     self.assertEqual(1234, response['IND_1'])
 def test_check_study(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'check_study.json')
     response = ProtocolBuilderService.check_study(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertIn('DETAIL', response[0].keys())
     self.assertIn('STATUS', response[0].keys())
 def test_get_required_docs(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'required_docs.json')
     response = ProtocolBuilderService.get_required_docs(self.test_study_id)
     auxdocs = response['AUXDOCS']
     self.assertIsNotNone(auxdocs)
     self.assertEqual(5, len(auxdocs))
     self.assertEqual(6, auxdocs[0]['SS_AUXILIARY_DOC_TYPE_ID'])
Exemplo n.º 11
0
 def test_get_investigators(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response('investigators.json')
     response = ProtocolBuilderService.get_investigators(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertEqual(5, len(response))
     self.assertEqual("DC", response[0]["INVESTIGATORTYPE"])
     self.assertEqual("Department Contact", response[0]["INVESTIGATORTYPEFULL"])
     self.assertEqual("asd3v", response[0]["NETBADGEID"])
 def test_get_study_sponsors(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'sponsors.json')
     response = ProtocolBuilderService.get_sponsors(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertEqual(3, len(response))
     self.assertEqual(2, response[0]["SS_STUDY"])
     self.assertEqual(2453, response[0]["SPONSOR_ID"])
     self.assertEqual("Abbott Ltd", response[0]["SP_NAME"])
 def test_info_script_investigators(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'investigators.json')
     response = ProtocolBuilderService.get_investigators(self.test_study_id)
     study_info, second_task = self.do_work(info_type='investigators')
     for i in range(len(response)):
         r = response[i]
         s = second_task.data['info'][response[i]['INVESTIGATORTYPE']]
         self.assertEqual(r['INVESTIGATORTYPEFULL'], s['label'])
 def test_irb_info_script(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'irb_info.json')
     workflow = self.create_workflow('irb_info_script')
     irb_info = ProtocolBuilderService.get_irb_info(workflow.study_id)
     workflow_api = self.get_workflow_api(workflow)
     first_task = workflow_api.next_task
     self.assertEqual('Task_PrintInfo', first_task.name)
     self.assertEqual(f'IRB Info: {irb_info}', first_task.documentation)
 def test_get_irb_info(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'irb_info.json')
     response = ProtocolBuilderService.get_irb_info(self.test_study_id)
     self.assertIsNotNone(response)
     self.assertEqual(3, len(response))
     self.assertEqual('IRB Event 1', response[0]["IRBEVENT"])
     self.assertEqual('IRB Event 2', response[1]["IRBEVENT"])
     self.assertEqual('IRB Event 3', response[2]["IRBEVENT"])
Exemplo n.º 16
0
 def _is_valid_study(study_id):
     study_info = None
     study_details = ProtocolBuilderService().get_study_details(study_id)
     if len(study_details) > 0:
         study_info = study_details[0]
     # The review types 2, 3, 23, 24 correspond to review type names
     # `Full Committee`, `Expedited`, `Non-UVA IRB Full Board`, and `Non-UVA IRB Expedited`
     if isinstance(study_info, dict) and 'REVIEW_TYPE' in study_info.keys(
     ) and study_info['REVIEW_TYPE'] in [2, 3, 23, 24]:
         return True
     return False
 def test_info_script_sponsors(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'sponsors.json')
     response = ProtocolBuilderService.get_sponsors(self.test_study_id)
     study_info, second_task = self.do_work(info_type='sponsors')
     for i in range(len(response)):
         self.assertEqual(response[i]['SPONSOR_ID'],
                          second_task.data['info'][i]['SPONSOR_ID'])
         self.assertEqual(response[i]['SP_NAME'],
                          second_task.data['info'][i]['SP_NAME'])
         self.assertEqual(response[i]['SS_STUDY'],
                          second_task.data['info'][i]['SS_STUDY'])
 def test_info_script_details(self, mock_get):
     app.config['PB_ENABLED'] = True
     mock_get.return_value.ok = True
     mock_get.return_value.text = self.protocol_builder_response(
         'study_details.json')
     response = ProtocolBuilderService.get_study_details(
         self.test_study_id)[0]
     study_info, second_task = self.do_work(info_type='details')
     self.assertEqual(response['IBC_NUMBER'],
                      second_task.data['info']['IBC_NUMBER'])
     self.assertEqual(response['IDE'], second_task.data['info']['IDE'])
     self.assertEqual(response['IND_1'], second_task.data['info']['IND_1'])
     self.assertEqual(response['IND_2'], second_task.data['info']['IND_2'])
     self.assertEqual(response['IND_3'], second_task.data['info']['IND_3'])
Exemplo n.º 19
0
class IRBInfo(Script):

    pb = ProtocolBuilderService()

    def get_description(self):
        return """Returns the IRB Info data for a Study"""

    def do_task_validate_only(self, task, study_id, workflow_id, *args,
                              **kwargs):
        return isinstance(study_id, int)

    def do_task(self, task, study_id, workflow_id, *args, **kwargs):
        irb_info = self.pb.get_irb_info(study_id)
        if irb_info:
            return irb_info
        else:
            raise ApiError.from_task(
                code='missing_irb_info',
                message=
                f'There was a problem retrieving IRB Info for study {study_id}.',
                task=task)
Exemplo n.º 20
0
    def synch_with_protocol_builder_if_enabled(user):
        """Assures that the studies we have locally for the given user are
        in sync with the studies available in protocol builder. """

        if ProtocolBuilderService.is_enabled():

            app.logger.info(
                "The Protocol Builder is enabled. app.config['PB_ENABLED'] = "
                + str(app.config['PB_ENABLED']))

            # Get studies matching this user from Protocol Builder
            pb_studies: List[
                ProtocolBuilderCreatorStudy] = ProtocolBuilderService.get_studies(
                    user.uid)

            # Get studies from the database
            db_studies = session.query(StudyModel).filter_by(
                user_uid=user.uid).all()

            # Update all studies from the protocol builder, create new studies as needed.
            # Further assures that every active study (that does exist in the protocol builder)
            # has a reference to every available workflow (though some may not have started yet)
            for pb_study in pb_studies:
                new_status = None
                new_progress_status = None
                db_study = next(
                    (s for s in db_studies if s.id == pb_study.STUDYID), None)
                if not db_study:
                    db_study = StudyModel(id=pb_study.STUDYID)
                    db_study.status = None  # Force a new sa
                    new_status = StudyStatus.in_progress
                    new_progress_status = ProgressStatus.in_progress

                    session.add(db_study)
                    db_studies.append(db_study)

                db_study.update_from_protocol_builder(pb_study, user.uid)
                StudyService._add_all_workflow_specs_to_study(db_study)

                # If there is a new automatic status change and there isn't a manual change in place, record it.
                if new_status and db_study.status != StudyStatus.hold:
                    db_study.status = new_status
                    # make sure status is `in_progress`, before processing new automatic progress_status.
                    if new_progress_status and db_study.status == StudyStatus.in_progress:
                        db_study.progress_status = new_progress_status
                    StudyService.add_study_update_event(
                        db_study,
                        status=new_status,
                        event_type=StudyEventType.automatic)

            # Mark studies as inactive that are no longer in Protocol Builder
            for study in db_studies:
                pb_study = next(
                    (pbs for pbs in pb_studies if pbs.STUDYID == study.id),
                    None)
                if not pb_study and study.status != StudyStatus.abandoned:
                    study.status = StudyStatus.abandoned
                    StudyService.add_study_update_event(
                        study,
                        status=StudyStatus.abandoned,
                        event_type=StudyEventType.automatic)

            db.session.commit()
Exemplo n.º 21
0
    def get_documents_status(study_id):
        """Returns a list of documents related to the study, and any file information
        that is available.."""

        # Get PB required docs, if Protocol Builder Service is enabled.
        if ProtocolBuilderService.is_enabled() and study_id is not None:
            try:
                pb_docs = ProtocolBuilderService.get_required_docs(
                    study_id=study_id)
            except requests.exceptions.ConnectionError as ce:
                app.logger.error(
                    f'Failed to connect to the Protocol Builder - {str(ce)}',
                    exc_info=True)
                pb_docs = []
        else:
            pb_docs = []

        # Loop through all known document types, get the counts for those files,
        # and use pb_docs to mark those as required.
        doc_dictionary = DocumentService.get_dictionary()

        documents = {}
        for code, doc in doc_dictionary.items():

            doc['required'] = False
            if ProtocolBuilderService.is_enabled() and doc['id'] != '':
                pb_data = next(
                    (item for item in pb_docs['AUXDOCS']
                     if int(item['SS_AUXILIARY_DOC_TYPE_ID']) == int(doc['id'])
                     ), None)
                if pb_data:
                    doc['required'] = True

            doc['study_id'] = study_id
            doc['code'] = code

            # Make a display name out of categories
            name_list = []
            for cat_key in ['category1', 'category2', 'category3']:
                if doc[cat_key] not in ['', 'NULL', None]:
                    name_list.append(doc[cat_key])
            doc['display_name'] = ' / '.join(name_list)

            # For each file, get associated workflow status
            doc_files = FileService.get_files_for_study(study_id=study_id,
                                                        irb_doc_code=code)
            doc['count'] = len(doc_files)
            doc['files'] = []

            for file_model in doc_files:
                file = File.from_models(
                    file_model, FileService.get_file_data(file_model.id), [])
                file_data = FileSchema().dump(file)
                del file_data['document']
                doc['files'].append(Box(file_data))
                # update the document status to match the status of the workflow it is in.
                if 'status' not in doc or doc['status'] is None:
                    status = session.query(WorkflowModel.status).filter_by(
                        id=file.workflow_id).scalar()
                    doc['status'] = status.value

            documents[code] = doc
        return Box(documents)
Exemplo n.º 22
0
class StudyInfo(Script):
    """Please see the detailed description that is provided below. """

    pb = ProtocolBuilderService()
    type_options = ['info', 'investigators', 'roles', 'details', 'documents', 'sponsors']

    # This is used for test/workflow validation, as well as documentation.
    example_data = {
        "StudyInfo": {
            "info": {
                "id": 12,
                "title": "test",
                "primary_investigator_id": 21,
                "user_uid": "dif84",
                "sponsor": "sponsor",
                "ind_number": "1234",
                "inactive": False
            },
            "sponsors": [
                {
                    "COMMONRULEAGENCY": None,
                    "SPONSOR_ID": 2453,
                    "SP_NAME": "Abbott Ltd",
                    "SP_TYPE": "Private",
                    "SP_TYPE_GROUP_NAME": None,
                    "SS_STUDY": 2
                },
                {
                    "COMMONRULEAGENCY": None,
                    "SPONSOR_ID": 2387,
                    "SP_NAME": "Abbott-Price",
                    "SP_TYPE": "Incoming Sub Award",
                    "SP_TYPE_GROUP_NAME": "Government",
                    "SS_STUDY": 2
                },
                {
                    "COMMONRULEAGENCY": None,
                    "SPONSOR_ID": 1996,
                    "SP_NAME": "Abernathy-Heidenreich",
                    "SP_TYPE": "Foundation/Not for Profit",
                    "SP_TYPE_GROUP_NAME": "Other External Funding",
                    "SS_STUDY": 2
                }
            ],

            "investigators": {
                'PI': {
                    'label': ProtocolBuilderInvestigatorType.PI.value,
                    'display': 'Always',
                    'unique': 'Yes',
                    'user_id': 'dhf8r',
                    'display_name': 'Dan Funk',
                    'given_name': 'Dan',
                    'email': '*****@*****.**',
                    'telephone_number': '+1 (434) 924-1723',
                    'title': "E42:He's a hoopy frood",
                    'department': 'E0:EN-Eng Study of Parallel Universes',
                    'affiliation': 'faculty',
                    'sponsor_type': 'Staff'},
                'SC_I': {
                    'label': 'Study Coordinator I',
                    'display': 'Always',
                    'unique': 'Yes',
                    'user_id': None},
                'DC': {
                    'label': 'Department Contact',
                    'display': 'Optional',
                    'unique': 'Yes',
                    'user_id': 'asd3v',
                    'error': 'Unable to locate a user with id asd3v in LDAP'},
                'DEPT_CH': {
                    'label': 'Department Chair',
                    'display': 'Always',
                    'unique': 'Yes',
                    'user_id': 'lb3dp'}
            },
            "documents": {
                'AD_CoCApp': {'category1': 'Ancillary Document', 'category2': 'CoC Application', 'category3': '',
                              'Who Uploads?': 'CRC', 'id': '12',
                              'description': 'Certificate of Confidentiality Application', 'required': False,
                              'study_id': 1, 'code': 'AD_CoCApp',
                              'display_name': 'Ancillary Document / CoC Application',
                              'count': 0, 'files': []},
                'UVACompl_PRCAppr': {'category1': 'UVA Compliance', 'category2': 'PRC Approval', 'category3': '',
                                     'Who Uploads?': 'CRC', 'id': '6',
                                     'description': "Cancer Center's PRC Approval Form",
                                     'required': True, 'study_id': 1, 'code': 'UVACompl_PRCAppr',
                                     'display_name': 'UVA Compliance / PRC Approval', 'count': 1, 'files': [
                        {'file_id': 10,
                         'task_id': 'fakingthisout',
                         'workflow_id': 2,
                         'workflow_spec_id': 'docx'}],
                                     'status': 'complete'}
            },
            "details":
                {},
        }
    }

    def example_to_string(self, key):
        return json.dumps(self.example_data['StudyInfo'][key], indent=2, separators=(',', ': '))

    def get_description(self):
        return """study_info(TYPE), where TYPE is one of 'info', 'investigators', 'details', or 'documents'.

Adds details about the current study to the Task Data.  The type of information required should be 
provided as an argument.  The following arguments are available:

### Info ###
Returns the basic information such as the id and title
```
{info_example}
```

### Investigators ###
Returns detailed information about related personnel.
The order returned is guaranteed to match the order provided in the investigators.xslx reference file.
Detailed information is added in from LDAP about each personnel based on their user_id. 
```
{investigators_example}
```

### Investigator Roles ###
Returns a list of all investigator roles, populating any roles with additional information available from
the Protocol Builder and LDAP.  Its basically just like Investigators, but it includes all the roles, rather
that just those that were set in Protocol Builder.
```
{investigators_example}
```


### Details ###
Returns detailed information about variable keys read in from the Protocol Builder.

### Documents ###
Returns a list of all documents that might be related to a study, reading all columns from the irb_documents.xsl 
file. Including information about any files that were uploaded or generated that relate to a given document. 
Please note this is just a few examples, ALL known document types are returned in an actual call.
```
{documents_example}
```


        """.format(info_example=self.example_to_string("info"),
                   investigators_example=self.example_to_string("investigators"),
                   documents_example=self.example_to_string("documents"),
                   )

    def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
        # we call the real do_task so we can
        # seed workflow validations with settings from studies in PB Mock
        # in order to test multiple paths thru the workflow
        return self.do_task(task, study_id, workflow_id, *args, **kwargs)

    @timeit
    def do_task(self, task, study_id, workflow_id, *args, **kwargs):
        self.check_args(args, 2)
        prefix = None
        if len(args) > 1:
            prefix = args[1]
        cmd = args[0]
        # study_info = {}
        # if self.__class__.__name__ in task.data:
        #     study_info = task.data[self.__class__.__name__]
        retval = None
        if cmd == 'info':
            study = session.query(StudyModel).filter_by(id=study_id).first()
            schema = StudySchema()
            retval = schema.dump(study)
        if cmd == 'investigators':
            retval = StudyService().get_investigators(study_id)
        if cmd == 'roles':
            retval = StudyService().get_investigators(study_id, all=True)
        if cmd == 'details':
            details = self.pb.get_study_details(study_id)
            if len(details) > 0:
                retval = details[0]
            else:
                retval = None
        if cmd == 'sponsors':
            retval = self.pb.get_sponsors(study_id)
        if cmd == 'documents':
            retval = StudyService().get_documents_status(study_id)

        return self.box_it(retval, prefix)

    def box_it(self, retval, prefix = None):
        if isinstance(retval, list):
            return [Box(item) for item in retval]
        if isinstance(retval, dict) and prefix is not None:
            return Box({x: retval[x] for x in retval.keys() if x[:len(prefix)] == prefix})
        elif isinstance(retval, dict):
            return Box(retval)


    def check_args(self, args, maxlen=1):
        if len(args) < 1 or len(args) > maxlen 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))
Exemplo n.º 23
0
class StudyInfo(Script):
    """Please see the detailed description that is provided below. """

    pb = ProtocolBuilderService()
    type_options = ['info', 'investigators', 'roles', 'details', 'approvals', 'documents', 'protocol']

    # This is used for test/workflow validation, as well as documentation.
    example_data = {
        "StudyInfo": {
            "info": {
                "id": 12,
                "title": "test",
                "primary_investigator_id": 21,
                "user_uid": "dif84",
                "sponsor": "sponsor",
                "ind_number": "1234",
                "inactive": False
            },
            "investigators": {
                'PI': {
                    'label': 'Primary Investigator',
                    'display': 'Always',
                    'unique': 'Yes',
                    'user_id': 'dhf8r',
                    'display_name': 'Dan Funk',
                    'given_name': 'Dan',
                    'email': '*****@*****.**',
                    'telephone_number': '+1 (434) 924-1723',
                    'title': "E42:He's a hoopy frood",
                    'department': 'E0:EN-Eng Study of Parallel Universes',
                    'affiliation': 'faculty',
                    'sponsor_type': 'Staff'},
                'SC_I': {
                    'label': 'Study Coordinator I',
                    'display': 'Always',
                    'unique': 'Yes',
                    'user_id': None},
                'DC': {
                    'label': 'Department Contact',
                    'display': 'Optional',
                    'unique': 'Yes',
                    'user_id': 'asd3v',
                    'error': 'Unable to locate a user with id asd3v in LDAP'}
            },
            "documents": {
                'AD_CoCApp': {'category1': 'Ancillary Document', 'category2': 'CoC Application', 'category3': '',
                               'Who Uploads?': 'CRC', 'id': '12',
                               'description': 'Certificate of Confidentiality Application', 'required': False,
                               'study_id': 1, 'code': 'AD_CoCApp', 'display_name': 'Ancillary Document / CoC Application',
                               'count': 0, 'files': []},
                'UVACompl_PRCAppr': {'category1': 'UVA Compliance', 'category2': 'PRC Approval', 'category3': '',
                                  'Who Uploads?': 'CRC', 'id': '6', 'description': "Cancer Center's PRC Approval Form",
                                  'required': True, 'study_id': 1, 'code': 'UVACompl_PRCAppr',
                                  'display_name': 'UVA Compliance / PRC Approval', 'count': 1, 'files': [
                                     {'file_id': 10,
                                      'task_id': 'fakingthisout',
                                      'workflow_id': 2,
                                      'workflow_spec_id': 'docx'}],
                                      'status': 'complete'}
            },
            "details":
                {},
            "approvals": {
                "study_id": 12,
                "workflow_id": 321,
                "display_name": "IRB API Details",
                "name": "irb_api_details",
                "status": WorkflowStatus.not_started.value,
                "workflow_spec_id": "irb_api_details",
            },
            'protocol': {
                id: 0,
            }
        }
    }

    def example_to_string(self, key):
        return json.dumps(self.example_data['StudyInfo'][key], indent=2, separators=(',', ': '))

    def get_description(self):
        return """
StudyInfo [TYPE], where TYPE is one of 'info', 'investigators', 'details', 'approvals',
'documents' or 'protocol'.

Adds details about the current study to the Task Data.  The type of information required should be 
provided as an argument.  The following arguments are available:

### Info ###
Returns the basic information such as the id and title
```
{info_example}
```

### Investigators ###
Returns detailed information about related personnel.
The order returned is guaranteed to match the order provided in the investigators.xslx reference file.
Detailed information is added in from LDAP about each personnel based on their user_id. 
```
{investigators_example}
```

### Investigator Roles ###
Returns a list of all investigator roles, populating any roles with additional information available from
the Protocol Builder and LDAP.  Its basically just like Investigators, but it includes all the roles, rather
that just those that were set in Protocol Builder.
```
{investigators_example}
```


### Details ###
Returns detailed information about variable keys read in from the Protocol Builder.

### Approvals ###
Returns data about the status of approvals related to a study.
```
{approvals_example}
```

### Documents ###
Returns a list of all documents that might be related to a study, reading all columns from the irb_documents.xsl 
file. Including information about any files that were uploaded or generated that relate to a given document. 
Please note this is just a few examples, ALL known document types are returned in an actual call.
```
{documents_example}
```

### Protocol ###
Returns information specific to the protocol. 


        """.format(info_example=self.example_to_string("info"),
                   investigators_example=self.example_to_string("investigators"),
                   approvals_example=self.example_to_string("approvals"),
                   documents_example=self.example_to_string("documents"),
                   )

    def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
        """For validation only, pretend no results come back from pb"""
        self.check_args(args)
        # Assure the reference file exists (a bit hacky, but we want to raise this error early, and cleanly.)
        FileService.get_reference_file_data(FileService.DOCUMENT_LIST)
        FileService.get_reference_file_data(FileService.INVESTIGATOR_LIST)
        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"
                    },
                "roles":
                    {
                        "INVESTIGATORTYPE": "PI",
                        "INVESTIGATORTYPEFULL": "Primary Investigator",
                        "NETBADGEID": "dhf8r"
                    },
                "details":
                    {
                        "IS_IND": 0,
                        "IS_IDE": 0,
                        "IS_MULTI_SITE": 0,
                        "IS_UVA_PI_MULTI": 0
                    },
                "approvals": {
                    "study_id": 12,
                    "workflow_id": 321,
                    "display_name": "IRB API Details",
                    "name": "irb_api_details",
                    "status": WorkflowStatus.not_started.value,
                    "workflow_spec_id": "irb_api_details",
                },
                'protocol': {
                    'id': 0,
                }
            }
        }
        self.add_data_to_task(task=task, data=data["study"])
        self.add_data_to_task(task, {"documents": StudyService().get_documents_status(study_id)})

    def do_task(self, task, study_id, workflow_id, *args, **kwargs):
        self.check_args(args)

        cmd = args[0]
        study_info = {}
        if self.__class__.__name__ in task.data:
            study_info = task.data[self.__class__.__name__]

        if cmd == 'info':
            study = session.query(StudyModel).filter_by(id=study_id).first()
            schema = StudySchema()
            self.add_data_to_task(task, {cmd: schema.dump(study)})
        if cmd == 'investigators':
            self.add_data_to_task(task, {cmd: StudyService().get_investigators(study_id)})
        if cmd == 'roles':
            self.add_data_to_task(task, {cmd: StudyService().get_investigators(study_id, all=True)})
        if cmd == 'details':
            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':
            self.add_data_to_task(task, {cmd: StudyService().get_documents_status(study_id)})
        if cmd == 'protocol':
            self.add_data_to_task(task, {cmd: StudyService().get_protocol(study_id)})


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