Skip to content

Commit

Permalink
added api route to upload variants and bugfix for clinvar uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinDo committed Aug 16, 2024
1 parent 99184fd commit e168054
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 200 deletions.
23 changes: 19 additions & 4 deletions src/common/db_IO.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,7 @@ def get_mane_select_for_gene(self, gene, source):
"""

def get_mane_select_for_gene(self, gene_id):
command = "SELECT name FROM transcript WHERE gene_id = %s AND (is_mane_select=1 or is_mane_plus_clinical=1)"
command = "SELECT name FROM transcript WHERE gene_id = %s AND is_mane_select=1"
self.cursor.execute(command, (gene_id, ))
result = self.cursor.fetchall()
return [x[0] for x in result if x[0].startswith("ENST")]
Expand Down Expand Up @@ -2088,12 +2088,12 @@ def get_heredicare_center_classifications(self, heredicare_annotation_id):
#result = sorted(result, key=lambda x: functions.convert_none_infinite(x[5]), reverse=True)
return result

def insert_user(self, username, first_name, last_name, affiliation):
def insert_user(self, username, first_name, last_name, affiliation, api_roles):
#command = "INSERT INTO user (username, first_name, last_name, affiliation) \
# SELECT %s FROM DUAL WHERE NOT EXISTS (SELECT * FROM user \
# WHERE `username`=%s LIMIT 1)"
command = "INSERT INTO user (username, first_name, last_name, affiliation) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE first_name=%s, last_name=%s, affiliation=%s"
self.cursor.execute(command, (username, first_name, last_name, affiliation, first_name, last_name, affiliation))
command = "INSERT INTO user (username, first_name, last_name, affiliation, api_roles) VALUES (%s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE first_name=%s, last_name=%s, affiliation=%s, api_roles=%s"
self.cursor.execute(command, (username, first_name, last_name, affiliation, api_roles, first_name, last_name, affiliation, api_roles))
self.conn.commit()

def get_user(self, user_id):
Expand All @@ -2106,6 +2106,11 @@ def set_api_key(self, username, api_key):
command = "UPDATE user SET api_key = %s WHERE username = %s"
self.cursor.execute(command, (api_key, username))
self.conn.commit()

def set_api_roles(self, username, api_roles):
command = "UPDATE user SET api_roles = %s WHERE username = %s"
self.cursor.execute(command, (api_roles, username))
self.conn.commit()

def check_api_key(self, api_key: str, username: str) -> bool:
command = "SELECT EXISTS (SELECT * FROM user WHERE api_key = %s AND username = %s)"
Expand All @@ -2115,6 +2120,16 @@ def check_api_key(self, api_key: str, username: str) -> bool:
return True
return False

def check_api_roles(self, username: str, roles: list):
command = "SELECT api_roles FROM user WHERE username = %s"
self.cursor.execute(command, (username, ))
result = self.cursor.fetchone()[0]
db_roles = result.split(';')
for role in roles:
if role not in db_roles:
return False, db_roles
return True, db_roles

def parse_raw_user(self, raw_user):
return models.User(id = raw_user[0],
full_name = raw_user[2] + ' ' + raw_user[3],
Expand Down
3 changes: 3 additions & 0 deletions src/frontend_celery/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ class Config(object):
MAIL_USE_SSL = False
MAIL_DEBUG = False

# misc
VCF_FILE_IMPORT_ACTIVE = False



class ProdConfig(Config):
Expand Down
104 changes: 18 additions & 86 deletions src/frontend_celery/webapp/api/api_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ..utils import *
from . import api_functions
from webapp import tasks
from webapp.variant import variant_functions

api_blueprint = Blueprint(
'api',
Expand All @@ -19,10 +20,10 @@


@api_blueprint.route('/api/v1.0/get/consensus_classification', methods=['GET'])
@accept_token
@require_api_token_permission(["read_only"])
def consensus_classification():

conn = Connection(roles = ["read_only"])
conn = get_connection() #Connection(roles = ["read_only"])

variant_id = request.args.get('variant_id')
if variant_id is None:
Expand All @@ -34,11 +35,9 @@ def consensus_classification():
variant_id = conn.get_variant_id(chrom, pos, ref, alt)

if variant_id is None:
conn.close()
abort(404, "Requested variant does not exist or missing variant information")

variant = conn.get_variant(variant_id, include_annotations = False, include_consensus = True, include_user_classifications = False, include_heredicare_classifications=False, include_automatic_classification=False, include_clinvar=False, include_consequences=False, include_assays=False, include_literature=False, include_external_ids=False)
conn.close()

v_res = api_functions.prepare_variant(variant)
mrcc = variant.get_recent_consensus_classification()
Expand All @@ -52,10 +51,10 @@ def consensus_classification():
return jsonify(result)


# DEPRECATED - DELETE - use /api/v1.0/check_variant
@api_blueprint.route('/api/v1.0/get/check_hgvs', methods = ['GET'])
@accept_token
def check_hgvs():

@api_blueprint.route('/api/v1.0/check_variant', methods = ['GET'])
@require_api_token_permission(["read_only"])
def check():
variant = {
'VID': None,
'CHROM': None,
Expand All @@ -76,40 +75,6 @@ def check_hgvs():
'canon_alt': '',
'comment': ''
}
conn = Connection(roles = ["read_only"])
status, message = tasks.map_hg38(variant, -1, conn, insert_variant = False, perform_annotation = False, external_ids = None)
conn.close()

result = {
"status": status,
"message": message
}
return jsonify(result)

# DEPRECATED - DELETE- use /api/v1.0/check_variant
@api_blueprint.route('/api/v1.0/get/check_genomic', methods = ['GET'])
@accept_token
def check_genomic():
variant = {
'VID': None,
'CHROM': None,
'POS_HG19': None,
'REF_HG19': None,
'ALT_HG19': None,
'POS_HG38': None,
'REF_HG38': None,
'ALT_HG38': None,
'REFSEQ': None,
'CHGVS': None,
'CGCHBOC': None,
'VISIBLE': 1,
'GEN': None,
'canon_chrom': '',
'canon_pos': '',
'canon_ref': '',
'canon_alt': '',
'comment': ''
}

genome_build = request.args.get('genome')
if genome_build == "GRCh38":
Expand All @@ -123,9 +88,8 @@ def check_genomic():
variant["REF_HG19"] = request.args.get('ref')
variant["ALT_HG19"] = request.args.get('alt')

conn = Connection(roles = ["read_only"])
conn = get_connection() # Connection(roles = ["read_only"])
status, message = tasks.map_hg38(variant, -1, conn, insert_variant = False, perform_annotation = False, external_ids = None)
conn.close()

result = {
"status": status,
Expand All @@ -135,48 +99,16 @@ def check_genomic():



@api_blueprint.route('/api/v1.0/check_variant', methods = ['GET'])
@accept_token
def check():
variant = {
'VID': None,
'CHROM': None,
'POS_HG19': None,
'REF_HG19': None,
'ALT_HG19': None,
'POS_HG38': None,
'REF_HG38': None,
'ALT_HG38': None,
'REFSEQ': request.args.get('transcript'),
'CHGVS': request.args.get('hgvsc'),
'CGCHBOC': None,
'VISIBLE': 1,
'GEN': request.args.get("gene"),
'canon_chrom': '',
'canon_pos': '',
'canon_ref': '',
'canon_alt': '',
'comment': ''
}

genome_build = request.args.get('genome')
if genome_build == "GRCh38":
variant["CHROM"] = request.args.get('chrom')
variant["POS_HG38"] = request.args.get('pos')
variant["REF_HG38"] = request.args.get('ref')
variant["ALT_HG38"] = request.args.get('alt')
elif genome_build == "GRCh37":
variant["CHROM"] = request.args.get('chrom')
variant["POS_HG19"] = request.args.get('pos')
variant["REF_HG19"] = request.args.get('ref')
variant["ALT_HG19"] = request.args.get('alt')
@api_blueprint.route('/api/v1.0/post/variant', methods = ['POST'])
@require_api_token_permission(["user"])
def insert_variant():
conn = get_connection()

user = conn.parse_raw_user(conn.get_user(session["user"]["user_id"]))
create_result, do_redirect = variant_functions.create_variant_from_request(request, user, conn)

return jsonify(create_result)


conn = Connection(roles = ["read_only"])
status, message = tasks.map_hg38(variant, -1, conn, insert_variant = False, perform_annotation = False, external_ids = None)
conn.close()

result = {
"status": status,
"message": message
}
return jsonify(result)
21 changes: 15 additions & 6 deletions src/frontend_celery/webapp/auth/auth_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def auth():
first_name = user_info['given_name']
last_name = user_info['family_name']
affiliation = user_info.get('affiliation')
roles = user_info['roles']


# init the session
session['user'] = user_info
Expand All @@ -66,8 +68,9 @@ def auth():
return redirect(url_for('auth.logout', auto_logout='True'))
conn = Connection(session['user']['roles'])
# this inserts only if the user is not already in the database and updates the information if the information changed (except for username this one has to stay)
conn.insert_user(username, first_name, last_name, affiliation)
user_info['user_id'] = conn.get_user_id(username)
conn.insert_user(username, first_name, last_name, affiliation, ';'.join(roles))
user_id = conn.get_user_id(username)
user_info['user_id'] = user_id
conn.close()

current_app.logger.info("User " + user_info['preferred_username'] + ' (' + user_info.get('affiliation') + ") successfully logged in.")
Expand Down Expand Up @@ -176,17 +179,23 @@ def edit_user(username):
flash('Please provide at least one role!', 'alert-danger')
return render_template('auth/edit_user.html', user = user, avail_roles = avail_roles, set_roles = set_roles)

if enabled == 'on':
enabled = True
else:
enabled = False
enabled = enabled == 'on'

resp = auth_functions.update_user_information(kc_user_id, user['username'], first_name, last_name, e_mail, affiliation, enabled)

added_roles, deleted_roles = auth_functions.get_added_and_deleted_roles(set_roles, new_roles)
resp = auth_functions.grant_roles(kc_user_id, added_roles, avail_roles)
resp = auth_functions.delete_roles(kc_user_id, deleted_roles, avail_roles)

avail_roles = auth_functions.get_roles()
new_roles = auth_functions.get_roles_of_user(kc_user_id, avail_roles)
final_new_roles = []
if enabled:
for new_role_index in new_roles:
final_new_roles.append(avail_roles[new_role_index]['name'])
conn = get_connection()
conn.set_api_roles(username, ";".join(final_new_roles))

if resp.status_code == 204:
flash('Successfully changed user information!', 'alert-success')
return redirect(url_for('auth.edit_user', username = username))
Expand Down
4 changes: 2 additions & 2 deletions src/frontend_celery/webapp/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,8 @@ def map_hg38(variant, user_id, conn:Connection, insert_variant = True, perform_a

if not was_successful and hgvs_c_valid and gene_valid:
gene_id = conn.get_gene_id_by_symbol(gene_symbol)
transcripts = conn.get_gencode_basic_transcripts(gene_id)
#transcripts = conn.get_mane_select_for_gene(gene_id)
#transcripts = conn.get_gencode_basic_transcripts(gene_id)
transcripts = conn.get_mane_select_for_gene(gene_id)

if transcripts is not None:
#print(transcripts)
Expand Down
14 changes: 14 additions & 0 deletions src/frontend_celery/webapp/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ <h4>Overview</h4>
<div class="bst bsb">
<h4>Changelog</h4>

<div class="underline">v 1.13.6 (16.08.2024)</div>
<div>
General changes:
<ul>
<li>Improved API security</li>
<li>Added variant upload and variant check through api</li>
</ul>
Bugfixes:
<ul>
<li>Fixed a visual bug when the request to start a new upload to HerediCaRe task was erroneous, but the ClinVar upload was successful</li>
</ul>
</div>


<div class="underline">v 1.13.5 (15.08.2024)</div>
<div>
Bugfixes:
Expand Down
Loading

0 comments on commit e168054

Please sign in to comment.