def favourite(): if re.search(r'^\d+$', request.form['vf_id']): vf_id = request.form['vf_id'] if vf_id is None: flash('Cannot mark a variant without id! Please contact us.', 'w3-pale-red') return 'notok' # print(vf_id) # g.user['id'] db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) if request.form['marker'] == 'mark': curs.execute( "INSERT INTO mobiuser_favourite (mobiuser_id, feature_id) VALUES (%s, %s)", (g.user['id'], vf_id) ) else: curs.execute( "DELETE FROM mobiuser_favourite WHERE mobiuser_id = %s AND feature_id = %s", (g.user['id'], vf_id) ) db.commit() close_db() return 'ok' else: flash('Cannot mark a variant without id! Please contact us.', 'w3-pale-red') return 'notok'
def index(): # print(app.config['RUN_MODE']) # check vv md_api_url = '{0}{1}'.format(request.host_url[:-1], url_for('api.check_vv_instance')) vv_instance = json.loads( http.request('GET', md_api_url).data.decode('utf-8')) # count genes db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute(""" SELECT COUNT(DISTINCT(gene_symbol)) AS gene, COUNT(refseq) AS transcript FROM gene WHERE variant_creation = 'ok' """) res = curs.fetchone() if res is None: close_db() error = 'There is a problem with the number of genes.' flash(error, 'w3-pale-red') md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails PostGreSQL error', '<p>There is a problem with the number of genes.\ <br /> in {}</p>'.format(os.path.basename(__file__))), '[MobiDetails - PostGreSQL Error]') else: close_db() return render_template('md/index.html', run_mode=md_utilities.get_running_mode(), nb_genes=res['gene'], nb_isoforms=res['transcript'], vv_instance=vv_instance)
def variant_list(list_name): if len(list_name) < 31 and \ re.search(r'^[\w]+$', list_name): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( """ SELECT a.variant_ids, b.username, a.creation_date, a.list_name FROM variants_groups a, mobiuser b WHERE a.mobiuser_id = b.id AND list_name = %s """, (list_name, )) res = curs.fetchone() # we've got an ARRAY of variants_ids and want to query on them... if res: res_ids_string = "(" for id in res['variant_ids']: res_ids_string += "{}, ".format(id) res_ids_string = res_ids_string[:-2] res_ids_string += ")" # print(res_ids_string) curs.execute(""" SELECT a.id, a.c_name, a.p_name, a.gene_symbol, a.refseq, a.creation_user, a.creation_date, b.username FROM variant_feature a, mobiuser b WHERE a.creation_user = b.id AND a.id IN {0} """.format(res_ids_string)) variants = curs.fetchall() close_db() return render_template('md/variant_multiple.html', variants=variants, unique_url_info=res) else: close_db() return render_template('errors/404.html'), 404 else: return render_template('errors/404.html'), 404
def load_logged_in_user(): user_id = session.get('user_id') if user_id is None: g.user = None else: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute("SELECT * FROM mobiuser WHERE id = %s", (user_id, )) g.user = curs.fetchone() close_db()
def forgot_pass(): if (md_utilities.get_running_mode() == 'maintenance'): return render_template('md/index.html', run_mode=md_utilities.get_running_mode()) if request.method == 'GET': return render_template('auth/forgot_pass.html') elif request.method == 'POST': error = None email = request.form['email'] if not re.search(r'^[a-zA-Z0-9\._%\+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email): error = 'The email address does not look valid.' flash(error, 'w3-pale-red') return render_template('auth/forgot_pass.html') else: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute("SELECT * FROM mobiuser WHERE email = %s", (email, )) user = curs.fetchone() if user is None: close_db() error = """ Your email address {} seems to be unknown by the system. """.format(email) flash(error, 'w3-pale-red') return render_template('auth/forgot_pass.html') # message, mail_object, receiver md_utilities.send_email( md_utilities.prepare_email_html( 'MobiDetails - Reset your password', """ Dear {0}, <p>please follow the link below to reset your MobiDetails password:</p> <p><a href="{1}{2}" title="Reset your MD password"> Reset your MD password</a></p> <p>If you do not know why you receive this email, do not follow the link and please alert [email protected].</p><br /> """.format( user['username'], request.host_url.rstrip('/'), url_for('auth.reset_password', mobiuser_id=user['id'], api_key=user['api_key'], ts=datetime.timestamp(datetime.now()))), False), '[MobiDetails - Password reset]', [email]) flash( """ Please check your e-mail inbox. You should have received a message with a link to reset your password """, 'w3-pale-green') close_db() return render_template('auth/forgot_pass.html') return render_template('md/unknown.html')
def genes(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute(""" SELECT DISTINCT(gene_symbol) FROM gene ORDER BY gene_symbol """) genes = curs.fetchall() close_db() if genes: return render_template('md/genes.html', run_mode=md_utilities.get_running_mode(), genes=genes) else: return render_template('md/unknown.html', run_mode=md_utilities.get_running_mode())
def defgen(): genome_regexp = md_utilities.regexp['genome'] if re.search(r'^\d+$', request.form['vfid']) and \ re.search(rf'^{genome_regexp}$', request.form['genome']): variant_id = request.form['vfid'] genome = request.form['genome'] db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # get all variant_features and gene info curs.execute( "SELECT * FROM variant_feature a, variant b, gene c WHERE \ a.id = b.feature_id AND a.gene_name = c.name AND a.id = '{0}'\ AND b.genome_version = '{1}'".format(variant_id, genome) ) vf = curs.fetchone() file_content = "GENE;VARIANT;A_ENREGISTRER;ETAT;RESULTAT;VARIANT_P;VARIANT_C;ENST;NM;POSITION_GENOMIQUE;CLASSESUR5;CLASSESUR3;COSMIC;RS;REFERENCES;CONSEQUENCES;COMMENTAIRE;CHROMOSOME;GENOME_REFERENCE;NOMENCLATURE_HGVS;LOCALISATION;SEQUENCE_REF;LOCUS;ALLELE1;ALLELE2\r\n" file_content += "{0};{1}.{2}:c.{3};;;;p.{4};c.{3};{5};{1}.{2};{6};;;;rs{7};;{8};;chr{9};{10};chr{9}:g.{11};{12} {13};;;;\r\n".format( vf['gene_name'][0], vf['gene_name'][1], vf['nm_version'], vf['c_name'], vf['p_name'], vf['enst'], vf['pos'], vf['dbsnp_id'], vf['prot_type'], vf['chr'], genome, vf['g_name'], vf['start_segment_type'], vf['start_segment_number'] ) # print(file_content) app_path = os.path.dirname(os.path.realpath(__file__)) file_loc = "defgen/{0}-{1}-{2}{3}-{4}.csv".format(genome, vf['chr'], vf['pos'], vf['pos_ref'], vf['pos_alt']) defgen_file = open("{0}/static/{1}".format(app_path, file_loc), "w") defgen_file.write(file_content) close_db() return render_template('ajax/defgen.html', variant="{0}.{1}:c.{2}".format( vf['gene_name'][1], vf['nm_version'], vf['c_name']), defgen_file=file_loc, genome=genome) else: # close_db() md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails Ajax error', '<p>DefGen file generation failed in {} (no variant_id)</p>'.format( os.path.basename(__file__) ) ), '[MobiDetails - Ajax Error]' ) return '<div class="w3-blue w3-ripple w3-padding-16 w3-large w3-center" style="width:100%">Impossible to create DEFGEN file</div>'
def activate(mobiuser_id, api_key): if (md_utilities.get_running_mode() == 'maintenance'): return render_template('md/index.html', run_mode=md_utilities.get_running_mode()) if isinstance(mobiuser_id, int) and \ isinstance(api_key, str): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( """ SELECT id, api_key, activated FROM mobiuser WHERE id = %s AND api_key = %s """, (mobiuser_id, api_key)) user = curs.fetchone() if user is None: message_body = """ <p>Account activation exception</p><p>Recived API key: {0} and mobiuser_id: {1} from {2} """.format(api_key, mobiuser_id, request.remote_addr) md_utilities.send_error_email( md_utilities.prepare_email_html('MobiDetails error', message_body), '[MobiDetails - Activation Error]') flash( 'API key and user id do not seem to fit. An admin has been warned', 'w3-pale-red') close_db() return render_template('md/index.html') else: if user['activated'] is False and \ user['api_key'] == api_key and \ user['id'] == mobiuser_id: curs.execute( "UPDATE mobiuser SET activated = 't' WHERE \ id = %s AND api_key = %s", (user['id'], user['api_key'])) db.commit() flash( 'Your account has been activated, you may now log in using your email address.', 'w3-pale-green') close_db() return render_template('auth/login.html') return render_template('md/unknown.html')
def api_variant_create_rs(rs_id=None, caller=None, api_key=None): # get params if request.args.get('rs_id') and \ request.args.get('caller') and \ request.args.get('api_key'): rs_id = request.args.get('rs_id', type=str) caller = request.args.get('caller', type=str) api_key = request.args.get('api_key', type=str) elif 'rs_id' in request.form and \ 'caller' in request.form and \ 'api_key' in request.form: rs_id = request.form['rs_id'] caller = request.form['caller'] api_key = request.form['api_key'] else: if caller == 'cli': return jsonify(mobidetails_error='I cannot fetch the right parameters') else: flash('I cannot fetch the right parameters for MD API.', 'w3-pale-red') return redirect(url_for('md.index')) db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # check api key res_check_api_key = md_utilities.check_api_key(db, api_key) if 'mobidetails_error' in res_check_api_key: if caller == 'cli': return jsonify(res_check_api_key) else: flash(res_check_api_key['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) else: g.user = res_check_api_key['mobiuser'] # check caller if md_utilities.check_caller(caller) == 'Invalid caller submitted': if caller == 'cli': return jsonify(mobidetails_error='Invalid caller submitted') else: flash('Invalid caller submitted to the API.', 'w3-pale-red') return redirect(url_for('md.index')) # check rs_id trunc_rs_id = None match_obj = re.search(r'^rs(\d+)$', rs_id) if match_obj: trunc_rs_id = match_obj.group(1) # check if rsid exists curs.execute( "SELECT a.c_name, a.id, b.name[2] as nm, b.nm_version FROM variant_feature a, gene b WHERE a.gene_name = b.name AND a.dbsnp_id = %s", (trunc_rs_id,) ) res_rs = curs.fetchall() if res_rs: vars_rs = {} for var in res_rs: current_var = '{0}.{1}:c.{2}'.format(var['nm'], var['nm_version'], var['c_name']) vars_rs[current_var] = { 'mobidetails_id': var['id'], 'url': '{0}{1}'.format( request.host_url[:-1], url_for('md.variant', variant_id=var['id']) ) } if caller == 'cli': return jsonify(vars_rs) else: if len(res_rs) == 1: return redirect(url_for('md.variant', variant_id=res_rs['id'])) else: return redirect(url_for('md.variant_multiple', vars_rs=vars_rs)) # use putalyzer to get HGVS noemclatures http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) mutalyzer_url = "{0}getdbSNPDescriptions?rs_id={1}".format( md_utilities.urls['mutalyzer_api_json'], rs_id ) # returns sthg like # ["NC_000011.10:g.112088901C>T", "NC_000011.9:g.111959625C>T", "NG_012337.3:g.7055C>T", "NM_003002.4:c.204C>T", "NM_003002.3:c.204C>T", "NM_001276506.2:c.204C>T", "NM_001276506.1:c.204C>T", "NR_077060.2:n.239C>T", "NR_077060.1:n.288C>T", "NM_001276504.2:c.87C>T", "NM_001276504.1:c.87C>T", "NG_033145.1:g.2898G>A"] try: mutalyzer_data = json.loads(http.request('GET', mutalyzer_url).data.decode('utf-8')) except Exception: close_db() if caller == 'cli': return jsonify(mobidetails_error='Mutalyzer did not return any value for the variant {}.'.format(rs_id)) else: flash('Mutalyzer did not return any value for the variant {}'.format(rs_id), 'w3-pale-red') return redirect(url_for('md.index')) # print(mutalyzer_data) md_response = {} # md_nm = list of NM recorded in MD, to be sure not to consider unexisting NM acc no # md_nm = [] hgvs_nc = [] gene_names = [] # can_nm = None for hgvs in mutalyzer_data: # works for exonic variants because mutalyzer returns no NM for intronic variants variant_regexp = md_utilities.regexp['variant'] # match_nm = re.search(rf'^(NM_\d+)\.\d+:c\.({variant_regexp})$', hgvs) # if match_nm: # current_nm = match_nm.group(1) # # get list of NM recorded in MD # if not md_nm: # curs.execute( # "SELECT name[2] as nm FROM gene WHERE name[1] = (SELECT name[1] FROM gene WHERE name[2] = %s)", # (current_nm,) # ) # res_nm_all = curs.fetchall() # if res_nm_all: # for nm in res_nm_all: # md_nm.append(nm[0]) # if current_nm in md_nm: # # check if variant with same name already recorded => at least we do not query multiple times for these # curs.execute( # "SELECT id FROM variant_feature WHERE dbsnp_id = %s AND c_name = %s", # (trunc_rs_id, match_nm.group(2),) # ) # res_known = curs.fetchall() # if not res_known: # # get gene and MD current NM version # curs.execute( # "SELECT nm_version FROM gene WHERE name[2] = %s", # (current_nm,) # ) # res_nm = curs.fetchone() # if res_nm: # md_api_url = '{0}{1}'.format(request.host_url[:-1], url_for('api.api_variant_create')) # variant_chgvs = '{0}.{1}:c.{2}'.format(current_nm, res_nm['nm_version'], match_nm.group(2)) # data = { # 'variant_chgvs': urllib.parse.quote(variant_chgvs), # 'caller': 'cli', # 'api_key': api_key # } # try: # md_response[variant_chgvs] = json.loads(http.request('POST', md_api_url, headers=md_utilities.api_agent, fields=data).data.decode('utf-8')) # except Exception: # md_response[variant_chgvs] = {'mobidetails_error': 'MobiDetails returned an unexpected error for your request {0}: {1}'.format(rs_id, variant_chgvs)} # else: # continue # else: # continue # else: # continue # intronic variant? # we need HGVS genomic to launch the API but also the gene - got from NG # f-strings usage https://stackoverflow.com/questions/6930982/how-to-use-a-variable-inside-a-regular-expression # https://www.python.org/dev/peps/pep-0498/ match_nc = re.search(rf'^(NC_0000\d{{2}}\.\d{{1,2}}):g\.({variant_regexp})$', hgvs) if match_nc and \ not md_response: # if hg38, we keep it in a variable that can be useful later curs.execute( "SELECT name, genome_version FROM chromosomes WHERE ncbi_name = %s", (match_nc.group(1),) ) res_chr = curs.fetchone() if res_chr and \ res_chr['genome_version'] == 'hg38': hgvs_nc.append(hgvs) # look for gene name positions = md_utilities.compute_start_end_pos(match_nc.group(2)) # print("SELECT a.name[1] as hgnc FROM gene a, segment b WHERE a.name = b.gene_name AND a.chr = {0} AND {1} BETWEEN SYMMETRIC # b.segment_start AND b.segment_end".format(res_chr['name'], positions[0])) if not gene_names: curs.execute( "SELECT DISTINCT(a.name[1]) as hgnc FROM gene a, segment b WHERE a.name = b.gene_name AND a.chr = %s AND %s BETWEEN SYMMETRIC b.segment_start AND b.segment_end", (res_chr['name'], positions[0]) ) res_gene = curs.fetchall() if res_gene: for hgnc_name in res_gene: gene_names.append(hgnc_name[0]) # match_ng = re.search(rf'^(NG_\d+)\.\d+:g\.{variant_regexp}$', hgvs) # if match_ng and \ # not gene_name and \ # not md_response: # # we need to get the gene name that can be useful later # curs.execute( # "SELECT name[1] as hgnc FROM gene WHERE ng LIKE %s AND canonical = 't'", # ('{}%'.format(match_ng.group(1)),) # ) # res_gene = curs.fetchone() # if res_gene: # gene_name = res_gene['hgnc'] else: continue # do we have an intronic variant? if hgvs_nc and \ gene_names: # and \ # not md_response: md_api_url = '{0}{1}'.format(request.host_url[:-1], url_for('api.api_variant_g_create')) for var_hgvs_nc in hgvs_nc: for gene_hgnc in gene_names: data = { 'variant_ghgvs': urllib.parse.quote(var_hgvs_nc), 'gene_hgnc': gene_hgnc, 'caller': 'cli', 'api_key': api_key } try: # print('{0}-{1}'.format(var_hgvs_nc, gene_hgnc)) md_response[var_hgvs_nc] = json.loads(http.request('POST', md_api_url, headers=md_utilities.api_agent, fields=data).data.decode('utf-8')) except Exception as e: md_response[var_hgvs_nc] = {'mobidetails_error': 'MobiDetails returned an unexpected error for your request {0}: {1}'.format(rs_id, var_hgvs_nc)} md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', '<p>Error with MDAPI file writing for {0} ({1})<br /> - from {2} with args: {3}</p>'.format( rs_id, var_hgvs_nc, os.path.basename(__file__), e.args ) ), '[MobiDetails - MDAPI Error]' ) if md_response: if caller == 'cli': return jsonify(md_response) else: if len(md_response) == 1: for var in md_response: if 'mobidetails_error' in md_response[var]: flash(md_response[var]['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) return redirect(url_for('md.variant', variant_id=md_response[var]['mobidetails_id'])) else: return render_template('md/variant_multiple.html', vars_rs=md_response) # md_utilities.api_end_according_to_caller( # caller=caller, # message='Using Mutalyzer, we did not find any suitable variant corresponding to your request {}'.format(rs_id), # url=url_for('md.index') # ) if caller == 'cli': return jsonify(mobidetails_error='Using Mutalyzer, we did not find any suitable variant corresponding to your request {}'.format(rs_id)) else: flash('Using <a href="https://www.mutalyzer.nl/snp-converter?rs_id={0}", target="_blank">Mutalyzer</a>, we did not find any suitable variant corresponding to your request {0}'.format(rs_id), 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Invalid rs id provided') else: flash('Invalid rs id provided', 'w3-pale-red') return redirect(url_for('md.index'))
def api_variant_g_create(variant_ghgvs=None, gene=None, caller=None, api_key=None): # get params if request.args.get('variant_ghgvs') and \ request.args.get('gene_hgnc') and \ request.args.get('caller') and \ request.args.get('api_key'): variant_ghgvs = request.args.get('variant_ghgvs', type=str) gene = request.args.get('gene_hgnc', type=str) caller = request.args.get('caller', type=str) api_key = request.args.get('api_key', type=str) elif 'variant_ghgvs' in request.form and \ 'gene_hgnc' in request.form and \ 'caller' in request.form and \ 'api_key' in request.form: variant_ghgvs = request.form['variant_ghgvs'] gene = request.form['gene_hgnc'] caller = request.form['caller'] api_key = request.form['api_key'] else: if caller == 'cli': return jsonify(mobidetails_error='I cannot fetch the right parameters') else: flash('I cannot fetch the right parameters', 'w3-pale-red') return redirect(url_for('md.index')) if variant_ghgvs is not None and \ gene is not None and \ caller is not None and \ api_key is not None: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # mobiuser_id = None res_check_api_key = md_utilities.check_api_key(db, api_key) if 'mobidetails_error' in res_check_api_key: if caller == 'cli': return jsonify(res_check_api_key) else: flash(res_check_api_key['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) else: g.user = res_check_api_key['mobiuser'] if md_utilities.check_caller(caller) == 'Invalid caller submitted': if caller == 'cli': return jsonify(mobidetails_error='Invalid caller submitted') else: flash('Invalid caller submitted to API.', 'w3-pale-red') return redirect(url_for('md.index')) # check gene exists curs.execute( "SELECT name, nm_version FROM gene WHERE name[1] = %s AND canonical = 't'", (gene,) ) res_gene = curs.fetchone() if res_gene: variant_regexp = md_utilities.regexp['variant'] match_object = re.search(rf'^([Nn][Cc]_0000\d{{2}}\.\d{{1,2}}):g\.({variant_regexp})', urllib.parse.unquote(variant_ghgvs)) if match_object: ncbi_chr, g_var = match_object.group(1), match_object.group(2) # 1st check hg38 curs.execute( "SELECT * FROM chromosomes WHERE ncbi_name = %s", (ncbi_chr,) ) res = curs.fetchone() if res and \ res['genome_version'] == 'hg38': genome_version, chrom = res['genome_version'], res['name'] # check if variant exists curs.execute( "SELECT feature_id FROM variant WHERE genome_version = %s AND g_name = %s AND chr = %s", (genome_version, g_var, chrom) ) res = curs.fetchone() if res: if caller == 'cli': return jsonify( mobidetails_id=res['feature_id'], url='{0}{1}'.format( request.host_url[:-1], url_for('md.variant', variant_id=res['feature_id']) ) ) else: return redirect(url_for('md.variant', variant_id=res['feature_id'])) else: # creation vv_base_url = md_utilities.get_vv_api_url() if not vv_base_url: close_db() if caller == 'cli': return jsonify(mobidetails_error='Variant Validator looks down!.') else: flash('Variant Validator looks down!.', 'w3-pale-red') return redirect(url_for('md.index')) vv_url = "{0}VariantValidator/variantvalidator/GRCh38/{1}/all?content-type=application/json".format( vv_base_url, variant_ghgvs ) # print(vv_url) # vv_key_var = "{0}.{1}:c.{2}".format(acc_no, acc_version, new_variant) try: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) vv_data = json.loads(http.request('GET', vv_url).data.decode('utf-8')) except Exception: close_db() if caller == 'cli': return jsonify(mobidetails_error='Variant Validator did not return any value for the variant {}.'.format(urllib.parse.unquote(variant_ghgvs))) else: try: flash('There has been a issue with the annotation of the variant via VariantValidator. \ The error is the following: {}'.format(vv_data['validation_warning_1']['validation_warnings']), 'w3-pale-red') except Exception: flash('There has been a issue with the annotation of the variant via VariantValidator. \ Sorry for the inconvenience. You may want to try directly in MobiDetails.', 'w3-pale-red') return redirect(url_for('md.index')) # look for gene acc # # print(vv_data) new_variant = None vv_key_var = None res_gene_non_can_list = [] # we still need a list of non canonical NM for the gene of interest curs.execute( "SELECT name, nm_version FROM gene WHERE name[1] = %s AND canonical = 'f'", (gene,) ) res_gene_non_can = curs.fetchall() for transcript in res_gene_non_can: res_gene_non_can_list.append('{0}.{1}'.format(transcript['name'][1], transcript['nm_version'])) for key in vv_data.keys(): variant_regexp = md_utilities.regexp['variant'] match_obj = re.search(rf'^([Nn][Mm]_\d+)\.(\d{{1,2}}):c\.({variant_regexp})', key) if match_obj: new_variant = match_obj.group(3) # print("{0}-{1}-{2}-{3}".format(match_obj.group(1), res_gene['name'][1], match_obj.group(2), res_gene['nm_version'])) if match_obj.group(1) == res_gene['name'][1] and \ str(match_obj.group(2)) == str(res_gene['nm_version']): # treat canonical as priority vv_key_var = "{0}.{1}:c.{2}".format(match_obj.group(1), match_obj.group(2), match_obj.group(3)) break elif not vv_key_var: # take into account non canonical isoforms # print('{0}.{1}'.format(match_obj.group(1), match_obj.group(2))) # print(res_gene_non_can_list) if '{0}.{1}'.format(match_obj.group(1), match_obj.group(2)) in res_gene_non_can_list: vv_key_var = "{0}.{1}:c.{2}".format(match_obj.group(1), match_obj.group(2), match_obj.group(3)) if vv_key_var: # print(vv_key_var) creation_dict = md_utilities.create_var_vv( vv_key_var, res_gene['name'][0], res_gene['name'][1], 'c.{}'.format(new_variant), new_variant, res_gene['nm_version'], vv_data, 'api', db, g ) if caller == 'cli': return jsonify(creation_dict) else: return redirect(url_for('md.variant', variant_id=creation_dict['mobidetails_id'])) else: if caller == 'cli': return jsonify(mobidetails_error='Could not create variant {} (possibly considered as intergenic).'.format(urllib.parse.unquote(variant_ghgvs)), variant_validator_output=vv_data) else: try: flash('There has been a issue with the annotation of the variant via VariantValidator. \ The error is the following: {}'.format(vv_data['validation_warning_1']['validation_warnings']), 'w3-pale-red') except Exception: flash('There has been a issue with the annotation of the variant via VariantValidator. \ Sorry for the inconvenience. You may want to try directly in MobiDetails.', 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Unknown chromosome {} submitted or bad genome version (hg38 only)'.format(ncbi_chr)) else: flash('The submitted chromosome or genome version looks corrupted (hg38 only).', 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Malformed query {}'.format(urllib.parse.unquote(variant_ghgvs))) else: flash('The query seems to be malformed: {}.'.format(urllib.parse.unquote(variant_ghgvs)), 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Unknown gene {} submitted'.format(gene)) else: flash('Unknown gene {} submitted'.format(gene), 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Invalid parameters') else: flash('The submitted parameters looks invalid!!!', 'w3-pale-red') return redirect(url_for('md.index'))
def file_upload(): if request.method == 'POST': if request.files: uploaded_file = request.files['file'] if uploaded_file.filename == "": flash('No filename.', 'w3-pale-red') return redirect(request.url) if allowed_file(uploaded_file.filename): # filename = secure_filename(uploaded_file.filename) lines = uploaded_file.read().decode().replace( '\r\n', '\n').replace('\r', '\n').split('\n') # print(lines) # ok we've got the file # get user API key or use MD db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) # headers header = md_utilities.api_agent result = [] api_key = md_utilities.get_api_key(g, curs) if api_key is None: flash('There is an issue in obtaining an API key') return redirect(request.url) # we need to check the format and send a proper query to the API for line in lines: # print('-{}-'.format(line)) # cDNA format md_response = [] if re.search(r'^#', line) or \ line == '': continue line = line.replace(' ', '') variant_regexp = md_utilities.regexp['variant'] match_obj_c = re.search( rf'^([Nn][Mm]_\d+)\.(\d+):(c\.{variant_regexp})$', line) if match_obj_c: # check NM number and version curs.execute( "SELECT nm_version FROM gene WHERE name[2] = %s", (match_obj_c.group(1), )) res_nm = curs.fetchone() if res_nm: # send var to api md_api_url = '{0}{1}'.format( request.host_url[:-1], url_for('api.api_variant_create')) # md_api_url = '{0}/api/variant/create'.format(md_api_base_url) # print(md_api_url) data = { 'variant_chgvs': urllib.parse.quote('{0}.{1}:{2}'.format( match_obj_c.group(1), match_obj_c.group(2), match_obj_c.group(3))), 'caller': 'cli', 'api_key': api_key } try: md_response = json.loads( http.request( 'POST', md_api_url, headers=header, fields=data).data.decode('utf-8')) # print(md_response) result.append({ 'variant': line, 'id': md_response['mobidetails_id'], 'url': md_response['url'] }) except Exception: if 'mobidetails_error' in md_response: result.append({ 'variant': line, 'error': md_response['mobidetails_error'] }) else: result.append({ 'variant': line, 'error': 'MDAPI call failed' }) else: result.append({ 'variant': line, 'error': 'Unknown NCBI NM accession number' }) continue # genomic format match_obj_g = re.search( rf'^([Nn][Cc]_\d+\.\d+:g\.{variant_regexp});([\w-]+)$', line) if match_obj_g: # check NM number and version curs.execute( "SELECT nm_version FROM gene WHERE name[1] = %s", (match_obj_g.group(2), )) res_nm = curs.fetchone() if res_nm: # send var to api md_api_url = '{0}{1}'.format( request.host_url[:-1], url_for('api.api_variant_g_create')) # print(md_api_url) data = { 'variant_ghgvs': urllib.parse.quote(match_obj_g.group(1)), 'gene_hgnc': match_obj_g.group(2), 'caller': 'cli', 'api_key': api_key } try: md_response = json.loads( http.request( 'POST', md_api_url, headers=header, fields=data).data.decode('utf-8')) # print(md_response) result.append({ 'variant': line, 'id': md_response['mobidetails_id'] }) except Exception: if 'variant_validator_output' in md_response and \ 'validation_warning_1' in md_response['variant_validator_output'] and \ 'validation_warnings' in md_response['variant_validator_output']['validation_warning_1']: result.append({ 'variant': line, 'error': md_response['variant_validator_output'] ['validation_warning_1'] ['validation_warnings'][0] }) elif 'mobidetails_error' in md_response: result.append({ 'variant': line, 'error': md_response['mobidetails_error'] }) v else: result.append({ 'variant': line, 'error': 'MDAPI call failed' }) else: result.append({ 'variant': line, 'error': 'Unknown gene' }) continue if re.search(rf'^rs\d+$', line): md_api_url = '{0}{1}'.format( request.host_url[:-1], url_for('api.api_variant_create_rs')) # md_api_url = '{0}/api/variant/create'.format(md_api_base_url) # print(md_api_url) data = { 'rs_id': line, 'caller': 'cli', 'api_key': api_key } try: md_response = json.loads( http.request('POST', md_api_url, headers=header, fields=data).data.decode('utf-8')) for var in md_response: print(md_response[var]) if 'mobidetails_error' in md_response[var]: result.append({ 'variant': '{0} - {1}'.format(line, var), 'error': md_response[var]['mobidetails_error'] }) elif var == 'mobidetails_error': result.append({ 'variant': line, 'error': md_response[var] }) else: result.append({ 'variant': '{0} - {1}'.format(line, var), 'id': md_response[var]['mobidetails_id'], 'url': md_response[var]['url'] }) except Exception: result.append({ 'variant': line, 'error': 'MDAPI call failed' }) continue result.append({'variant': line, 'error': 'Bad format'}) close_db() flash('File correctly uploaded', 'w3-pale-green') if g.user: # send an email # print(g.user) result_list = '' for resul in result: result_list = '{0}<li>{1}'.format( result_list, resul['variant']) if 'error' in resul: result_list = '{0} - {1}</li>'.format( result_list, resul['error']) else: result_list = '{0} - <a href="{1}{2}">success</a></li>'.format( result_list, request.host_url[:-1], url_for('md.variant', variant_id=resul['id'])) message = 'Dear {0},<br /><p>Your batch job returned the following results:</p><ul>{1}</ul><p>You can have a direct acces to the successfully annotated variants at your <a href="{2}{3}">profile page</a>.'.format( g.user['username'], result_list, request.host_url[:-1], url_for('auth.profile', mobiuser_id=0)) md_utilities.send_email( md_utilities.prepare_email_html( 'MobiDetails - Batch job', message, False), '[MobiDetails - Batch job]', [g.user['email']]) return render_template('md/variant_multiple.html', upload=result) else: flash('That file extension is not allowed', 'w3-pale-red') return redirect(request.url) return render_template("upload/upload_form.html")
def gene(gene_symbol=None): if gene_symbol is None: return render_template('md/unknown.html', query='No gene provided') gene_match_obj = re.search(r'^([\w-]+)$', gene_symbol) if not gene_match_obj: return render_template('md/unknown.html', query=gene_symbol) gene_symbol = gene_match_obj.group(1) db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # main isoform? now canonical is stored in db curs.execute( """ SELECT * FROM gene WHERE gene_symbol = %s AND canonical = 't' """, (gene_symbol, )) main = curs.fetchone() if main: curs.execute(""" SELECT * FROM gene WHERE gene_symbol = %s ORDER BY number_of_exons DESC """, (gene_symbol, )) # get all isoforms result_all = curs.fetchall() num_iso = len(result_all) # get metadome json? enst_ver = {} # if not json metadome file on filesystem, create it in # radboud server, then next time get it - it will then be # available for future requests # if we have the main => next step if not os.path.isfile('{0}{1}.json'.format( md_utilities.local_files['metadome']['abs_path'], main['enst'])): for gene in result_all: if not os.path.isfile('{0}{1}.json'.format( md_utilities.local_files['metadome']['abs_path'], gene['enst'])): if gene['enst'] not in enst_ver: # get enst versions in a dict metad_ts = None try: metad_ts = json.loads( http.request( 'GET', '{0}get_transcripts/{1}'.format( md_utilities.urls['metadome_api'], gene['gene_symbol'])).data.decode( 'utf-8')) if metad_ts is not None and \ 'trancript_ids' in metad_ts: for ts in metad_ts['trancript_ids']: if ts['has_protein_data']: match_obj = re.search( r'^(ENST\d+)\.\d', ts['gencode_id']) enst_ver[match_obj.group( 1)] = ts['gencode_id'] except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', """ <p>MetaDome first block code failed for gene {0} ({1})<br /> - from {2} with args: {3}</p> """.format(gene_symbol, gene['enst'], os.path.basename(__file__), e.args)), '[MobiDetails - API Error]') break # we check if data exist at metadome # we have a set of metadome transcripts for enst in enst_ver: # print('enst: {}'.format(enst_ver[enst])) # print('--{}--'.format(app.debug)) # print(json.dumps({'transcript_id': enst_ver[enst]})) if not os.path.isfile('{0}{1}.json'.format( md_utilities.local_files['metadome']['abs_path'], enst)): metad_data = None try: metad_data = json.loads( http.request( 'GET', '{0}status/{1}/'.format( md_utilities.urls['metadome_api'], enst_ver[enst])).data.decode('utf-8')) except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', """ <p>MetaDome second block code failed for gene {0} ({1})<br /> - from {2} with args: {3}</p> """.format(gene_symbol, enst, os.path.basename(__file__), e.args)), '[MobiDetails - API Error]') if metad_data is not None: if metad_data['status'] == 'PENDING': # get transcript_ids ?? coz of the version number # send request to build visualization to metadome vis_request = None # find out how to get app object try: # enst_metadome = {transcript_id: enst_ver[enst]} header = md_utilities.api_agent header['Content-Type'] = 'application/json' vis_request = json.loads( http.request( 'POST', '{0}submit_visualization/'.format( md_utilities.urls['metadome_api']), # headers={'Content-Type': 'application/json'}, headers=header, body=json.dumps({ 'transcript_id': enst_ver[enst] })).data.decode('utf-8')) if not app.debug: app.logger.info( '{} submitted to metadome'.format( vis_request['transcript_id'])) else: print('{} submitted to metadome'.format( vis_request['transcript_id'])) except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', """ <p>Error with metadome submission for {0} ({1})<br /> - from {2} with args: {3}</p> """.format(gene_symbol, enst, os.path.basename(__file__), e.args)), '[MobiDetails - API Error]') # print('Error with metadome # submission for {}'.format(enst)) elif metad_data['status'] == 'SUCCESS': # get_request = None try: get_request = json.loads( http.request( 'GET', '{0}result/{1}/'.format( md_utilities.urls['metadome_api'], enst_ver[enst])).data.decode('utf-8')) # copy in file system with open('{0}{1}.json'.format( md_utilities.local_files['metadome'] ['abs_path'], enst), "w", encoding='utf-8') as metad_file: json.dump(get_request, metad_file, ensure_ascii=False, indent=4) if not app.debug: app.logger.info( 'saving metadome {} into local file system' .format(enst_ver[enst])) else: print( 'saving metadome {} into local file system' .format(enst_ver[enst])) except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', """ <p>Error with metadome file writing for {0} ({1})<br /> - from {2} with args: {3}</p> """.format(gene_symbol, enst, os.path.basename(__file__), e.args)), '[MobiDetails - API Error]') # print('error saving metadome json # file for {}'.format(enst)) if result_all is not None: # get annotations curs.execute( """ SELECT * FROM gene_annotation WHERE gene_symbol = %s """, (gene_symbol, )) annot = curs.fetchone() if annot is None: annot = {'nognomad': 'No values in gnomAD'} curs.execute( """ SELECT MAX(prot_size) AS size FROM gene WHERE gene_symbol = %s """, (gene_symbol, )) res_size = curs.fetchone() # get refseq select, mane, etc from vv json file no_vv_file = 0 try: json_file = open( '{0}{1}.json'.format( # lgtm [py/path-injection] md_utilities.local_files['variant_validator'] ['abs_path'], gene_symbol)) except IOError: no_vv_file = 1 if no_vv_file == 0: transcript_road_signs = {} vv_json = json.load(json_file) if 'error' in vv_json \ or ('message' in vv_json and vv_json['message'] == 'Internal Server Error'): no_vv_file = 1 curs.execute( """ UPDATE gene SET variant_creation = 'not_in_vv_json' WHERE gene_symbol = %s AND variant_creation <> 'not_in_vv_json' """, (gene_symbol, )) db.commit() curs.execute(""" SELECT * FROM gene WHERE gene_symbol = %s ORDER BY number_of_exons DESC """, (gene_symbol, )) # get all isoforms result_all = curs.fetchall() num_iso = len(result_all) if no_vv_file == 0: for vv_transcript in vv_json['transcripts']: for res in result_all: # need to check vv isoforms against MD isoforms to keep only relevant ones if vv_transcript['reference'] == res['refseq']: if 'mane_select' in vv_transcript['annotations'] and \ 'mane_plus_clinical' in vv_transcript['annotations'] and \ 'refseq_select' in vv_transcript['annotations']: transcript_road_signs[res['refseq']] = { 'mane_select': vv_transcript['annotations'] ['mane_select'], 'mane_plus_clinical': vv_transcript['annotations'] ['mane_plus_clinical'], 'refseq_select': vv_transcript['annotations'] ['refseq_select'] } # print(transcript_road_signs) close_db() clingen_criteria_specification_id = md_utilities.get_clingen_criteria_specification_id( gene_symbol) return render_template('md/gene.html', run_mode=md_utilities.get_running_mode(), urls=md_utilities.urls, gene_symbol=gene_symbol, num_iso=num_iso, main_iso=main, res=result_all, annotations=annot, max_prot_size=res_size['size'], clingen_criteria_specification_id= clingen_criteria_specification_id, transcript_road_signs=transcript_road_signs) else: close_db() return render_template('md/unknown.html', run_mode=md_utilities.get_running_mode(), query=gene_symbol) else: close_db() return render_template('md/unknown.html', run_mode=md_utilities.get_running_mode(), query=gene_symbol)
def search_engine(): # print("--{}--".format(request.form['search'])) query_engine = request.form['search'] # query_engine = query_engine.upper() error = None variant_regexp = md_utilities.regexp['variant'] variant_regexp_flexible = md_utilities.regexp['variant_flexible'] amino_acid_regexp = md_utilities.regexp['amino_acid'] nochr_captured_regexp = md_utilities.regexp['nochr_captured'] ncbi_transcript_regexp = md_utilities.regexp['ncbi_transcript'] vcf_str_regexp = md_utilities.regexp['vcf_str'] if query_engine is not None and \ query_engine != '': pattern = '' query_type = '' sql_table = 'variant_feature' col_names = 'id, c_name, gene_symbol, refseq, p_name' semaph_query = 0 query_engine = re.sub(r'\s', '', query_engine) query_engine = re.sub(r"'", '', query_engine) if re.search(r'^last$', query_engine): # get variant annotated the last 7 days if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute(""" SELECT a.id, a.c_name, a.p_name, a.gene_symbol, a.refseq, a.creation_user, a.creation_date, b.username FROM variant_feature a, mobiuser b WHERE a.creation_user = b.id AND a.creation_date > CURRENT_DATE - 7 ORDER BY creation_date DESC """) variants = curs.fetchall() close_db() return render_template('md/variant_multiple.html', variants=variants) match_object = re.search( rf'^([Gg]*[rR]*[hHcC][gGhH][13][978])[:_-]({vcf_str_regexp})$', query_engine) # match_object = re.search(rf'^{vcf_str_regexp}$', query_engine) if match_object: genome_version = md_utilities.translate_genome_version( match_object.group(1)) if genome_version != 'wrong_genome_input': if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) api_key = md_utilities.get_api_key(g, curs) if api_key is not None: close_db() return redirect(url_for('api.api_create_vcf_str', genome_version=genome_version, vcf_str=match_object.group(2), caller='browser', api_key=api_key), code=307) else: error = 'Your genome version looks suspicious ({}). Supported are hg19, hg38, GRCh37, GRCh38'.format( match_object.group(1)) else: match_object = re.search(rf'^{vcf_str_regexp}$', query_engine) if match_object: if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) api_key = md_utilities.get_api_key(g, curs) if api_key is not None: close_db() return redirect(url_for('api.api_create_vcf_str', vcf_str=query_engine, caller='browser', api_key=api_key), code=307) match_object = re.search( rf'^([{amino_acid_regexp}]{{1}})(\d+)([{amino_acid_regexp}\*]{{1}})$', query_engine) # e.g. R34X if match_object: confusing_genes = [ 'C1R', 'C8G', 'S100G', 'F11R', 'C1S', 'A2M', 'C1D', 'S100P', 'F2R', 'C8A', 'C4A' ] # list of genes that look like the query if query_engine.upper() in confusing_genes: sql_table = 'gene' query_type = 'gene_symbol' col_names = 'gene_symbol' pattern = query_engine.upper() else: query_type = 'p_name' pattern = md_utilities.one2three_fct(query_engine) elif re.search(r'^rs\d+$', query_engine): query_type = 'dbsnp_id' match_object = re.search(r'^rs(\d+)$', query_engine) pattern = match_object.group(1) elif re.search(r'^p\..+', query_engine): query_type = 'p_name' var = md_utilities.clean_var_name(query_engine) match_object = re.search(r'^(\w{1})(\d+)([\w\*]{1})$', var) # e.g. p.R34X match_object_inter = re.search(r'^(\w{1})(\d+)([d][ue][pl])', var) # e.g. p.L34del match_object_long = re.search(r'^(\w{1})(\d+_)(\w{1})(\d+.+)$', var) # e.g. p.R34_E68del if match_object or match_object_inter or match_object_long: pattern = md_utilities.one2three_fct(var) else: if re.search('X', var): pattern = re.sub(r'X', 'Ter', var) elif re.search(r'\*', var): pattern = re.sub(r'\*', 'Ter', var) else: pattern = var elif re.search(rf'^{ncbi_transcript_regexp}:c\.{variant_regexp}$', query_engine) or \ re.search(rf'^{ncbi_transcript_regexp}\([A-Za-z0-9-]+\):c\.{variant_regexp}$', query_engine): # NM acc_no variant # f-strings usage https://stackoverflow.com/questions/6930982/how-to-use-a-variable-inside-a-regular-expression # API call if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) api_key = md_utilities.get_api_key(g, curs) match_obj = re.search( rf'^({ncbi_transcript_regexp})\(*[A-Za-z0-9-]*\)*(:c\.{variant_regexp})$', query_engine) if api_key is not None: close_db() return redirect(url_for('api.api_variant_create', variant_chgvs='{0}{1}'.format( match_obj.group(1), match_obj.group(2)), caller='browser', api_key=api_key), code=307) elif re.search(r'^[Nn][Mm]_\d+', query_engine): # NM acc_no sql_table = 'gene' query_type = 'refseq' col_names = 'gene_symbol' match_object = re.search(r'^(^[Nn][Mm]_\d+)', query_engine) if match_object: col_names = 'partial_name' pattern = match_object.group(1) elif re.search( rf'^[Nn][Cc]_0000\d{{2}}\.\d{{1,2}}:g\.{variant_regexp}$', query_engine): # strict HGVS genomic sql_table = 'variant' query_type = 'g_name' col_names = 'feature_id' db = get_db() match_object = re.search( rf'^([Nn][Cc]_0000\d{{2}}\.\d{{1,2}}):g\.({variant_regexp})$', query_engine) # res_common = md_utilities.get_common_chr_name(db, match_object.group(1)) chrom = md_utilities.get_common_chr_name(db, match_object.group(1))[0] pattern = match_object.group(2) close_db() # res_common = md_utilities.get_common_chr_name(db, ) elif re.search( rf'^[Nn][Cc]_0000\d{{2}}\.\d{{1,2}}:g\.{variant_regexp};[\w-]+$', query_engine): # strict HGVS genomic + gene (API call) # API call if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) api_key = md_utilities.get_api_key(g, curs) match_obj = re.search( rf'^([Nn][Cc]_0000\d{{2}}\.\d{{1,2}}:g\.{variant_regexp});([\w-]+)$', query_engine) if api_key is not None: close_db() return redirect(url_for('api.api_variant_g_create', variant_ghgvs=match_obj.group(1), gene_hgnc=match_obj.group(2), caller='browser', api_key=api_key), code=307) elif re.search( rf'^[Cc][Hh][Rr]({nochr_captured_regexp}):g\.{variant_regexp_flexible}$', query_engine): # deal w/ genomic sql_table = 'variant' query_type = 'g_name' col_names = 'feature_id' match_object = re.search( rf'^[Cc][Hh][Rr]({nochr_captured_regexp}):g\.({variant_regexp_flexible})$', query_engine) chrom = match_object.group(1) pattern = match_object.group(2) # if re.search(r'>', pattern): # pattern = pattern.upper() elif re.search(r'^g\..+', query_engine): # g. ng dna vars query_type = 'ng_name' pattern = md_utilities.clean_var_name(query_engine) elif re.search(r'^c\..+', query_engine): # c. dna vars query_type = 'c_name' pattern = md_utilities.clean_var_name(query_engine) elif re.search( r'^%\d+$', query_engine ): # only numbers: get matching variants (exact position match) - specific query match_obj = re.search(r'^%(\d+)$', query_engine) pattern = match_obj.group(1) db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute(r""" SELECT {0} FROM {1} WHERE c_name ~ '^{2}[^\d]' OR c_name ~ '_{2}[^\d]' OR p_name ~ '^{2}[^\d]' OR p_name ~ '_{2}[^\d]' """.format(col_names, sql_table, pattern)) semaph_query = 1 close_db() elif re.search( r'^\d{2,}$', query_engine ): # only numbers: get matching variants (partial match, at least 2 numbers) - specific query db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute(""" SELECT {0} FROM {1} WHERE c_name LIKE '%{2}%' OR p_name LIKE '%{2}%' """.format(col_names, sql_table, query_engine)) semaph_query = 1 close_db() elif re.search(r'^[A-Za-z0-9-]+$', query_engine): # genes sql_table = 'gene' query_type = 'gene_symbol' col_names = 'gene_symbol' pattern = query_engine if not re.search(r'[oO][rR][fF]', pattern): pattern = pattern.upper() else: # from experience the only low case in gene is 'orf' pattern = re.sub(r'O', 'o', pattern) pattern = re.sub(r'R', 'r', pattern) pattern = re.sub(r'F', 'f', pattern) pattern = pattern.capitalize() else: error = 'Sorry I did not understand this query ({}).'.format( query_engine) # return render_template('md/unknown.html', query=query_engine, transformed_query=pattern) if error is None: # print(semaph_query) if semaph_query == 0: if 'db' not in locals(): db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # sql_query = "SELECT {0} FROM {1} WHERE {2} = '{3}'".format(col_names, sql_table, query_type, pattern) # return render_template('md/search_engine.html', query=text) # a little bit of formatting if re.search('variant', sql_table) and \ re.search('>', pattern): # upper for the end of the variant, lower for genomic chr var_match = re.search( r'^(.*)(\d+)([ACTGactg]>[ACTGactg])$', pattern) if var_match: pattern = var_match.group(1).lower() + var_match.group( 2) + var_match.group(3).upper() else: close_db() error = 'You submitted a forbidden character in "{}".'.format( pattern) flash(error, 'w3-pale-red') return render_template('md/unknown.html', run_mode=app.config['RUN_MODE']) # print(pattern) if pattern == 'g_name': curs.execute(""" SELECT {0} FROM {1} WHERE chr = {2} AND {3} = '{4}' """.format(col_names, sql_table, chrom, query_type, pattern)) else: if col_names == 'partial_name': col_names = 'gene_symbol' curs.execute(""" SELECT {0} FROM {1} WHERE {2} LIKE '{3}%' """.format(col_names, sql_table, query_type, pattern)) else: curs.execute(""" SELECT {0} FROM {1} WHERE {2} = '{3}' """.format(col_names, sql_table, query_type, pattern)) result = None if sql_table == 'gene': result = curs.fetchone() if result is None: query_type = 'second_name' # https://www.postgresql.org/docs/9.3/functions-matching.html#POSIX-ESCAPE-SEQUENCES curs.execute(r""" SELECT {0} FROM {1} WHERE {2} ~* '\m[;,]?{3}[;,]?\M' """.format(col_names, sql_table, query_type, pattern)) result_second = curs.fetchone() close_db() if result_second is None: error = """ Sorry the gene does not seem to exist yet in MD or cannot be annotated for some reason ({}). """.format(query_engine) else: return redirect( url_for('md.gene', gene_symbol=result_second[col_names])) else: close_db() return redirect( url_for('md.gene', gene_symbol=result[col_names])) else: result = curs.fetchall() if not result: if query_type == 'dbsnp_id': # api call to create variant from rs id api_key = md_utilities.get_api_key(g, curs) if api_key is not None: close_db() return redirect(url_for( 'api.api_variant_create_rs', rs_id='rs{}'.format(pattern), caller='browser', api_key=api_key), code=307) error = """ Sorry the variant or gene does not seem to exist yet in MD or cannot be annotated for some reason ({}).<br /> You can annotate it directly at the corresponding gene page. """.format(query_engine) else: if len(result) == 1: close_db() return redirect( url_for('api.variant', variant_id=result[0][0], caller='browser')) else: variants = result if query_type == 'g_name': # coming from a query type: NC_000001.11:g.216422237G>A id_tuple = [] for feature_id in result: id_tuple.append(feature_id['feature_id']) curs.execute( """ SELECT id, c_name, p_name, gene_symbol, refseq FROM variant_feature WHERE id IN %s ORDER BY id """, (tuple(id_tuple), )) variants = curs.fetchall() close_db() return render_template( 'md/variant_multiple.html', run_mode=md_utilities.get_running_mode(), variants=variants) if 'db' in locals(): close_db() else: error = 'Please type something for the search engine to work.' flash(error, 'w3-pale-red') return render_template('md/unknown.html', run_mode=md_utilities.get_running_mode())
def vars(gene_symbol=None): if gene_symbol is None: return render_template('md/unknown.html', query='No gene provided') elif re.search(r'[^\w-]', gene_symbol): return render_template('md/unknown.html', query=gene_symbol) gene_symbol = re.escape(gene_symbol) db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # error = None # main isoform? curs.execute( """ SELECT * FROM gene WHERE gene_symbol = %s AND canonical = 't' """, (gene_symbol, )) main = curs.fetchone() if main is not None: curs.execute(""" SELECT gene_symbol, refseq, variant_creation FROM gene WHERE gene_symbol = %s """, (gene_symbol, )) # get all isoforms result_all = curs.fetchall() num_iso = len(result_all) # curs.execute( # """ # SELECT d.variant_creation, d.refseq, b.pos, a.c_name, a.p_name, a.start_segment_type, a.start_segment_number, a.creation_date, c.username, a.prot_type, a.ivs_name, a.id as vf_id # FROM variant_feature a, variant b, mobiuser c, gene d # WHERE a.id = b.feature_id # AND a.gene_symbol = d.gene_symbol # AND a.refseq = d.refseq # AND a.creation_user = c.id # AND b.genome_version = 'hg38' # AND a.gene_symbol = %s # """, # (gene_symbol,) # ) curs.execute( """ SELECT d.variant_creation, d.refseq, a.c_name, a.p_name, a.start_segment_type, a.start_segment_number, a.creation_date, c.username, a.prot_type, a.ivs_name, a.id as vf_id FROM variant_feature a, mobiuser c, gene d WHERE a.gene_symbol = d.gene_symbol AND a.refseq = d.refseq AND a.creation_user = c.id AND a.gene_symbol = %s """, (gene_symbol, )) variants = curs.fetchall() curs.execute( """ SELECT MAX(prot_size) AS size FROM gene WHERE gene_symbol = %s """, (gene_symbol, )) res_size = curs.fetchone() # if vars_type is not None: close_db() clingen_criteria_specification_id = md_utilities.get_clingen_criteria_specification_id( gene_symbol) return render_template( 'md/vars.html', run_mode=md_utilities.get_running_mode(), urls=md_utilities.urls, gene_symbol=gene_symbol, num_iso=num_iso, variants=variants, gene_info=main, res=result_all, max_prot_size=res_size['size'], clingen_criteria_specification_id=clingen_criteria_specification_id ) else: close_db() return render_template('md/unknown.html', run_mode=md_utilities.get_running_mode(), query=gene_symbol)
def register(): if (md_utilities.get_running_mode() == 'maintenance'): return render_template('md/index.html', run_mode=md_utilities.get_running_mode()) if request.method == 'POST': # username = urllib.parse.unquote(request.form['username']) username = request.form['username'] password = request.form['password'] country = request.form['country'] institute = request.form['institute'] email = request.form['email'] db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) error = None if not username: error = 'Username is required.' elif len(username) < 5: error = 'Username should be at least 5 characters.' elif not password: error = 'Password is required.' elif len(password) < 8 or \ not re.search(r'[a-z]', password) or \ not re.search(r'[A-Z]', password) or \ not re.search(r'[0-9]', password): error = """ Password should be at least 8 characters and mix at least letters (upper and lower case) and numbers. """ elif not country or re.match('--', country): error = 'Country is required.' elif not institute: error = 'Institute is required.' elif not email: error = 'Email is required.' elif not re.search( r'^[a-zA-Z0-9\._%\+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email): error = 'The email address does not look valid.' else: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) # try: # read api key for mailboxvalidator apikey = configuration.mdconfig(section='email_check')['apikey'] mv_url = 'https://api.mailboxvalidator.com/v1/validation/single?key={0}&format=json&email={1}'.format( apikey, email) try: mv_json = json.loads( http.request('GET', mv_url).data.decode('utf-8')) except Exception: mv_json = None if mv_json is not None: try: if mv_json['credits_available'] > 0: if mv_json['status'] == "False": if (mv_json['is_high_risk'] == "True" or mv_json['is_suppressed'] == "True" or mv_json['is_catchall'] == "True"): error = """ The email address is reported as risky or suppressed. If this is not the case, please send us an email directly to mobide tails .iurc@ gmail. com. """ # else:valid adressese such as # [email protected] are reported as False else: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails email validation error', '<p>mailboxvalidator credits == 0</p>'), '[MobiDetails - Email Validation Error]') except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails email validation error', """ <p>mailboxvalidator validation failed: <br/> {0} <br /> - from {1} with args: {2}</p> """.format(mv_json, os.path.basename(__file__), e.args)), '[MobiDetails - Email Validation Error]') # 2nd check https://www.stopforumspam.com/ # ex https://www.stopforumspam.com/api?ip=&email=&username=&f=json sfs_url = 'https://www.stopforumspam.com/api?ip={0}&email={1}&username={2}&f=json'.format( request.remote_addr, email, username) # print(sfs_url) try: sfs_json = json.loads( http.request('GET', sfs_url).data.decode('utf-8')) except Exception: sfs_json = None if sfs_json is not None: try: print(sfs_json) if sfs_json['success'] == 1: # sfs return a boolean for username, ip, email # if email or ip = 1 => rejected # username won't be rejected but a warning will be sent if sfs_json['ip']['appears'] == 1 or \ sfs_json['email']['appears'] == 1: error = """ Sorry, your input data is reported as risky. If this is not the case, please send us an email directly to mobide tails .iurc @gmail .com. """ elif sfs_json['username']['appears'] == 1: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails stop forum spam \ username validation error', """ <p>Stop forum spam username validation failed (user created but to follow): <br/> {0} <br /> - from {1} with url: {2}</p> """.format(sfs_json, os.path.basename(__file__), sfs_url)), '[MobiDetails - Email Validation Error]') else: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails stop forum spam validation error', """ <p>Stop forum spam validation failed:<br/> {0}<br /> - from {1} with url: {2}</p> """.format(sfs_json, os.path.basename(__file__), sfs_url)), '[MobiDetails - Email Validation Error]') except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails stop forum spam validation error', """ <p>Stop forum spam validation failed:<br/> {0}<br /> - from {1} with args: {2}</p> """.format(sfs_json, os.path.basename(__file__), e.args)), '[MobiDetails - Email Validation Error]') if error is None: # curs.execute( # "SELECT id FROM mobiuser WHERE username = '******'\ # OR email = '{1}'".format(username, email) # ) curs.execute( """ SELECT id FROM mobiuser WHERE username = %s OR email = %s """, (username, email)) if curs.fetchone() is not None: error = 'User {0} or email address {1}\ is already registered.'.format(username, email) if error is None: key = secrets.token_urlsafe(32) curs.execute( """ INSERT INTO mobiuser (username, password, country, institute, email, api_key, activated) VALUES (%s, %s, %s, %s, %s, %s, 'f') RETURNING id """, (username, generate_password_hash(password), country, institute, email, key)) user_id = curs.fetchone()[0] db.commit() md_utilities.send_email( md_utilities.prepare_email_html( 'MobiDetails - Account activation', """ Dear {0}, <p>thank you for registering in MobiDetails. We hope you will find this website useful.</p> <p>Please follow the link below to activate your MobiDetails account:</p> <p><a href="{1}{2}" title="Activate your MD account"> Activate your MD account</a></p> <p>If you do not know why you receive this email, do not follow the link and please alert [email protected].</p><br /> """.format( username, request.host_url.rstrip('/'), url_for('auth.activate', mobiuser_id=user_id, api_key=key)), False), '[MobiDetails - Account activation]', [email], [app.config["MAIL_ERROR_RECIPIENT"]]) flash( """ <br /><p>Your account has been created but requires an activation step. An email has been sent to {} with an activation link.</p><br /> """.format(email), 'w3-pale-green') close_db() return redirect(url_for('md.index'), code=302) flash(error, 'w3-pale-red') if error is not None and not app.config['TESTING']: message_body = """ <p>{0}</p><p>Originated from :</p><ul><li> Remote IP: {1}</li><li>Username: {2}</li> <li>Country: {3}</li><li>Institute: {4}</li> <li>Email: {5}</li></ul> """.format(error, request.remote_addr, username, country, institute, email) md_utilities.send_error_email( md_utilities.prepare_email_html('MobiDetails error', message_body), '[MobiDetails - Registering Error]') close_db() return render_template('auth/register.html', countries=md_utilities.countries, prev_username=username, prev_institute=institute, prev_email=email) return render_template('auth/register.html', countries=md_utilities.countries)
def create(): # start_time = time.time() # print(request.form['new_variant']) variant_regexp = md_utilities.regexp['variant'] if request.form['new_variant'] == '': return md_utilities.danger_panel('variant creation attempt', 'Please fill in the form before submitting!') if re.search(r'^[\w-]+$', request.form['gene']) and \ re.search(r'^NM_\d+$', request.form['acc_no']) and \ re.search(rf'^c\.{variant_regexp}$', request.form['new_variant']) and \ re.search(r'^\d+$', request.form['acc_version']): gene = request.form['gene'] acc_no = request.form['acc_no'] new_variant = request.form['new_variant'] new_variant = new_variant.replace(" ", "").replace("\t", "") # new_variant = new_variant.replace("\t", "") original_variant = new_variant acc_version = request.form['acc_version'] alt_nm = None if 'alt_iso' in request.form: alt_nm = request.form['alt_iso'] # return md_utilities.danger_panel(alt_nm, acc_version) if alt_nm != '' and alt_nm != "{0}.{1}".format(acc_no, acc_version): acc_no, acc_version = alt_nm.split('.') # variant already registered? db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) var_db = new_variant.replace("c.", "") curs.execute( "SELECT id FROM variant_feature WHERE c_name = %s AND gene_name[2] = %s", (var_db, acc_no) ) res = curs.fetchone() if res: close_db() return md_utilities.info_panel('Variant already in MobiDetails: ', var_db, res['id']) if re.search(r'c\..+', new_variant): # is vv alive? # vv_alive = None vv_base_url = md_utilities.get_vv_api_url() # try: # json.loads(http.request('GET', md_utilities.urls['variant_validator_api_hello']).data.decode('utf-8')) # except Exception as e: # try: # json.loads(http.request('GET', md_utilities.urls['variant_validator_api_hello_backup']).data.decode('utf-8')) # vv_base_url = md_utilities.urls['variant_validator_api_backup'] # except Exception as e: # md_utilities.send_error_email( # md_utilities.prepare_email_html( # 'MobiDetails VariantValidator error', # '<p>VariantValidator looks down!!<br /> - from {0} with args: {1}</p>'.format( # os.path.basename(__file__), e.args # ) # ), # '[MobiDetails - VariantValidator Error]' # ) # # vv_data = {'apiVersion': 'Service Unavailable'} # # vv_alive = {'status': 'Service Unavailable'} if not vv_base_url: close_db() return md_utilities.danger_panel( new_variant, 'Variant Validator did not answer our call, status: Service Unavailable. \ I have been informed by email. Please retry later.') # http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) if not alt_nm or acc_no == request.form['acc_no']: vv_url = "{0}VariantValidator/variantvalidator/GRCh38/{1}.{2}:{3}/{1}.{2}?content-type=application/json".format( vv_base_url, acc_no, acc_version, new_variant ) else: vv_url = "{0}VariantValidator/variantvalidator/GRCh38/{1}.{2}:{3}/all?content-type=application/json".format( vv_base_url, acc_no, acc_version, new_variant ) vv_key_var = "{0}.{1}:{2}".format(acc_no, acc_version, new_variant) try: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) vv_data = json.loads(http.request('GET', vv_url).data.decode('utf-8')) except Exception: close_db() return md_utilities.danger_panel( new_variant, 'Variant Validator did not return any value for the variant.\ Either it is down or your nomenclature is very odd!' ) if re.search('[di][neu][psl]', new_variant): # need to redefine vv_key_var for indels as the variant name returned by vv is likely to be different form the user's for key in vv_data: if re.search('{0}.{1}'.format(acc_no, acc_version), key): vv_key_var = key # print(key) var_obj = re.search(r':(c\..+)$', key) if var_obj: new_variant = var_obj.group(1) else: close_db() return md_utilities.danger_panel(new_variant, 'Please provide the variant name as HGVS c. nomenclature (including c.)') return md_utilities.create_var_vv( vv_key_var, gene, acc_no, new_variant, original_variant, acc_version, vv_data, 'webApp', db, g ) else: close_db() return md_utilities.danger_panel('variant creation attempt', 'A mandatory argument is lacking or is malformed.')
def reset_password(): if (md_utilities.get_running_mode() == 'maintenance'): return render_template('md/index.html', run_mode=md_utilities.get_running_mode()) if request.method == 'GET': if re.search(r'^\d+$', request.args.get('mobiuser_id')) and \ re.search(r'^[\d\.]+$', request.args.get('ts')): mobiuser_id = request.args.get('mobiuser_id') api_key = request.args.get('api_key') original_timestamp = request.args.get('ts') # we will keep the link alive for 30 minutes i.e. 1800 s if float(float(datetime.timestamp( datetime.now()) ) - float(original_timestamp)) > 1800 or \ float(float(datetime.timestamp( datetime.now()) ) - float(original_timestamp)) < 0: flash( """ This link is outdated. Please try again the procedure. """, 'w3-pale-red') return render_template('auth/login.html') db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( """ SELECT id, api_key, activated FROM mobiuser WHERE id = %s AND api_key = %s """, (mobiuser_id, api_key)) user = curs.fetchone() if user is None: message_body = """ <p>Password reset exception</p><p>Received API key: {0} and mobiuser_id: {1} from {2} """.format(api_key, mobiuser_id, request.remote_addr) md_utilities.send_error_email( md_utilities.prepare_email_html('MobiDetails error', message_body), '[MobiDetails - Password reset Error]') flash( """ API key and user id do not seem to fit. An admin has been warned """, 'w3-pale-red') close_db() return render_template('auth/forgot_pass.html') else: close_db() return render_template('auth/reset_pass.html', mobiuser_id=mobiuser_id, api_key=api_key) else: message_body = """ <p>Password reset exception</p><p>Received timestamp: {0} and mobiuser_id: {1} from {2} """.format(request.args.get('ts'), request.args.get('mobiuser_id'), request.remote_addr) md_utilities.send_error_email( md_utilities.prepare_email_html('MobiDetails error', message_body), '[MobiDetails - Password reset Error]') flash( """ Some parameters are not legal. An admin has been warned """, 'w3-pale-red') return render_template('auth/forgot_pass.html') elif request.method == 'POST': mobiuser_id = request.form['mobiuser_id'] api_key = request.form['api_key'] password = request.form['password'] error = None if len(password) < 8 or \ not re.search(r'[a-z]', password) or \ not re.search(r'[A-Z]', password) or \ not re.search(r'[0-9]', password): error = """ Password should be at least 8 characters and mix at least letters (upper and lower case) and numbers. """ else: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( """ UPDATE mobiuser SET password= %s WHERE id = %s AND api_key = %s """, (generate_password_hash(password), mobiuser_id, api_key)) db.commit() flash('Your password has just been reset.', 'w3-pale-green') close_db() return render_template('auth/login.html') if error is not None: flash(error, 'w3-pale-red') return render_template('auth/reset_pass.html', mobiuser_id=mobiuser_id, api_key=api_key) return render_template('md/unknown.html')
def profile(mobiuser_id=0): if re.search(r'^\d+$', str(mobiuser_id)): user_id = g.user['id'] if mobiuser_id != 0: user_id = mobiuser_id db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( """ SELECT id, username, email, institute, country, api_key, email_pref, lovd_export, academic FROM mobiuser WHERE id = %s """, (user_id, )) mobiuser = curs.fetchone() error = None if mobiuser is None: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails error', '<p>Bad profile attempt username: from id: {0} file: {1}\ </p>'.format(g.user['id'], os.path.basename(__file__))), '[MobiDetails - Profile Error]') error = 'You seem to be unknown by MobiDetails.' if mobiuser_id != 0: error = 'This user seems to be unknown by MobiDetails.' if mobiuser_id == 0: # curs.execute( # "SELECT id, c_name, gene_name, p_name, creation_date \ # FROM variant_feature WHERE\ # creation_user = %s ORDER BY creation_date DESC", # (g.user['id'],) # ) curs.execute( """ SELECT a.id, a.c_name, a.gene_symbol, a.p_name, a.creation_date, b.mobiuser_id FROM variant_feature a LEFT JOIN mobiuser_favourite b ON a.id = b.feature_id WHERE a.creation_user = %s ORDER BY a.creation_date DESC """, (g.user['id'], )) variants = curs.fetchall() num_var = curs.rowcount curs.execute( """ SELECT * FROM variants_groups WHERE mobiuser_id = %s ORDER BY creation_date DESC """, (g.user['id'], )) variant_groups = curs.fetchall() variant_groups_number = curs.rowcount # we need to modify the username to propose a list name without special characters clean_username = re.sub(r'[^\w]', '_', mobiuser['username']) curs.execute( """ SELECT a.id, a.c_name, a.ng_name, a.gene_symbol, a.p_name FROM variant_feature a, mobiuser_favourite b WHERE a.id = b.feature_id AND b.mobiuser_id = %s ORDER BY a.gene_symbol, a.ng_name """, (g.user['id'], )) variants_favourite = curs.fetchall() if error is None: # num_var_fav = curs.rowcount close_db() return render_template( 'auth/profile.html', run_mode=md_utilities.get_running_mode(), mobiuser=mobiuser, view='own', num_var=num_var, num_var_fav=curs.rowcount, variants=variants, variants_favourite=variants_favourite, variant_groups=variant_groups, variant_groups_number=variant_groups_number + 1, clean_username=clean_username) elif error is None: # other profile view close_db() return render_template('auth/profile.html', run_mode=md_utilities.get_running_mode(), mobiuser=mobiuser, view='other', num_var=None, num_var_fav=None, variants=None, variants_favourite=None, variant_groups=None, variant_groups_number=None, clean_username=None) flash(error, 'w3-pale-red') close_db() return render_template('md/index.html', run_mode=md_utilities.get_running_mode()) else: ('Invalid user ID!!', 'w3-pale-red') return render_template('md/index.html', run_mode=md_utilities.get_running_mode())
def login(): referrer_page = None if request.method == 'GET': referrer_page = request.referrer # if request.referrer is not None and \ # (url_parse(request.referrer).host == # url_parse(request.base_url).host): # referrer_page = request.referrer if request.method == 'POST': email = request.form['email'] password = request.form['password'] try: referrer_page = request.form['referrer_page'] # if request.form['referrer_page'] is not None and \ # (url_parse(request.form['referrer_page']).host == # url_parse(request.base_url).host: # referrer_page = request.form['referrer_page'] except Exception: pass db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) error = None curs.execute( """ SELECT * FROM mobiuser WHERE email = %s """, (email, )) user = curs.fetchone() if user is None: error = 'Unknown email.' elif not check_password_hash(user['password'], password): error = 'Incorrect password.' elif user['activated'] is False: error = """ This account is not activated. An email to activate your account has been sent to {} """.format(user['email']) # message, mail_object, receiver md_utilities.send_email( md_utilities.prepare_email_html( 'MobiDetails - Account activation', """ Dear {0}, <p>please follow the link below to activate your MobiDetails account:</p> <p><a href="{1}{2}" title="Activate your MD account"> Activate your MD account</a></p> <p>If you do not know why you receive this email, do not follow the link and please alert [email protected].</p><br /> """.format( user['username'], request.host_url.rstrip('/'), url_for('auth.activate', mobiuser_id=user['id'], api_key=user['api_key'])), False), '[MobiDetails - Account activation]', [email]) if error is None: session.clear() close_db() flash( 'You have successfully been logged in as {}.'.format( user["username"]), 'w3-pale-green') session['user_id'] = user['id'] if referrer_page is None or \ (url_parse(referrer_page).host != url_parse(request.base_url).host or re.search(r'(login|register)', referrer_page)): return redirect(url_for( 'auth.profile', run_mode=md_utilities.get_running_mode(), mobiuser_id=0), code=302) else: if referrer_page is not None and \ (url_parse(referrer_page).host == url_parse(request.base_url).host): # not coming from mobidetails # rebuild redirect URL return redirect( md_utilities.build_redirect_url(referrer_page), code=302) else: return redirect(url_for( 'auth.profile', run_mode=md_utilities.get_running_mode(), mobiuser_id=0), code=302) close_db() flash(error, 'w3-pale-red') # not coming from mobidetails # rebuild redirect URL return render_template( 'auth/login.html', run_mode=md_utilities.get_running_mode(), referrer_page=md_utilities.build_redirect_url(referrer_page))
def api_variant_create(variant_chgvs=None, caller=None, api_key=None): # print(request.form) # print(request.args) # get params # swagger/curl sends request.args e.g. # curl -X POST "http://10.34.20.79:5001/api/variant/create?variant_chgvs=NM_005422.2%3Ac.5040G%3ET&api_key=XXX" -H "accept: application/json" # while # urllib3 request from create_vars_batch.py sends request.form if request.args.get('variant_chgvs') and \ request.args.get('caller') and \ request.args.get('api_key'): variant_chgvs = request.args.get('variant_chgvs', type=str) caller = request.args.get('caller', type=str) api_key = request.args.get('api_key', type=str) elif 'variant_chgvs' in request.form and \ 'caller' in request.form and \ 'api_key' in request.form: variant_chgvs = request.form['variant_chgvs'] caller = request.form['caller'] api_key = request.form['api_key'] else: if caller == 'cli': return jsonify(mobidetails_error='I cannot fetch the right parameters') else: flash('I cannot fetch the right parameters', 'w3-pale-red') return redirect(url_for('md.index')) if variant_chgvs is not None and \ caller is not None and \ api_key is not None: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # mobiuser_id = None res_check_api_key = md_utilities.check_api_key(db, api_key) if 'mobidetails_error' in res_check_api_key: if caller == 'cli': return jsonify(res_check_api_key) else: flash(res_check_api_key['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) else: g.user = res_check_api_key['mobiuser'] if md_utilities.check_caller(caller) == 'Invalid caller submitted': if caller == 'cli': return jsonify(mobidetails_error='Invalid caller submitted') else: flash('Invalid caller submitted to API.', 'w3-pale-red') return redirect(url_for('md.index')) # if len(api_key) != 43: # return jsonify(mobidetails_error='Invalid API key') # else: # curs.execute( # "SELECT * FROM mobiuser WHERE api_key = %s", # (api_key,) # ) # res = curs.fetchone() # if res is None: # return jsonify(mobidetails_error='Unknown API key') # else: # g.user = res # if variant_chgvs is None: # return jsonify(mobidetails_error='No variant submitted') variant_regexp = md_utilities.regexp['variant'] match_object = re.search(rf'^([Nn][Mm]_\d+)\.(\d{{1,2}}):c\.({variant_regexp})', urllib.parse.unquote(variant_chgvs)) if match_object: # match_object = re.search(r'^([Nn][Mm]_\d+)\.(\d{1,2}):c\.(.+)', variant_chgvs) acc_no, acc_version, new_variant = match_object.group(1), match_object.group(2), match_object.group(3) new_variant = new_variant.replace(" ", "").replace("\t", "") # new_variant = new_variant.replace("\t", "") original_variant = new_variant curs.execute( "SELECT id FROM variant_feature WHERE c_name = %s AND gene_name[2] = %s", (new_variant, acc_no) ) res = curs.fetchone() if res is not None: if caller == 'cli': return jsonify( mobidetails_id=res['id'], url='{0}{1}'.format( request.host_url[:-1], url_for('md.variant', variant_id=res['id']) ) ) else: return redirect(url_for('md.variant', variant_id=res['id'])) else: # creation # get gene curs.execute( "SELECT name[1] as gene FROM gene WHERE name[2] = %s", (acc_no,) ) res_gene = curs.fetchone() if res_gene is None: return jsonify(mobidetails_error='The gene corresponding to {} is not yet present in MobiDetails'.format(acc_no)) vv_base_url = md_utilities.get_vv_api_url() if not vv_base_url: close_db() if caller == 'cli': return jsonify(mobidetails_error='Variant Validator looks down!.') else: flash('TVariant Validator looks down!.', 'w3-pale-red') return redirect(url_for('md.index')) vv_url = "{0}VariantValidator/variantvalidator/GRCh38/{1}.{2}:{3}/all?content-type=application/json".format( vv_base_url, acc_no, acc_version, new_variant ) vv_key_var = "{0}.{1}:c.{2}".format(acc_no, acc_version, new_variant) try: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) vv_data = json.loads(http.request('GET', vv_url).data.decode('utf-8')) except Exception: close_db() if caller == 'cli': return jsonify(mobidetails_error='Variant Validator did not return any value for the variant {}.'.format(new_variant)) else: try: flash('There has been a issue with the annotation of the variant via VariantValidator. \ The error is the following: {}'.format(vv_data['validation_warning_1']['validation_warnings']), 'w3-pale-red') except Exception: flash('There has been a issue with the annotation of the variant via VariantValidator. \ Sorry for the inconvenience. You may want to try directly in MobiDetails.', 'w3-pale-red') return redirect(url_for('md.index')) if re.search('[di][neu][psl]', new_variant): # need to redefine vv_key_var for indels as the variant name returned by vv is likely to be different form the user's for key in vv_data.keys(): if re.search('{0}.{1}'.format(acc_no, acc_version), key): vv_key_var = key # print(key) var_obj = re.search(r':c\.(.+)$', key) if var_obj is not None: new_variant = var_obj.group(1) creation_dict = md_utilities.create_var_vv( vv_key_var, res_gene['gene'], acc_no, 'c.{}'.format(new_variant), original_variant, acc_version, vv_data, 'api', db, g ) if caller == 'cli': return jsonify(creation_dict) else: return redirect(url_for('md.variant', variant_id=creation_dict['mobidetails_id'])) else: if caller == 'cli': return jsonify(mobidetails_error='Malformed query {}'.format(urllib.parse.unquote(variant_chgvs))) else: flash('The query seems to be malformed: {}.'.format(urllib.parse.unquote(variant_chgvs)), 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Invalid parameters') else: flash('The submitted parameters looks invalid!!!', 'w3-pale-red') return redirect(url_for('md.index'))
def intervar(): genome_regexp = md_utilities.regexp['genome'] nochr_chrom_regexp = md_utilities.regexp['nochr_chrom'] if re.search(rf'^{genome_regexp}$', request.form['genome']) and \ re.search(rf'^{nochr_chrom_regexp}$', request.form['chrom']) and \ re.search(r'^\d+$', request.form['pos']) and \ re.search(r'^[ATGC]+$', request.form['ref']) and \ re.search(r'^[ATGC]+$', request.form['alt']) and \ 'gene' in request.form: genome = request.form['genome'] chrom = request.form['chrom'] pos = request.form['pos'] ref = request.form['ref'] alt = request.form['alt'] gene = request.form['gene'] if len(ref) > 1 or len(alt) > 1: return 'No wintervar for indels' if ref == alt: return 'hg19 reference is equal to variant: no wIntervar query' http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) intervar_url = "{0}{1}_updated.v.201904&chr={2}&pos={3}&ref={4}&alt={5}".format( md_utilities.urls['intervar_api'], genome, chrom, pos, ref, alt ) try: intervar_data = [json.loads(http.request('GET', intervar_url).data.decode('utf-8'))] except Exception: try: # intervar can return mutliple json objects, e.g.: # {"Intervar":"Uncertain significance","Chromosome":1,"Position_hg19":151141512,"Ref_allele":"T","Alt_allele":"A","Gene":"SCNM1","PVS1":0,"PS1":0,"PS2":0,"PS3":0,"PS4":0,"PM1":1,"PM2":1,"PM3":0,"PM4":0,"PM5":0,"PM6":0,"PP1":0,"PP2":0,"PP3":0,"PP4":0,"PP5":0,"BA1":0,"BP1":0,"BP2":0,"BP3":0,"BP4":0,"BP5":0,"BP6":0,"BP7":0,"BS1":0,"BS2":0,"BS3":0,"BS4":0}{"Intervar":"Uncertain significance","Chromosome":1,"Position_hg19":151141512,"Ref_allele":"T","Alt_allele":"A","Gene":"TNFAIP8L2-SCNM1","PVS1":0,"PS1":0,"PS2":0,"PS3":0,"PS4":0,"PM1":1,"PM2":1,"PM3":0,"PM4":0,"PM5":0,"PM6":0,"PP1":0,"PP2":0,"PP3":0,"PP4":0,"PP5":0,"BA1":0,"BP1":0,"BP2":0,"BP3":0,"BP4":0,"BP5":0,"BP6":0,"BP7":0,"BS1":0,"BS2":0,"BS3":0,"BS4":0} intervar_list = re.split('}{', http.request('GET', intervar_url).data.decode('utf-8')) i = 0 for obj in intervar_list: # some housekeeping to get proper strings obj = obj.replace('{', '').replace('}', '') obj = '{{{}}}'.format(obj) intervar_list[i] = obj i += 1 # if i == 0: # intervar_list[i] = '{0}}}'.format(obj) # else: # intervar_list[i] = '{{{0}'.format(obj) # if i > 2: # intervar_list[i-1] = '{0}}}'.format(intervar_list[i-1]) intervar_data = [] for obj in intervar_list: # print(obj) intervar_data.append(json.loads(obj)) except Exception as e: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', '<p>Intervar API call failed for {0}-{1}-{2}-{3}-{4} - url: {5} <br /> - from {6} with args: {7}</p>'.format( genome, chrom, pos, ref, alt, intervar_url, os.path.basename(__file__), e.args ) ), '[MobiDetails - API Error]' ) return "<span>No wintervar class</span>" intervar_acmg = None if len(intervar_data) == 1: intervar_acmg = intervar_data[0]['Intervar'] else: for intervar_dict in intervar_data: # intervar likely returns several json objects if intervar_dict['Gene'] == gene: intervar_acmg = intervar_dict['Intervar'] if intervar_acmg is not None: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute( "SELECT html_code FROM valid_class WHERE acmg_translation = '{}'".format( intervar_acmg.lower() ) ) res = curs.fetchone() close_db() return "<span style='color:{0};'>{1}</span>".format(res['html_code'], intervar_acmg) else: return "<span>No wintervar class</span>" else: md_utilities.send_error_email( md_utilities.prepare_email_html( 'MobiDetails API error', '<p>Intervar API call failed for {0}-{1}-{2}-{3}-{4}<br /> - from {5} with args: A mandatory argument is missing</p>'.format( request.form['genome'], request.form['chrom'], request.form['pos'], request.form['ref'], request.form['alt'], os.path.basename(__file__) ) ), '[MobiDetails - API Error]' ) return "<span>No wintervar class</span>"
def api_variant_create(variant_chgvs=None, caller=None, api_key=None): # get params caller = md_utilities.get_post_param(request, 'caller') variant_chgvs = md_utilities.get_post_param(request, 'variant_chgvs') api_key = md_utilities.get_post_param(request, 'api_key') if variant_chgvs and \ caller and \ api_key: db = get_db() curs = db.cursor(cursor_factory=psycopg2.extras.DictCursor) # mobiuser_id = None res_check_api_key = md_utilities.check_api_key(db, api_key) if 'mobidetails_error' in res_check_api_key: if caller == 'cli': return jsonify(res_check_api_key) else: flash(res_check_api_key['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) else: g.user = res_check_api_key['mobiuser'] if md_utilities.check_caller(caller) == 'Invalid caller submitted': if caller == 'cli': return jsonify(mobidetails_error='Invalid caller submitted') else: flash('Invalid caller submitted to API.', 'w3-pale-red') return redirect(url_for('md.index')) variant_regexp = md_utilities.regexp['variant'] match_object = re.search( rf'^([Nn][Mm]_\d+)\.(\d{{1,2}}):c\.({variant_regexp})', urllib.parse.unquote(variant_chgvs)) if match_object: # match_object = re.search(r'^([Nn][Mm]_\d+)\.(\d{1,2}):c\.(.+)', variant_chgvs) acc_no, submitted_nm_version, new_variant = match_object.group( 1), match_object.group(2), match_object.group(3) # acc_no, acc_version, new_variant = match_object.group(1), match_object.group(2), match_object.group(3) new_variant = new_variant.replace(" ", "").replace("\t", "") # new_variant = new_variant.replace("\t", "") original_variant = new_variant curs.execute( "SELECT id FROM variant_feature WHERE c_name = %s AND gene_name[2] = %s", (new_variant, acc_no)) res = curs.fetchone() if res is not None: if caller == 'cli': return jsonify(mobidetails_id=res['id'], url='{0}{1}'.format( request.host_url[:-1], url_for('md.variant', variant_id=res['id']))) else: return redirect(url_for('md.variant', variant_id=res['id'])) else: # creation # get gene curs.execute( "SELECT name[1] as gene, nm_version FROM gene WHERE name[2] = %s and variant_creation = 'ok'", (acc_no, )) res_gene = curs.fetchone() if res_gene is None: if caller == 'cli': return jsonify( mobidetails_error= 'The gene corresponding to {} is not yet available for variant annotation in MobiDetails' .format(acc_no)) else: flash( 'The gene corresponding to {} is not available for variant annotation in MobiDetails.' .format(acc_no), 'w3-pale-red') return redirect(url_for('md.index')) if int(res_gene['nm_version']) != int(submitted_nm_version): if caller == 'cli': return jsonify( mobidetails_error= 'The RefSeq accession number submitted ({0}) for {1} does not match MobiDetail\'s ({2}).' .format(submitted_nm_version, acc_no, res_gene['nm_version'])) else: flash( 'The RefSeq accession number submitted ({0}) for {1} does not match MobiDetail\'s ({2}).' .format(submitted_nm_version, acc_no, res_gene['nm_version']), 'w3-pale-red') return redirect(url_for('md.index')) acc_version = res_gene['nm_version'] vv_base_url = md_utilities.get_vv_api_url() if not vv_base_url: close_db() if caller == 'cli': return jsonify( mobidetails_error='Variant Validator looks down!.') else: flash('Variant Validator looks down!.', 'w3-pale-red') return redirect(url_for('md.index')) vv_url = "{0}VariantValidator/variantvalidator/GRCh38/{1}.{2}:{3}/all?content-type=application/json".format( vv_base_url, acc_no, acc_version, new_variant) vv_key_var = "{0}.{1}:c.{2}".format(acc_no, acc_version, new_variant) try: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) vv_data = json.loads( http.request('GET', vv_url).data.decode('utf-8')) except Exception: close_db() if caller == 'cli': return jsonify( mobidetails_error= 'Variant Validator did not return any value for the variant {}.' .format(new_variant)) else: try: flash( 'There has been a issue with the annotation of the variant via VariantValidator. \ The error is the following: {}'.format( vv_data['validation_warning_1'] ['validation_warnings']), 'w3-pale-red') except Exception: flash( 'There has been a issue with the annotation of the variant via VariantValidator. \ Sorry for the inconvenience. You may want to try directly in MobiDetails.', 'w3-pale-red') return redirect(url_for('md.index')) if re.search('[di][neu][psl]', new_variant): # need to redefine vv_key_var for indels as the variant name returned by vv is likely to be different form the user's for key in vv_data.keys(): if re.search('{0}.{1}'.format(acc_no, acc_version), key): vv_key_var = key # print(key) var_obj = re.search(r':c\.(.+)$', key) if var_obj is not None: new_variant = var_obj.group(1) creation_dict = md_utilities.create_var_vv( vv_key_var, res_gene['gene'], acc_no, 'c.{}'.format(new_variant), original_variant, acc_version, vv_data, 'api', db, g) if 'mobidetails_error' in creation_dict: if caller == 'cli': return jsonify(creation_dict) else: flash(creation_dict['mobidetails_error'], 'w3-pale-red') return redirect(url_for('md.index')) if caller == 'cli': return jsonify(creation_dict) else: return redirect( url_for('md.variant', variant_id=creation_dict['mobidetails_id'])) else: if caller == 'cli': return jsonify(mobidetails_error='Malformed query {}'.format( urllib.parse.unquote(variant_chgvs))) else: flash( 'The query seems to be malformed: {}.'.format( urllib.parse.unquote(variant_chgvs)), 'w3-pale-red') return redirect(url_for('md.index')) else: if caller == 'cli': return jsonify(mobidetails_error='Invalid parameters') else: flash('The submitted parameters looks invalid!!!', 'w3-pale-red') return redirect(url_for('md.index'))