コード例 #1
0
 def test_get_user_with_spaces(self):
     user_info = LdapService.user_info("    LB3DP ")
     # Call this a second time, becuase the error we ran into wasn't that it wasn't possible to find it,
     # but that it attepts to add it to the database a second time with the same id.
     user_info = LdapService.user_info("    LB3DP ")
     self.assertIsNotNone(user_info)
     self.assertEqual("lb3dp", user_info.uid)
コード例 #2
0
    def test_delete_study_with_workflow_and_status_etc(self):
        self.load_example_data()
        workflow = session.query(WorkflowModel).first()
        stats1 = StudyEvent(
            study_id=workflow.study_id,
            status=StudyStatus.in_progress,
            comment='Some study status change event',
            event_type=StudyEventType.user,
            user_uid=self.users[0]['uid'],
        )
        LdapService.user_info('dhf8r') # Assure that there is a dhf8r in ldap for StudyAssociated.

        email = EmailModel(subject="x", study_id=workflow.study_id)
        associate = StudyAssociated(study_id=workflow.study_id, uid=self.users[0]['uid'])
        event = StudyEvent(study_id=workflow.study_id)
        session.add_all([email, associate, event])


        stats2 = TaskEventModel(study_id=workflow.study_id, workflow_id=workflow.id, user_uid=self.users[0]['uid'])
        session.add_all([stats1, stats2])
        session.commit()
        rv = self.app.delete('/v1.0/study/%i' % workflow.study_id, headers=self.logged_in_headers())
        self.assert_success(rv)
        del_study = session.query(StudyModel).filter(StudyModel.id == workflow.study_id).first()
        self.assertIsNone(del_study)
コード例 #3
0
    def update_approval(approval_id, approver_uid):
        """Update a specific approval
        NOTE: Actual update happens in the API layer, this
        funtion is currently in charge of only sending
        corresponding emails
        """
        db_approval = session.query(ApprovalModel).get(approval_id)
        status = db_approval.status
        if db_approval:
            if status == ApprovalStatus.APPROVED.value:
                # second_approval = ApprovalModel().query.filter_by(
                #     study_id=db_approval.study_id, workflow_id=db_approval.workflow_id,
                #     status=ApprovalStatus.PENDING.value, version=db_approval.version).first()
                # if second_approval:
                # send rrp approval request for second approver
                ldap_service = LdapService()
                pi_user_info = ldap_service.user_info(
                    db_approval.study.primary_investigator_id)
                approver_info = ldap_service.user_info(approver_uid)
                # send rrp submission
                mail_result = send_ramp_up_approved_email(
                    '*****@*****.**', [pi_user_info.email_address],
                    f'{approver_info.display_name} - ({approver_info.uid})')
                if mail_result:
                    app.logger.error(mail_result, exc_info=True)
            elif status == ApprovalStatus.DECLINED.value:
                ldap_service = LdapService()
                pi_user_info = ldap_service.user_info(
                    db_approval.study.primary_investigator_id)
                approver_info = ldap_service.user_info(approver_uid)
                # send rrp submission
                mail_result = send_ramp_up_denied_email(
                    '*****@*****.**', [pi_user_info.email_address],
                    f'{approver_info.display_name} - ({approver_info.uid})')
                if mail_result:
                    app.logger.error(mail_result, exc_info=True)
                first_approval = ApprovalModel().query.filter_by(
                    study_id=db_approval.study_id,
                    workflow_id=db_approval.workflow_id,
                    status=ApprovalStatus.APPROVED.value,
                    version=db_approval.version).first()
                if first_approval:
                    # Second approver denies
                    first_approver_info = ldap_service.user_info(
                        first_approval.approver_uid)
                    approver_email = [
                        first_approver_info.email_address
                    ] if first_approver_info.email_address else app.config[
                        'FALLBACK_EMAILS']
                    # send rrp denied by second approver email to first approver
                    mail_result = send_ramp_up_denied_email_to_approver(
                        '*****@*****.**', approver_email,
                        f'{pi_user_info.display_name} - ({pi_user_info.uid})',
                        f'{approver_info.display_name} - ({approver_info.uid})'
                    )
                    if mail_result:
                        app.logger.error(mail_result, exc_info=True)

        return db_approval
コード例 #4
0
    def load_example_data(self, use_crc_data=False, use_rrt_data=False):
        """use_crc_data will cause this to load the mammoth collection of documents
        we built up developing crc, use_rrt_data will do the same for hte rrt project,
         otherwise it depends on a small setup for running tests."""
        from example_data import ExampleDataLoader
        ExampleDataLoader.clean_db()
        # If in production mode, only add the first user.
        if app.config['PRODUCTION']:
            ldap_info = LdapService.user_info(self.users[0]['uid'])
            session.add(
                UserModel(uid=self.users[0]['uid'], ldap_info=ldap_info))
        else:
            for user_json in self.users:
                ldap_info = LdapService.user_info(user_json['uid'])
                session.add(
                    UserModel(uid=user_json['uid'], ldap_info=ldap_info))

        if use_crc_data:
            ExampleDataLoader().load_all()
        elif use_rrt_data:
            ExampleDataLoader().load_rrt()
        else:
            ExampleDataLoader().load_test_data()

        session.commit()
        for study_json in self.studies:
            study_model = StudyModel(**study_json)
            session.add(study_model)
            StudyService._add_all_workflow_specs_to_study(study_model)
            session.commit()
            update_seq = f"ALTER SEQUENCE %s RESTART WITH %s" % (
                StudyModel.__tablename__ + '_id_seq', study_model.id + 1)
            print("Update Sequence." + update_seq)
            session.execute(update_seq)
        session.flush()

        specs = session.query(WorkflowSpecModel).all()
        self.assertIsNotNone(specs)

        for spec in specs:
            files = session.query(FileModel).filter_by(
                workflow_spec_id=spec.id).all()
            self.assertIsNotNone(files)
            self.assertGreater(len(files), 0)
            for file in files:
                # file_data = session.query(FileDataModel).filter_by(file_model_id=file.id).all()
                file_data = SpecFileService().get_spec_file_data(file.id).data
                self.assertIsNotNone(file_data)
                self.assertGreater(len(file_data), 0)
コード例 #5
0
    def get_study(study_id, study_model: StudyModel = None, do_status=False):
        """Returns a study model that contains all the workflows organized by category.
        IMPORTANT:  This is intended to be a lightweight call, it should never involve
        loading up and executing all the workflows in a study to calculate information."""
        if not study_model:
            study_model = session.query(StudyModel).filter_by(
                id=study_id).first()

        study = Study.from_model(study_model)
        study.create_user_display = LdapService.user_info(
            study.user_uid).display_name
        last_event: TaskEventModel = session.query(TaskEventModel) \
            .filter_by(study_id=study_id, action='COMPLETE') \
            .order_by(TaskEventModel.date.desc()).first()
        if last_event is None:
            study.last_activity_user = '******'
            study.last_activity_date = ""
        else:
            study.last_activity_user = LdapService.user_info(
                last_event.user_uid).display_name
            study.last_activity_date = last_event.date
        study.categories = StudyService.get_categories()
        workflow_metas = StudyService._get_workflow_metas(study_id)
        files = FileService.get_files_for_study(study.id)
        files = (File.from_models(model, FileService.get_file_data(model.id),
                                  DocumentService.get_dictionary())
                 for model in files)
        study.files = list(files)
        # Calling this line repeatedly is very very slow.  It creates the
        # master spec and runs it.  Don't execute this for Abandoned studies, as
        # we don't have the information to process them.
        if study.status != StudyStatus.abandoned:
            # this line is taking 99% of the time that is used in get_study.
            # see ticket #196
            if do_status:
                # __get_study_status() runs the master workflow to generate the status dictionary
                status = StudyService._get_study_status(study_model)
                study.warnings = StudyService._update_status_of_workflow_meta(
                    workflow_metas, status)

            # Group the workflows into their categories.
            for category in study.categories:
                category.workflows = {
                    w
                    for w in workflow_metas if w.category_id == category.id
                }

        return study
コード例 #6
0
    def set_users_info_in_task(self, task, args):
        if len(args) > 1:
            raise ApiError(code="invalid_argument",
                           message="Ldap takes at most one argument, the "
                           "UID for the person we want to look up.")
        if len(args) < 1:
            if UserService.has_user():
                uid = UserService.current_user().uid
        else:
            uid = args[0]

        try:
            user_info = LdapService.user_info(uid)
        except ApiError as ae:
            app.logger.info(ae)
            return {}
        except Exception as e:
            app.logger.info(e)
            return {}
        else:
            user_info_dict = {
                "display_name": user_info.display_name,
                "given_name": user_info.given_name,
                "email_address": user_info.email_address,
                "telephone_number": user_info.telephone_number,
                "title": user_info.title,
                "department": user_info.department,
                "affiliation": user_info.affiliation,
                "sponsor_type": user_info.sponsor_type,
                "uid": user_info.uid,
                "proper_name": user_info.proper_name()
            }
            return user_info_dict
コード例 #7
0
    def get_users_info(self, task, args):
        if len(args) < 1:
            raise ApiError(
                code="missing_argument",
                message="Email script requires at least one argument.  The "
                "name of the variable in the task data that contains user"
                "id to process.  Multiple arguments are accepted.")
        emails = []
        for arg in args:
            try:
                uid = task.workflow.script_engine.evaluate_expression(
                    task, arg)
            except Exception as e:
                app.logger.error(f'Workflow engines could not parse {arg}',
                                 exc_info=True)
                continue
            user_info = LdapService.user_info(uid)
            email = user_info.email_address
            emails.append(user_info.email_address)
            if not isinstance(email, str):
                raise ApiError(
                    code="invalid_argument",
                    message=
                    "The Email script requires at least 1 UID argument.  The "
                    "name of the variable in the task data that contains subject and"
                    " user ids to process.  This must point to an array or a string, but "
                    "it currently points to a %s " % emails.__class__.__name__)

        return emails
コード例 #8
0
 def get_associated_emails(study_id):
     associated_emails = []
     associates = StudyService.get_study_associates(study_id)
     for associate in associates:
         if associate.send_email is True:
             user_info = LdapService.user_info(associate.uid)
             associated_emails.append(user_info.email_address)
     return associated_emails
コード例 #9
0
    def get_approval_details(approval):
        """Returns a list of packed approval details, obtained from
        the task data sent during the workflow """
        def extract_value(task, key):
            if key in task['data']:
                return pickle.loads(b64decode(task['data'][key]['__bytes__']))
            else:
                return ""

        def find_task(uuid, task):
            if task['id']['__uuid__'] == uuid:
                return task
            for child in task['children']:
                task = find_task(uuid, child)
                if task:
                    return task

        if approval.status != ApprovalStatus.APPROVED.value:
            return {}
        for related_approval in approval.related_approvals:
            if related_approval.status != ApprovalStatus.APPROVED.value:
                continue
        workflow = db.session.query(WorkflowModel).filter(
            WorkflowModel.id == approval.workflow_id).first()
        data = json.loads(workflow.bpmn_workflow_json)
        last_task = find_task(data['last_task']['__uuid__'], data['task_tree'])
        personnel = extract_value(last_task, 'personnel')
        training_val = extract_value(last_task, 'RequiredTraining')
        pi_supervisor = extract_value(last_task, 'PISupervisor')['value']
        review_complete = 'AllRequiredTraining' in training_val
        pi_uid = workflow.study.primary_investigator_id
        pi_details = LdapService.user_info(pi_uid)
        details = {
            'Supervisor': pi_supervisor,
            'PI_Details': pi_details,
            'Review': review_complete
        }
        details['person_details'] = []
        details['person_details'].append(pi_details)
        for person in personnel:
            uid = person['PersonnelComputingID']['value']
            details['person_details'].append(LdapService.user_info(uid))

        return details
コード例 #10
0
    def from_model(cls, model: ApprovalModel):
        args = dict(
            (k, v) for k, v in model.__dict__.items() if not k.startswith('_'))
        instance = cls(**args)
        instance.related_approvals = []
        instance.title = model.study.title if model.study else ''

        try:
            instance.approver = LdapService.user_info(model.approver_uid)
            instance.primary_investigator = LdapService.user_info(
                model.study.primary_investigator_id)
        except ApiError as ae:
            app.logger.error(
                f'Ldap lookup failed for approval record {model.id}',
                exc_info=True)

        doc_dictionary = FileService.get_doc_dictionary()
        instance.associated_files = []
        for approval_file in model.approval_files:
            try:
                # fixme: This is slow because we are doing a ton of queries to find the irb code.
                extra_info = doc_dictionary[
                    approval_file.file_data.file_model.irb_doc_code]
            except:
                extra_info = None
            associated_file = {}
            associated_file['id'] = approval_file.file_data.file_model.id
            if extra_info:
                associated_file['name'] = '_'.join(
                    (extra_info['category1'],
                     approval_file.file_data.file_model.name))
                associated_file['description'] = extra_info['description']
            else:
                associated_file[
                    'name'] = approval_file.file_data.file_model.name
                associated_file['description'] = 'No description available'
            associated_file[
                'name'] = '(' + model.study.primary_investigator_id + ')' + associated_file[
                    'name']
            associated_file[
                'content_type'] = approval_file.file_data.file_model.content_type
            instance.associated_files.append(associated_file)

        return instance
コード例 #11
0
 def create_user(self,
                 uid="dhf8r",
                 email="*****@*****.**",
                 display_name="Hoopy Frood"):
     user = session.query(UserModel).filter(UserModel.uid == uid).first()
     if user is None:
         ldap_user = LdapService.user_info(uid)
         user = UserModel(uid=uid, ldap_info=ldap_user)
         session.add(user)
         session.commit()
     return user
コード例 #12
0
 def test_get_single_user(self):
     user_info = LdapService.user_info("lb3dp")
     self.assertIsNotNone(user_info)
     self.assertEqual("lb3dp", user_info.uid)
     self.assertEqual("Laura Barnes", user_info.display_name)
     self.assertEqual("Laura", user_info.given_name)
     self.assertEqual("*****@*****.**", user_info.email_address)
     self.assertEqual("+1 (434) 924-1723", user_info.telephone_number)
     self.assertEqual(
         "E0:Associate Professor of Systems and Information Engineering",
         user_info.title)
     self.assertEqual("E0:EN-Eng Sys and Environment", user_info.department)
     self.assertEqual("faculty", user_info.affiliation)
     self.assertEqual("Staff", user_info.sponsor_type)
コード例 #13
0
 def __init__(self, model: TaskEventModel, study: StudyModel,
              workflow: WorkflowMetadata):
     self.id = model.id
     self.study = study
     self.workflow = workflow
     self.user_uid = model.user_uid
     self.user_display = LdapService.user_info(model.user_uid).display_name
     self.action = model.action
     self.task_id = model.task_id
     self.task_title = model.task_title
     self.task_name = model.task_name
     self.task_type = model.task_type
     self.task_state = model.task_state
     self.task_lane = model.task_lane
     self.date = model.date
コード例 #14
0
    def test_current_user_status(self):
        self.load_example_data()
        rv = self.app.get('/v1.0/user')
        self.assert_failure(rv, 401)

        rv = self.app.get('/v1.0/user', headers=self.logged_in_headers())
        self.assert_success(rv)

        # User must exist in the mock ldap responses.
        user = UserModel(uid="dhf8r", ldap_info=LdapService.user_info("dhf8r"))
        rv = self.app.get('/v1.0/user',
                          headers=self.logged_in_headers(
                              user, redirect_url='http://omg.edu/lolwut'))
        self.assert_success(rv)
        user_data = json.loads(rv.get_data(as_text=True))
        self.assertTrue(user_data['is_admin'])
コード例 #15
0
    def create_user_with_study_and_workflow(self):

        # clear it all out.
        from example_data import ExampleDataLoader
        ExampleDataLoader.clean_db()

        # Assure some basic models are in place, This is a damn mess.  Our database models need an overhaul to make
        # this easier - better relationship modeling is now critical.
        cat = WorkflowSpecCategoryModel(id=None,
                                        display_name="Approvals",
                                        display_order=0)
        db.session.add(cat)
        db.session.commit()
        self.load_test_spec("top_level_workflow",
                            master_spec=True,
                            category_id=cat.id)
        user = db.session.query(UserModel).filter(
            UserModel.uid == "dhf8r").first()
        if not user:
            ldap = LdapService.user_info('dhf8r')
            user = UserModel(uid="dhf8r", ldap_info=ldap)
            db.session.add(user)
            db.session.commit()
        else:
            for study in db.session.query(StudyModel).all():
                StudyService().delete_study(study.id)

        study = StudyModel(title="My title",
                           status=StudyStatus.in_progress,
                           user_uid=user.uid)
        db.session.add(study)

        self.load_test_spec("random_fact", category_id=cat.id)

        self.assertIsNotNone(study.id)
        workflow = WorkflowModel(workflow_spec_id="random_fact",
                                 study_id=study.id,
                                 status=WorkflowStatus.not_started,
                                 last_updated=datetime.utcnow())
        db.session.add(workflow)
        db.session.commit()
        # Assure there is a master specification, one standard spec, and lookup tables.
        ExampleDataLoader().load_reference_documents()
        return user
コード例 #16
0
    def get_study_associates(study_id):
        """
        gets all associated people for a study from the database
        """
        study = db.session.query(StudyModel).filter(
            StudyModel.id == study_id).first()

        if study is None:
            raise ApiError('study_not_found',
                           'No study found with id = %d' % study_id)

        people = db.session.query(StudyAssociated).filter(
            StudyAssociated.study_id == study_id).all()
        ldap_info = LdapService.user_info(study.user_uid)
        owner = StudyAssociated(uid=study.user_uid,
                                role='owner',
                                send_email=True,
                                access=True,
                                ldap_info=ldap_info)
        people.append(owner)
        return people
コード例 #17
0
 def test_get_user_with_caps(self):
     user_info = LdapService.user_info("LB3DP")
     self.assertIsNotNone(user_info)
     self.assertEqual("lb3dp", user_info.uid)
コード例 #18
0
    def add_approval(study_id, workflow_id, approver_uid):
        """we might have multiple approvals for a workflow, so I would expect this
            method to get called multiple times for the same workflow.  This will
            only add a new approval if no approval already exists for the approver_uid,
            unless the workflow has changed, at which point, it will CANCEL any
            pending approvals and create a new approval for the latest version
            of the workflow."""

        # Find any existing approvals for this workflow.
        latest_approval_requests = db.session.query(ApprovalModel). \
            filter(ApprovalModel.workflow_id == workflow_id). \
            order_by(desc(ApprovalModel.version))

        latest_approver_request = latest_approval_requests.filter(
            ApprovalModel.approver_uid == approver_uid).first()

        # Construct as hash of the latest files to see if things have changed since
        # the last approval.
        workflow = db.session.query(WorkflowModel).filter(
            WorkflowModel.id == workflow_id).first()
        workflow_data_files = FileService.get_workflow_data_files(workflow_id)
        current_data_file_ids = list(data_file.id
                                     for data_file in workflow_data_files)

        if len(current_data_file_ids) == 0:
            raise ApiError(
                "invalid_workflow_approval",
                "You can't create an approval for a workflow that has"
                "no files to approve in it.")

        # If an existing approval request exists and no changes were made, do nothing.
        # If there is an existing approval request for a previous version of the workflow
        # then add a new request, and cancel any waiting/pending requests.
        if latest_approver_request:
            request_file_ids = list(
                file.file_data_id
                for file in latest_approver_request.approval_files)
            current_data_file_ids.sort()
            request_file_ids.sort()
            other_approver = latest_approval_requests.filter(
                ApprovalModel.approver_uid != approver_uid).first()
            if current_data_file_ids == request_file_ids:
                return  # This approval already exists or we're updating other approver.
            else:
                for approval_request in latest_approval_requests:
                    if (approval_request.version
                            == latest_approver_request.version
                            and approval_request.status !=
                            ApprovalStatus.CANCELED.value):
                        approval_request.status = ApprovalStatus.CANCELED.value
                        db.session.add(approval_request)
                version = latest_approver_request.version + 1
        else:
            version = 1

        model = ApprovalModel(study_id=study_id,
                              workflow_id=workflow_id,
                              approver_uid=approver_uid,
                              status=ApprovalStatus.PENDING.value,
                              message="",
                              date_created=datetime.now(),
                              version=version)
        approval_files = ApprovalService._create_approval_files(
            workflow_data_files, model)

        # Check approvals count
        approvals_count = ApprovalModel().query.filter_by(
            study_id=study_id, workflow_id=workflow_id,
            version=version).count()

        db.session.add(model)
        db.session.add_all(approval_files)
        db.session.commit()

        # Send first email
        if approvals_count == 0:
            ldap_service = LdapService()
            pi_user_info = ldap_service.user_info(
                model.study.primary_investigator_id)
            approver_info = ldap_service.user_info(approver_uid)
            # send rrp submission
            mail_result = send_ramp_up_submission_email(
                '*****@*****.**', [pi_user_info.email_address],
                f'{approver_info.display_name} - ({approver_info.uid})')
            if mail_result:
                app.logger.error(mail_result, exc_info=True)
            # send rrp approval request for first approver
            # enhance the second part in case it bombs
            approver_email = [
                approver_info.email_address
            ] if approver_info.email_address else app.config['FALLBACK_EMAILS']
            mail_result = send_ramp_up_approval_request_first_review_email(
                '*****@*****.**', approver_email,
                f'{pi_user_info.display_name} - ({pi_user_info.uid})')
            if mail_result:
                app.logger.error(mail_result, exc_info=True)
コード例 #19
0
 def test_find_missing_user(self):
     try:
         user_info = LdapService.user_info("nosuch")
         self.assertFalse(True, "An API error should be raised.")
     except ApiError as ae:
         self.assertEqual("missing_ldap_record", ae.code)
コード例 #20
0
    def test_study_sponsors_script(self, mock_get):
        mock_get.return_value.ok = True
        mock_get.return_value.text = self.protocol_builder_response(
            'sponsors.json')
        flask.g.user = UserModel(uid='dhf8r')
        app.config['PB_ENABLED'] = True

        self.load_example_data()
        study = session.query(StudyModel).first()
        workflow_spec_model = self.load_test_spec("study_sponsors_associate")
        workflow_model = StudyService._create_workflow_model(
            study, workflow_spec_model)
        WorkflowService.test_spec("study_sponsors_associate")
        processor = WorkflowProcessor(workflow_model)
        processor.do_engine_steps()
        self.assertTrue(processor.bpmn_workflow.is_completed())
        data = processor.next_task().data
        self.assertIn('sponsors', data)
        self.assertIn('out', data)
        print(data['out'])
        dhf8r_info = LdapSchema().dump(LdapService.user_info('dhf8r'))
        lb3dp_info = LdapSchema().dump(LdapService.user_info('lb3dp'))

        self.assertDictEqual(
            {
                'uid': 'dhf8r',
                'role': 'owner',
                'send_email': True,
                'access': True,
                'ldap_info': dhf8r_info
            }, data['out'][1])
        self.assertDictEqual(
            {
                'uid': 'lb3dp',
                'role': 'SuperDude',
                'send_email': False,
                'access': True,
                'ldap_info': lb3dp_info
            }, data['out'][0])
        self.assertDictEqual(
            {
                'uid': 'lb3dp',
                'role': 'SuperDude',
                'send_email': False,
                'access': True,
                'ldap_info': lb3dp_info
            }, data['out2'])
        self.assertDictEqual(
            {
                'uid': 'dhf8r',
                'role': 'owner',
                'send_email': True,
                'access': True,
                'ldap_info': dhf8r_info
            }, data['out3'][1])
        self.assertDictEqual(
            {
                'uid': 'lb3dp',
                'role': 'SuperGal',
                'send_email': False,
                'access': True,
                'ldap_info': lb3dp_info
            }, data['out3'][0])
        self.assertDictEqual(
            {
                'uid': 'lb3dp',
                'role': 'SuperGal',
                'send_email': False,
                'access': True,
                'ldap_info': lb3dp_info
            }, data['out4'])
        self.assertEqual(3, len(data['sponsors']))
コード例 #21
0
 def _run_ldap_query(query, value, limit):
     if value:
         return [LdapSchema().dump(LdapService.user_info(value))]
     else:
         users = LdapService.search_users(query, limit)
     return users