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)
Example #2
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)
Example #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
Example #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)
Example #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
Example #6
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
Example #7
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
Example #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
Example #9
0
def login(
        uid=None,
        redirect_url=None,
):
    """
        In non-production environment, provides an endpoint for end-to-end system testing that allows the system
        to simulate logging in as a specific user. In production environment, simply logs user in via single-sign-on
        (SSO) Shibboleth authentication headers.

        Args:
            uid:  Optional[str]
            redirect_url: Optional[str]

        Returns:
            str.  If not on production, returns the frontend auth callback URL, with auth token appended.
            If on production and user is authenticated via SSO, returns the frontend auth callback URL,
            with auth token appended.

        Raises:
            ApiError.  If on production and user is not authenticated, returns a 404 error.
   """

    # ----------------------------------------
    # Shibboleth Authentication Headers
    # ----------------------------------------
    # X-Remote-Cn: Daniel Harold Funk (dhf8r)
    # X-Remote-Sn: Funk
    # X-Remote-Givenname: Daniel
    # X-Remote-Uid: dhf8r
    # Eppn: [email protected]
    # Cn: Daniel Harold Funk (dhf8r)
    # Sn: Funk
    # Givenname: Daniel
    # Uid: dhf8r
    # X-Remote-User: [email protected]
    # X-Forwarded-For: 128.143.0.10
    # X-Forwarded-Host: dev.crconnect.uvadcos.io
    # X-Forwarded-Server: dev.crconnect.uvadcos.io
    # Connection: Keep-Alive

    # If we're in production, override any uid with the uid from the SSO request headers
    if _is_production():
        uid = _get_request_uid(request)
    else:
        if not app.config['TESTING']:
            uid = app.config['DEFAULT_UID']

    if uid:
        app.logger.info("SSO_LOGIN: Full URL: " + request.url)
        app.logger.info("SSO_LOGIN: User Id: " + uid)
        app.logger.info("SSO_LOGIN: Will try to redirect to : " + str(redirect_url))

        ldap_info = LdapService().user_info(uid)

        if ldap_info:
            return _handle_login(ldap_info, redirect_url)

    raise ApiError('404', 'unknown')
Example #10
0
 def get_ldap_dict_if_available(user_id):
     try:
         return LdapSchema().dump(LdapService().user_info(user_id))
     except ApiError as ae:
         app.logger.info(str(ae))
         return {"error": str(ae)}
     except LDAPSocketOpenError:
         app.logger.info("Failed to connect to LDAP Server.")
         return {}
Example #11
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
Example #12
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
Example #13
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
Example #14
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)
Example #15
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
Example #16
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'])
Example #17
0
 def _run_ldap_query(query, limit):
     users = LdapService.search_users(query, limit)
     """Converts the user models into something akin to the
     LookupModel in models/file.py, so this can be returned in the same way 
      we return a lookup data model."""
     user_list = []
     for user in users:
         user_list.append({
             "value":
             user['uid'],
             "label":
             user['display_name'] + " (" + user['uid'] + ")",
             "data":
             user
         })
     return user_list
Example #18
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
Example #19
0
    def update_study_associates(study_id, associates):
        """
        updates the list of associates in the database for a study_id and a list
        of dicts that contains associates
        """
        if study_id is None:
            raise ApiError('study_id not specified',
                           "This function requires the study_id parameter")

        for person in associates:
            if not LdapService.user_exists(person.get('uid',
                                                      'impossible_uid')):
                if person.get('uid', 'impossible_uid') == 'impossible_uid':
                    raise ApiError(
                        'associate with no uid',
                        'One of the associates passed as a parameter doesnt have '
                        'a uid specified')
                raise ApiError(
                    'trying_to_grant_access_to_user_not_found_in_ldap',
                    "You are trying to grant access to "
                    "%s, but that user was not found in "
                    "ldap "
                    "- please check to ensure it is a "
                    "valid uva uid" % person.get('uid'))

        study = db.session.query(StudyModel).filter(
            StudyModel.id == study_id).first()
        if study is None:
            raise ApiError('study_id not found',
                           "A study with id# %d was not found" % study_id)

        db.session.query(StudyAssociated).filter(
            StudyAssociated.study_id == study_id).delete()
        for person in associates:
            newAssociate = StudyAssociated()
            newAssociate.study_id = study_id
            newAssociate.uid = person['uid']
            newAssociate.role = person.get('role', None)
            newAssociate.send_email = person.get('send_email', False)
            newAssociate.access = person.get('access', False)
            session.add(newAssociate)
        session.commit()
Example #20
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
Example #21
0
    def update_study_associate(study_id=None,
                               uid=None,
                               role="",
                               send_email=False,
                               access=False):
        if study_id is None:
            raise ApiError('study_id not specified',
                           "This function requires the study_id parameter")
        if uid is None:
            raise ApiError('uid not specified',
                           "This function requires a uva uid parameter")

        if not LdapService.user_exists(uid):
            raise ApiError(
                'trying_to_grant_access_to_user_not_found_in_ldap',
                "You are trying to grant access to "
                "%s but they were not found in ldap "
                "- please check to ensure it is a "
                "valid uva uid" % uid)
        study = db.session.query(StudyModel).filter(
            StudyModel.id == study_id).first()
        if study is None:
            raise ApiError('study_id not found',
                           "A study with id# %d was not found" % study_id)
        db.session.query(
            StudyAssociated).filter((StudyAssociated.study_id == study_id)
                                    & (StudyAssociated.uid == uid)).delete()

        newAssociate = StudyAssociated()
        newAssociate.study_id = study_id
        newAssociate.uid = uid
        newAssociate.role = role
        newAssociate.send_email = send_email
        newAssociate.access = access
        session.add(newAssociate)
        session.commit()
        return True
Example #22
0
    def start_impersonating(uid=None):
        if not UserService.has_user():
            raise ApiError("logged_out",
                           "You are no longer logged in.",
                           status_code=401)

        if not UserService.user_is_admin():
            raise ApiError("unauthorized",
                           "You do not have permissions to do this.",
                           status_code=403)

        if uid is None:
            raise ApiError("invalid_uid", "Please provide a valid user uid.")

        if UserService.is_different_user(uid):
            # Impersonate the user if the given uid is valid.

            # If the user is not in the User table, add them to it
            ldap_info = LdapService().user_info(uid)
            crc.api.user._upsert_user(ldap_info)

            impersonate_user = session.query(UserModel).filter(
                UserModel.uid == uid).first()

            if impersonate_user is not None:
                g.impersonate_user = impersonate_user

                # Store the uid and user session token.
                session.query(AdminSessionModel).filter(
                    AdminSessionModel.token == g.token).delete()
                session.add(
                    AdminSessionModel(token=g.token,
                                      admin_impersonate_uid=uid))
                session.commit()
            else:
                raise ApiError("invalid_uid", "The uid provided is not valid.")
Example #23
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)
Example #24
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)
Example #25
0
 def test_get_user_with_caps(self):
     user_info = LdapService.user_info("LB3DP")
     self.assertIsNotNone(user_info)
     self.assertEqual("lb3dp", user_info.uid)
Example #26
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']))
Example #27
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