def switch_user_privs(email_address, user_type):
    """
    updates a users status
    :param email_address: the email address of the user to update
    :param user_type: regular, curator, admin
    :return: true on success, None upon failure
    """

    is_admin = True if (user_type == "administrator") else False
    is_curator = True if (user_type == "curator") else False

    db = mds.get_db()

    user = db.users.find_one({
        'email_address_lowercase':
        email_address.strip().lower(),
    })

    if user is not None:
        db.users.update_one(
            {'_id': user['_id']},
            {'$set': {
                'administrator': is_admin,
                'curator': is_curator
            }})
        return True
    else:
        return None
Beispiel #2
0
def main():
    db = mds.get_db()
    schema_version = mds.get_schema_version(db)
    if schema_version is not None:
        hit_schema_version = False
        for from_version, conversion_func in SCHEMA_UPGRADE_FUNCTIONS:
            if hit_schema_version or schema_version == from_version:
                hit_schema_version = True
                observed_from_version = mds.get_schema_version(db)
                if observed_from_version != from_version:
                    raise Exception(
                        'Aborting schema upgrade. '
                        'Expected schema version to be {} but observed {}'.
                        format(
                            mds.version_to_str(from_version),
                            mds.version_to_str(observed_from_version),
                        ))

                print('upgrading schema from version:',
                      mds.version_to_str(from_version))
                conversion_func(db)

    mds.init_db(db)
    print(
        'successfully upgraded schema to version:',
        mds.version_to_str(mds.get_schema_version(db)),
    )
Beispiel #3
0
 def _test_get_sample(self):
     """see if the test sample exists"""
     db = mdb.get_db()
     sample = db.samples.find_one({'sample_id': "unit_tester"})
     if sample:
         print("sample id: {}, visibility: {}, owner: {}".format(
             sample['_id'], sample['is_public'], sample['owner']))
     else:
         print("no test sample found!")
def invite_user(email_address, affiliation, db=None):
    '''
    invite regular user
    :param email_address:
    :param affiliation
    :param db:
    :return:
    '''
    if db is None:
        db = mds.get_db()

    email_address = email_address.strip()

    #check and see if user already exists first
    user = db.users.find_one({
        'email_address_lowercase':
        email_address.strip().lower(),
    })

    if user is None:
        ts = '{:%m/%d/%Y %H:%M %p}'.format(datetime.datetime.now())
        password_reset_id = str(uuid4())
        db.users.insert({
            'created': ts,
            'email_address': email_address,
            'email_address_lowercase': email_address.lower(),
            'affiliation': affiliation,
            'administrator': False,
            'password_reset_hash': hash_str(password_reset_id),
        })

        msg_template = \
            '''You have been invited to create a HaploQA account. ''' \
            '''To validate your account and create a password, visit this link: {} ''' \
            '''Please ignore this message if it was sent in error.'''
        msg = MIMEText(
            msg_template.format(
                flask.url_for('validate_reset',
                              password_reset_id=password_reset_id,
                              _external=True)))

        from_addr = noreply_address()
        msg['Subject'] = 'Confirm HaploQA Account'
        msg['From'] = from_addr
        msg['To'] = email_address

        # Send the message via our own SMTP server, but don't include the
        # envelope header.
        s = smtplib.SMTP(HAPLOQA_CONFIG['SMTP_HOST'],
                         HAPLOQA_CONFIG['SMTP_PORT'])
        s.sendmail(from_addr, [email_address], msg.as_string())
        s.quit()
        return True
    else:
        return None
def invite_admin(email_address, db=None):
    """
    invite a new user with the given email address (sends out an invite email via an SMTP server)
    :param email_address: the address of the user to invite
    :param db: the mongo database
    """

    if db is None:
        db = mds.get_db()

    email_address = email_address.strip()

    user = db.users.find_one({
        'email_address_lowercase':
        email_address.strip().lower(),
    })

    if user is None:
        password_reset_id = str(uuid4())
        db.users.insert({
            'email_address': email_address,
            'email_address_lowercase': email_address.lower(),
            'administrator': True,
            'password_reset_hash': hash_str(password_reset_id),
        })

        msg_template = \
            '''You have been invited to create a HaploQA account. ''' \
            '''To validate your account and create a password, visit this link: {} ''' \
            '''Please ignore this message if it was sent in error.'''
        msg = MIMEText(
            msg_template.format(
                flask.url_for('validate_reset',
                              password_reset_id=password_reset_id,
                              _external=True)))

        from_addr = noreply_address()
        msg['Subject'] = 'Confirm HaploQA Account'
        msg['From'] = from_addr
        msg['To'] = email_address

        # Send the message via our own SMTP server, but don't include the
        # envelope header.
        s = smtplib.SMTP(HAPLOQA_CONFIG['SMTP_HOST'],
                         HAPLOQA_CONFIG['SMTP_PORT'])
        s.sendmail(from_addr, [email_address], msg.as_string())
        s.quit()
    else:
        return None
def reset_password(email_address, db=None):
    """
    Reset password for user with the given email address (sends out a reset email via an SMTP
    server). If the user does not follow the link from the email that's sent out
    this should have no effect.
    :param email_address: the address of the user to invite
    :param db: the mongo database
    """

    if db is None:
        db = mds.get_db()

    password_reset_id = str(uuid4())
    user = db.users.find_one_and_update(
        {'email_address_lowercase': email_address.strip().lower()},
        {'$set': {
            'password_reset_hash': hash_str(password_reset_id)
        }})
    if user is None:
        return None

    else:
        msg_template = \
            '''Someone has attempted to reset your password for the HaploQA application. ''' \
            '''To validate this request follow this link: {} ''' \
            '''Please ignore this message if it was sent in error.'''
        msg = MIMEText(
            msg_template.format(
                flask.url_for('validate_reset',
                              password_reset_id=password_reset_id,
                              _external=True)))

        from_addr = noreply_address()
        msg['Subject'] = 'Reset password request for HaploQA'
        msg['From'] = from_addr
        msg['To'] = user['email_address']

        # Send the message via our own SMTP server, but don't include the
        # envelope header.
        s = smtplib.SMTP(HAPLOQA_CONFIG['SMTP_HOST'],
                         HAPLOQA_CONFIG['SMTP_PORT'])
        s.sendmail(from_addr, [user['email_address']], msg.as_string())
        s.quit()
        return True
def remove_user(email_address):
    """
    remove a user from the system
    :param email_address: the email address of the user to remove
    :param db: the database
    :return: true on success, None upon failure
    """

    db = mds.get_db()

    user = db.users.find_one({
        'email_address_lowercase':
        email_address.strip().lower(),
    })

    if user is not None:
        db.users.delete_one({'_id': user['_id']})
        return True
    else:
        return None
def _create_admin(email_address, password, db=None):
    """
    This method is just used to create the first admin user (all of the rest can be
    created by using the "invite admin" functionality of the web interface)
    :param email_address:
    :param password:
    :param db:
    :return:
    """
    if db is None:
        db = mds.get_db()

    email_address = email_address.strip()

    salt = str(uuid4())
    db.users.insert({
        'email_address': email_address,
        'email_address_lowercase': email_address.lower(),
        'administrator': True,
        'salt': salt,
        'password_hash': hash_str(password + salt),
    })
Beispiel #9
0
 def setUpClass(cls):
     tester_email = '*****@*****.**'
     cls._tester_email = tester_email
     db = mdb.get_db()
     # find a user to duplicate
     user = db.users.find_one(({'administrator': False}))
     test_user = user
     uid = ObjectId()
     test_user['_id'] = uid
     test_user['email_address_lowercase'] = tester_email
     test_user['email_address'] = tester_email
     db.users.insert_one(test_user)
     # make the user id available to the tests
     cls._uid = uid
     print("Setup: Created test user {}".format(uid))
     # find a private sample not owned by the test user
     sample = db.samples.find_one({'sample_id': "32C"})
     sample2 = sample
     sample_id = ObjectId()
     print("Setup: test sample id is {}".format(sample_id))
     sample2['_id'] = sample_id
     #make the id available to the tests
     cls._sample_id = sample_id
     sample2['sample_id'] = 'unit_tester'
     sample2['tags'] = ["unit_testing_tag"]
     sample2['is_public'] = True
     sample2['owner'] = tester_email
     db.samples.insert_one(sample2)
     print("Setup: creating test client")
     client = hqa.app.test_client()
     # make the flask test client available for the tests
     cls._client = client
     # make the db connection available for the tests
     cls._db = db
     # edit button html for samples page
     button = '<button id="edit-{}-button" type="button" class="btn btn-primary">'
     cls._edit_sample_button = button.format('sample')
     cls._edit_samples_button = button.format('samples')
Beispiel #10
0
def get_gemm_intens(sample_obj_ids, min_pos_neg_count=4, db=None):
    """
    Extract GEMM intensities from the database. This extracts both the control
    intensities along with intensities for the given sample_obj_ids
    :param sample_obj_ids:
    :param min_pos_neg_count:
    :param db:
    :return:
    """

    if db is None:
        db = mds.get_db()

    # organize GEMM snps by target
    gemm_snps = list(db.snps.find({'engineered_target': {'$exists': True}}))
    gemm_snp_dict = {snp['engineered_target']: [] for snp in gemm_snps}
    for snp in gemm_snps:
        snp['pos_ctrls'] = []
        snp['neg_ctrls'] = []
        snp['non_ctrls'] = []
        gemm_snp_dict[snp['engineered_target']].append(snp)

    # find all pos/neg samples and collect their probe intensities for all of the engineered targets
    # TODO this approach is much more expensive than it should be (we're loading
    # all SNP data for the samples even though we only care about a small subset
    # of SNPs) but I'm just going to go with this for now.
    for sample in db.samples.find(
        {'pos_ctrl_eng_tgts': {
            '$exists': True,
            '$ne': []
        }}):
        for pos_tgt in set(sample['pos_ctrl_eng_tgts']):
            for snp in gemm_snp_dict.get(pos_tgt, []):
                intens_val = _get_gemm_snp_intens(snp, sample)
                snp['pos_ctrls'].append({
                    'sample_obj_id': sample['_id'],
                    'sample_id': sample['sample_id'],
                    'probe_intensity': intens_val
                })

    # and now the same for the negative samples
    for sample in db.samples.find(
        {'neg_ctrl_eng_tgts': {
            '$exists': True,
            '$ne': []
        }}):
        for neg_tgt in set(sample['neg_ctrl_eng_tgts']):
            for snp in gemm_snp_dict.get(neg_tgt, []):
                intens_val = _get_gemm_snp_intens(snp, sample)
                snp['neg_ctrls'].append({
                    'sample_obj_id': sample['_id'],
                    'sample_id': sample['sample_id'],
                    'probe_intensity': intens_val
                })

    # and now the same for test samples
    for sample_obj_id in sample_obj_ids:
        sample = db.samples.find_one({'_id': sample_obj_id})
        for snp in gemm_snps:
            intens_val = _get_gemm_snp_intens(snp, sample)
            snp['non_ctrls'].append({
                'sample_obj_id': sample['_id'],
                'sample_id': sample['sample_id'],
                'probe_intensity': intens_val
            })

    # here we remove any targets that don't meet the minimum count threshold
    for tgt in list(gemm_snp_dict.keys()):
        if not gemm_snp_dict[tgt]:
            del gemm_snp_dict[tgt]
        else:
            # the counts should be the same for all SNPs in the
            # target so we only need to check the first one to
            # see if we're keeping it
            fst_snp = gemm_snp_dict[tgt][0]
            if len(fst_snp['pos_ctrls']) < min_pos_neg_count or len(
                    fst_snp['neg_ctrls']) < min_pos_neg_count:
                del gemm_snp_dict[tgt]

    return gemm_snp_dict
def get_all_users(db=None):
    if db is None:
        db = mds.get_db()
    return db.users.find()
Beispiel #12
0
def merge_h5(h5_filename, match_other_ids):
    """
    Merge data from the given HDF5 file into the database

    :param h5_filename: the name of the HDF5 file containing the data to merge
    :param match_other_ids:
        this indicates that the IDs present are not canonical and should be matched against
        a sample's "other_ids" attribute rather than the canonical "sample_id" attribute
    """
    db = mds.get_db()
    samples = db.samples
    h5_file = h5py.File(h5_filename, 'r')

    platform_tuple_cache = dict()

    def get_platform_tuple(platform_id):
        if platform_id in platform_tuple_cache:
            return platform_tuple_cache[platform_id]
        else:
            tup = mds.within_chr_snp_indices(platform_id, db)
            platform_tuple_cache[platform_id] = tup
            return tup

    samples_grp = h5_file['samples']
    for sample_grp_name in samples_grp:
        sample_grp = samples_grp[sample_grp_name]

        # for attr_name in sample_grp:
        #     print('\t' + attr_name)

        if 'sample_id' in sample_grp:
            h5_sample_id = as_utf8(sample_grp['sample_id'][0])
            matching_samples = list(
                samples.find({'other_ids': h5_sample_id}, {
                    '_id': 1,
                    'sample_id': 1,
                    'platform_id': 1
                }))
            matching_sample_count = len(matching_samples)
            if matching_sample_count == 1:
                print('processing:', h5_sample_id)
                mds_matching_sample = matching_samples[0]

                mds_sample_id = mds_matching_sample['sample_id']
                mds_sample_platform = mds_matching_sample['platform_id']
                h5_sample_platform = as_utf8(sample_grp['platform'][0])
                if h5_sample_platform != mds_sample_platform:
                    print('skipping',
                          h5_sample_id,
                          'due to platform mismatch',
                          file=sys.stderr)
                    continue

                h5_probeset_ids = [
                    as_utf8(x) for x in np.array(sample_grp['probeset_ids'])
                ]
                h5_diplotype_probabilities = np.array(
                    sample_grp['diplotype_probabilities']).transpose().tolist(
                    )
                h5_diplotype_strains = list(
                    map(lambda x: list(map(as_utf8, x)),
                        sample_grp['diplotype_strains']))

                platform_chrs, snp_count_per_chr, snp_chr_indexes = get_platform_tuple(
                    h5_sample_platform)

                # create a per-chromosome 2D list with shape (# platform SNPs, # diplotype strains) for probabilities
                chr_probs = {
                    chrom: [[float('nan')] * len(h5_diplotype_strains)
                            for _ in range(count)]
                    for chrom, count in snp_count_per_chr.items()
                }
                for h5_idx, probeset_id in enumerate(h5_probeset_ids):
                    if probeset_id in snp_chr_indexes:
                        chr_idx = snp_chr_indexes[probeset_id]
                        chr_probs[chr_idx['chromosome']][chr_idx[
                            'index']] = h5_diplotype_probabilities[h5_idx]

                for chr, probs in chr_probs.items():
                    db.diplotype_probabilities.update_one(
                        {
                            'sample_id': mds_sample_id,
                            'chromosome': chr
                        },
                        {
                            '$set': {
                                'sample_id': mds_sample_id,
                                'chromosome': chr,
                                'platform_id': h5_sample_platform,
                                'diplotype_probabilities': probs,
                                'diplotype_strains': h5_diplotype_strains,
                            }
                        },
                        upsert=True,
                    )
            else:
                print('skipping:', h5_sample_id)