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
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)), )
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), })
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')
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()
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)