From 2ab012a50e915ff17d7a303f5bd7e400237ebde8 Mon Sep 17 00:00:00 2001 From: asherpasha Date: Mon, 26 Dec 2022 14:21:15 -0500 Subject: [PATCH 1/2] Removed BAR Summarizations. --- api/__init__.py | 57 +- api/models/summarization.py | 32 - api/resources/api_manager.py | 201 ------ .../summarization_gene_expression.py | 601 ------------------ api/utils/api_manager_utils.py | 80 --- .../summarization_gene_expression_utils.py | 63 -- config/BAR_API.cfg | 22 - config/databases/summarization.sql | 122 ---- requirements.txt | 37 +- tests/data/test_key.bin | 1 - tests/resources/test_api_manager.py | 111 ---- .../test_summarization_gene_expression.py | 61 -- 12 files changed, 21 insertions(+), 1367 deletions(-) delete mode 100644 api/models/summarization.py delete mode 100644 api/resources/api_manager.py delete mode 100644 api/resources/summarization_gene_expression.py delete mode 100644 api/utils/api_manager_utils.py delete mode 100644 api/utils/summarization_gene_expression_utils.py delete mode 100644 config/databases/summarization.sql delete mode 100644 tests/data/test_key.bin delete mode 100644 tests/resources/test_api_manager.py delete mode 100644 tests/resources/test_summarization_gene_expression.py diff --git a/api/__init__.py b/api/__init__.py index 53eeb003..f0df4d68 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -19,39 +19,10 @@ def create_app(): # Travis print("We are now loading configuration.") bar_app.config.from_pyfile(os.getcwd() + "/config/BAR_API.cfg", silent=True) - if bar_app.config.get("ADMIN_ENCRYPT_KEY"): - os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get( - "TEST_ADMIN_ENCRYPT_KEY" - ) - if bar_app.config.get("ADMIN_PASSWORD_FILE"): - os.environ["ADMIN_PASSWORD_FILE"] = bar_app.config.get( - "TEST_ADMIN_PASSWORD_FILE" - ) - if bar_app.config.get("ADMIN_EMAIL"): - os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL") - if bar_app.config.get("EMAIL_PASS_KEY"): - os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY") - if bar_app.config.get("EMAIL_PASS_FILE"): - os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE") + elif os.environ.get("BAR"): # The BAR bar_app.config.from_pyfile(os.environ.get("BAR_API_PATH"), silent=True) - if bar_app.config.get("ADMIN_EMAIL"): - os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL") - if bar_app.config.get("EMAIL_PASS_KEY"): - os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY") - if bar_app.config.get("EMAIL_PASS_FILE"): - os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE") - if bar_app.config.get("ADMIN_ENCRYPT_KEY"): - os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get("ADMIN_ENCRYPT_KEY") - if bar_app.config.get("ADMIN_PASSWORD_FILE"): - os.environ["ADMIN_PASSWORD_FILE"] = bar_app.config.get( - "ADMIN_PASSWORD_FILE" - ) - if bar_app.config.get("DRIVE_LIST_KEY"): - os.environ["DRIVE_LIST_KEY"] = bar_app.config.get("DRIVE_LIST_KEY") - if bar_app.config.get("DRIVE_LIST_FILE"): - os.environ["DRIVE_LIST_FILE"] = bar_app.config.get("DRIVE_LIST_FILE") else: # The localhost bar_app.config.from_pyfile( @@ -59,22 +30,6 @@ def create_app(): ) # Load environment variables - if bar_app.config.get("ADMIN_ENCRYPT_KEY"): - os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get("ADMIN_ENCRYPT_KEY") - if bar_app.config.get("ADMIN_PASSWORD_FILE"): - os.environ["ADMIN_PASSWORD_FILE"] = bar_app.config.get( - "ADMIN_PASSWORD_FILE" - ) - if bar_app.config.get("ADMIN_EMAIL"): - os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL") - if bar_app.config.get("EMAIL_PASS_KEY"): - os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY") - if bar_app.config.get("EMAIL_PASS_FILE"): - os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE") - if bar_app.config.get("DRIVE_LIST_KEY"): - os.environ["DRIVE_LIST_KEY"] = bar_app.config.get("DRIVE_LIST_KEY") - if bar_app.config.get("DRIVE_LIST_FILE"): - os.environ["DRIVE_LIST_FILE"] = bar_app.config.get("DRIVE_LIST_FILE") if bar_app.config.get("PHENIX"): os.environ["PHENIX"] = bar_app.config.get("PHENIX") if bar_app.config.get("PHENIX_VERSION"): @@ -85,7 +40,6 @@ def create_app(): ) # Initialize the databases - annotations_lookup_db.init_app(bar_app) eplant2_db.init_app(bar_app) eplant_poplar_db.init_app(bar_app) eplant_soybean_db.init_app(bar_app) @@ -96,7 +50,6 @@ def create_app(): tomato_nssnp_db.init_app(bar_app) tomato_seq_db.init_app(bar_app) single_cell_db.init_app(bar_app) - summarization_db.init_app(bar_app) rice_interactions_db.init_app(bar_app) # Initialize the cache @@ -115,10 +68,7 @@ def create_app(): # Now add routes from api.resources.gene_information import gene_information from api.resources.rnaseq_gene_expression import rnaseq_gene_expression - from api.resources.summarization_gene_expression import ( - summarization_gene_expression, - ) - from api.resources.api_manager import api_manager + from api.resources.proxy import bar_proxy from api.resources.thalemine import thalemine from api.resources.snps import snps @@ -130,8 +80,6 @@ def create_app(): bar_api.add_namespace(gene_information) bar_api.add_namespace(rnaseq_gene_expression) - bar_api.add_namespace(summarization_gene_expression) - bar_api.add_namespace(api_manager) bar_api.add_namespace(bar_proxy) bar_api.add_namespace(thalemine) bar_api.add_namespace(snps) @@ -158,7 +106,6 @@ def create_app(): soybean_nssnp_db = SQLAlchemy(metadata=MetaData()) tomato_seq_db = SQLAlchemy(metadata=MetaData()) single_cell_db = SQLAlchemy(metadata=MetaData()) -summarization_db = SQLAlchemy(metadata=MetaData()) rice_interactions_db = SQLAlchemy(metadata=MetaData()) # Initialize Redis diff --git a/api/models/summarization.py b/api/models/summarization.py deleted file mode 100644 index 18c58f62..00000000 --- a/api/models/summarization.py +++ /dev/null @@ -1,32 +0,0 @@ -from api import summarization_db as db - - -class SummarizationGeneExpression(db.Model): - __bind_key__ = "summarization" - __tablename__ = "summarization" - proj_id = db.Column(db.String(5), nullable=False) - sample_id = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue()) - data_probeset_id = db.Column(db.String(24), primary_key=True) - data_signal = db.Column(db.Integer, primary_key=True) - data_bot_id = db.Column(db.String(32), primary_key=True) - - -class Requests(db.Model): - __bind_key__ = "summarization" - __tablename__ = "requests" - first_name = db.Column(db.String(32), index=True) - last_name = db.Column(db.String(32), index=True) - email = db.Column(db.String(120), index=True, primary_key=True, unique=True) - notes = db.Column(db.String(500), index=True) - - -class Users(db.Model): - __bind_key__ = "summarization" - __tablename__ = "users" - first_name = db.Column(db.String(32), index=True) - last_name = db.Column(db.String(32), index=True) - email = db.Column(db.String(120), index=True, unique=True) - api_key = db.Column(db.String(120), primary_key=True) - status = db.Column(db.String(32), index=True) - date_added = db.Column(db.Date, nullable=False) - uses_left = db.Column(db.Integer, index=True, default=25) diff --git a/api/resources/api_manager.py b/api/resources/api_manager.py deleted file mode 100644 index 64836729..00000000 --- a/api/resources/api_manager.py +++ /dev/null @@ -1,201 +0,0 @@ -from api import summarization_db as db -from api.models.summarization import Users, Requests -from api.utils.bar_utils import BARUtils -from api.utils.api_manager_utils import ApiManagerUtils -from flask import request -from flask_restx import Namespace, Resource -from datetime import datetime -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import object_mapper -import uuid -import pandas - -CAPTCHA_KEY_FILE = "/home/bpereira/data/bar.summarization/key" -api_manager = Namespace("API Manager", description="API Manager", path="/api_manager") - - -@api_manager.route("/validate_admin_password", methods=["POST"], doc=False) -class ApiManagerValidate(Resource): - def post(self): - """Verify admin password""" - response_json = request.get_json() - password = response_json["password"] - if ApiManagerUtils.check_admin_pass(password): - return BARUtils.success_exit(True) - else: - return BARUtils.success_exit(False) - - -@api_manager.route("/validate_api_key", methods=["POST"], doc=False) -class ApiManagerValidateKey(Resource): - def post(self): - """Verify if an API key provided by the user exists in the database""" - tbl = Users() - json = request.get_json() - key = json["key"] - try: - row = tbl.query.filter_by(api_key=key).first() - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - - if row is None: - return BARUtils.error_exit("API key not found"), 404 - else: - if row.uses_left > 0: - return BARUtils.success_exit(True) - else: - return BARUtils.error_exit("API key expired"), 401 - - -@api_manager.route("/request", methods=["POST"], doc=False) -class ApiManagerRequest(Resource): - def post(self): - captchaVal = request.headers.get("captchaVal") - if ApiManagerUtils.validate_captcha(captchaVal): - response_json = request.get_json() - df = pandas.DataFrame.from_records([response_json]) - con = db.get_engine(bind="summarization") - try: - reqs = Requests() - users = Users() - row_req = reqs.query.filter_by(email=df.email[0]).first() - row_users = users.query.filter_by(email=df.email[0]).first() - - if row_req is None and row_users is None: - df.to_sql("requests", con, if_exists="append", index=False) - subject = "[Bio-Analytic Resource] New API key request" - text = """\ - There is a new API key request. - You can approve or reject it at http://bar.utoronto.ca/~bpereira/webservices/bar-request-manager/build/index.html - """ - ApiManagerUtils.send_email_notification(subject, text) - return BARUtils.success_exit("Data added") - else: - return BARUtils.error_exit("E-mail already in use"), 409 - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - else: - return BARUtils.error_exit("Failed Captcha verification") - - -@api_manager.route("/get_pending_requests", methods=["POST"], doc=False) -class ApiManagerGetPending(Resource): - def post(self): - """Returns list of pending requests from the database""" - response_json = request.get_json() - password = response_json["password"] - if ApiManagerUtils.check_admin_pass(password): - table = Requests() - values = [] - try: - rows = table.query.filter_by().all() - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [ - values.append( - { - "first_name": row.first_name, - "last_name": row.last_name, - "email": row.email, - "notes": row.notes, - } - ) - for row in rows - ] - return BARUtils.success_exit(values) - else: - return BARUtils.error_exit("Forbidden"), 403 - - -@api_manager.route("/reject_request", methods=["POST"], doc=False) -class ApiManagerRejectRequest(Resource): - def post(self): - """Delete a request from the database""" - response_json = request.get_json() - password = response_json["password"] - if ApiManagerUtils.check_admin_pass(password): - response_json = request.get_json() - table = Requests() - try: - el = table.query.filter_by(email=response_json["email"]).one() - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - db.session.delete(el) - db.session.commit() - # table.query.filter_by(email=response_json['email']).delete() - return BARUtils.success_exit(True) - else: - return BARUtils.error_exit("Forbidden"), 403 - - -@api_manager.route("/approve_request", methods=["POST"], doc=False) -class ApiManagerApproveRequest(Resource): - def post(self): - """Approve a request from the database and add it to the Users table""" - response_json = request.get_json() - email = response_json["email"] - password = response_json["password"] - if ApiManagerUtils.check_admin_pass(password): - table = Requests() - values = [] - try: - rows = table.query.filter_by(email=email).all() - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - key = uuid.uuid4().hex - [ - values.append( - { - "first_name": row.first_name, - "last_name": row.last_name, - "email": row.email, - "date_added": datetime.now(), - "status": "user", - "api_key": key, - "uses_left": 100, - } - ) - for row in rows - ] - df = pandas.DataFrame.from_records([values[0]]) - con = db.get_engine(bind="summarization") - try: - df.to_sql("users", con, if_exists="append", index=False) - - class UserTable(db.Model): - __bind_key__ = "summarization" - __tablename__ = key - __table_args__ = ( - db.Index( - "data_probeset_id", - "data_probeset_id", - "data_bot_id", - "data_signal", - ), - ) - - proj_id = db.Column(db.String(5), nullable=False) - sample_id = db.Column( - db.Integer, nullable=False, server_default=db.FetchedValue() - ) - data_probeset_id = db.Column( - db.String(24), nullable=False, primary_key=True - ) - data_signal = db.Column( - db.Float, server_default=db.FetchedValue(), primary_key=True - ) - data_bot_id = db.Column( - db.String(32), nullable=False, primary_key=True - ) - - UserTable.__table__.create( - db.session().get_bind(object_mapper(UserTable())), checkfirst=True - ) - el = table.query.filter_by(email=email).one() - db.session.delete(el) - db.session.commit() - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - return BARUtils.success_exit(key) - else: - return BARUtils.error_exit("Forbidden"), 403 diff --git a/api/resources/summarization_gene_expression.py b/api/resources/summarization_gene_expression.py deleted file mode 100644 index 46962bdd..00000000 --- a/api/resources/summarization_gene_expression.py +++ /dev/null @@ -1,601 +0,0 @@ -import requests -import json as jsonlib -import tempfile -import os -import pandas -from api import summarization_db as db -from api import limiter -from flask import request, send_file -from werkzeug.utils import secure_filename -from api.utils.bar_utils import BARUtils -from flask_restx import Namespace, Resource -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.inspection import inspect -from scour.scour import scourString -from math import floor -from cryptography.fernet import Fernet -from api.utils.summarization_gene_expression_utils import ( - SummarizationGeneExpressionUtils, -) - - -DATA_FOLDER = "/home/bpereira/data/summarization-data" -# DATA_FOLDER = '/windir/c/Users/Bruno/Documents/SummarizationCache' -SUMMARIZATION_FILES_PATH = "/home/barapps/cromwell/summarization" -CROMWELL_URL = "http://localhost:3020" -GTF_DICT = { - "Hsapiens": "./data/hg38.ensGene.gtf", - "Athaliana": "./data/Araport11_GFF3_genes_transposons.201606.gtf", - "Mmusculus": "./data/GCF_000001635.27_GRCm39_genomic.gtf", -} - -summarization_gene_expression = Namespace( - "Summarization Gene Expression", - description="Gene Expression data from the BAR's summarization procedure", - path="/summarization_gene_expression", -) - - -@summarization_gene_expression.route("/summarize", methods=["POST"]) -class SummarizationGeneExpressionSummarize(Resource): - decorators = [ - limiter.limit("1/minute") - ] # Limit to 1 per minute using Flask limiter - - def post(self): - """Takes a Google Drive folder ID (containing BAM files) and submits them to the Cromwell server for summarization""" - json = request.get_json() - key = request.headers.get("X-Api-Key") - species = json["species"] - email = json["email"] - aliases = json["aliases"] - csv_email = json["csvEmail"] - if json["overwrite"] is True: - overwrite = "append" - else: - overwrite = "replace" - gtf = GTF_DICT[species] - if SummarizationGeneExpressionUtils.decrement_uses(key): - inputs = { - "geneSummarization.summarizeGenesScript": "./summarize_genes.R", - "geneSummarization.downloadFilesScript": "./downloadDriveFiles.py", - "geneSummarization.chrsScript": "./chrs.py", - "geneSummarization.folderId": json["folderId"], - "geneSummarization.credentials": "./data/credentials.json", - "geneSummarization.token": "./data/token.pickle", - "geneSummarization.species": species, - "geneSummarization.gtf": gtf, - "geneSummarization.aliases": str(aliases), - "geneSummarization.id": key, - "geneSummarization.pairedEndScript": "./paired.sh", - "geneSummarization.insertDataScript": "./insertData.py", - "geneSummarization.barEmailScript": "./bar_email.py", - "geneSummarization.email": email, - "geneSummarization.csvEmail": csv_email, - "geneSummarization.overwrite": overwrite, - } - # Send request to Cromwell - path = os.path.join(SUMMARIZATION_FILES_PATH, "rpkm.wdl") - file = tempfile.TemporaryFile(mode="w+") - file.write(jsonlib.dumps(inputs)) - file.seek(0) - files = { - "workflowSource": ("rpkm.wdl", open(path, "rb")), - "workflowInputs": ("rpkm_inputs.json", file.read()), - } - id_and_status = requests.post( - CROMWELL_URL + "/api/workflows/v1", files=files - ) - id_and_status = id_and_status.json() - file.close() - gkey = os.environ.get("DRIVE_LIST_KEY") - cipher_suite = Fernet(gkey) - with open(os.environ.get("DRIVE_LIST_FILE"), "rb") as f: - for line in f: - encrypted_key = line - uncipher_text = cipher_suite.decrypt(encrypted_key) - plain_text_gkey = bytes(uncipher_text).decode("utf-8") - r = requests.get( - "https://www.googleapis.com/drive/v3/files?corpora=user&includeItemsFromAllDrives=true&q=%27" - + json["folderId"] - + "%27%20in%20parents&supportsAllDrives=true&key=" - + plain_text_gkey - ) - # Return ID for future accessing - if r.status_code == 200: - fs = [x["name"] for x in r.json()["files"] if ".bam" in x["name"]] - else: - fs = r.status_code - return BARUtils.success_exit((id_and_status["id"], fs)), 200 - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route( - "/progress/", methods=["GET"], doc=False -) -class SummarizationGeneExpressionProgress(Resource): - @summarization_gene_expression.param("job_id", _in="path", default="") - def get(self, job_id): - """Get progress of a job given its ID""" - progress = requests.get( - CROMWELL_URL + "/api/workflows/v1/" + job_id + "/status" - ) - if progress.status_code == 200: - return BARUtils.success_exit(progress.status), 200 - else: - return BARUtils.error_exit(progress.status_code) - - -@summarization_gene_expression.route("/user", methods=["GET"], doc=False) -class SummarizationGeneExpressionUser(Resource): - def get(self): - """Get a user's details from the server""" - key = request.headers.get("X-Api-Key") - tbl = SummarizationGeneExpressionUtils.get_table_object("users") - con = db.get_engine(bind="summarization") - values = [] - - validated_key = SummarizationGeneExpressionUtils.validated_api_key(key) - - try: - rows = con.execute(db.select("*").where(tbl.c.api_key == validated_key)) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [ - values.append( - [ - row.first_name, - row.last_name, - row.email, - ] - ) - for row in rows - ] - return BARUtils.success_exit(values) - - -@summarization_gene_expression.route("/tsv_upload", methods=["POST"], doc=False) -class SummarizationGeneExpressionTsvUpload(Resource): - decorators = [limiter.limit("1/minute")] - - def post(self): - """Takes a TSV file from Kallisto converts to RPKM""" - key = request.headers.get("X-Api-Key") - overwrite = request.form.get("overwrite") - email = request.form.get("email") - if overwrite == "true": - overwrite = "replace" - else: - overwrite = "append" - # Create folder for user data if it doesn't exist - files = request.files.items() - for i in files: - filename = secure_filename(i[0]) - dir_name = os.path.join("/DATA/users/www-data/", secure_filename(key)) - if not os.path.exists(dir_name): - os.makedirs(dir_name) - i[1].save(os.path.join(dir_name, filename)) - if SummarizationGeneExpressionUtils.decrement_uses(key): - inputs = ( - """ - { - "tsvUpload.insertDataScript": "./insertData.py", - "tsvUpload.conversionScript": "./kallistoToRpkm.R", - "tsvUpload.id": """ - + key - + """, - "tsvUpload.tsv": """ - + str( - [ - os.path.join("/DATA/users/www-data/", secure_filename(key), x) - for x in os.listdir(dir_name) - ] - ) - + """, - "tsvUpload.overwrite": """ - + overwrite - + """, - "tsvUpload.email": """ - + email - + """ - } - """ - ) - path = os.path.join(SUMMARIZATION_FILES_PATH, "tsvUpload.wdl") - files = { - "workflowSource": ("tsvUpload.wdl", open(path, "rb")), - "workflowInputs": ("rpkm_inputs.json", inputs), - } - requests.post(CROMWELL_URL + "/api/workflows/v1", files=files) - return BARUtils.success_exit(key) - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route("/csv_upload", methods=["POST"], doc=False) -class SummarizationGeneExpressionCsvUpload(Resource): - decorators = [limiter.limit("1/minute")] - - def post(self): - """Takes a CSV file containing expression data and inserts the data into the database""" - if "file" not in request.files: - return BARUtils.error_exit("No file attached"), 400 - file = request.files["file"] - if file: - filename = secure_filename(file.filename) - key = request.headers.get("X-Api-Key") - overwrite = request.form.get("overwrite") - email = request.form.get("email") - if overwrite == "true": - overwrite = "replace" - else: - overwrite = "append" - dir_name = os.path.join("/DATA/users/www-data/", secure_filename(key)) - if not os.path.exists(dir_name): - os.makedirs(dir_name) - file.save(os.path.join(dir_name, secure_filename(filename))) - if SummarizationGeneExpressionUtils.decrement_uses(key): - inputs = ( - """ - { - "csvUpload.insertDataScript": "./insertData.py", - "csvUpload.id": """ - + key - + """, - "csvUpload.csv": """ - + os.path.join(dir_name, filename) - + """, - "csvUpload.overwrite": """ - + overwrite - + """, - "csvUpload.email": """ - + email - + """ - } - """ - ) - path = os.path.join(SUMMARIZATION_FILES_PATH, "csvUpload.wdl") - files = { - "workflowSource": ("csvUpload.wdl", open(path, "rb")), - "workflowInputs": ("rpkm_inputs.json", inputs), - } - requests.post(CROMWELL_URL + "/api/workflows/v1", files=files) - return BARUtils.success_exit(key) - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route("/insert", methods=["POST"], doc=False) -class SummarizationGeneExpressionInsert(Resource): - def post(self): - """This function adds a CSV's data to the database. This is only called by the Cromwell server after receiving the user's file.""" - if request.remote_addr != "127.0.0.1": - return BARUtils.error_exit("Forbidden"), 403 - - key = request.headers.get("X-Api-Key") - if SummarizationGeneExpressionUtils.decrement_uses(key): - csv = request.get_json()["csv"] - db_id = request.get_json()["uid"] - df = pandas.read_csv(csv) - db_id = db_id.split(".")[0] - df = df.melt( - id_vars=["data_probeset_id"], - var_name="data_bot_id", - value_name="data_signal", - ) - db_id = db_id.split("/")[len(db_id.split("/")) - 1] - con = db.get_engine(bind="summarization") - df.to_sql(db_id, con, if_exists="append", index=True) - return BARUtils.success_exit("Success") - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route( - "/value//", defaults={"sample": ""} -) -@summarization_gene_expression.route( - "/value///", methods=["GET"] -) -class SummarizationGeneExpressionValue(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - @summarization_gene_expression.param("sample", _in="path", default="") - @summarization_gene_expression.param("gene", _in="path", default="At1g01010") - def get(self, table_id, sample, gene): - """Returns the value for a given gene and sample. If no sample is given returns all values for that gene""" - if not BARUtils.is_arabidopsis_gene_valid(gene): - return BARUtils.success_exit("Invalid gene ID"), 400 - else: - key = request.headers.get("X-Api-Key") - if SummarizationGeneExpressionUtils.decrement_uses(key): - con = db.get_engine(bind="summarization") - tbl = SummarizationGeneExpressionUtils.get_table_object(table_id) - if sample == "": - values = {} - try: - rows = con.execute( - tbl.select(tbl.c.data_signal).where( - tbl.c.data_probeset_id == gene - ) - ) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - for row in rows: - values.update({str(row.data_bot_id): float(row.data_signal)}) - else: - values = [] - try: - rows = con.execute( - tbl.select(tbl.c.data_signal) - .where(tbl.c.data_bot_id == sample) - .where(tbl.c.data_probeset_id == gene) - ) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [values.append(row.Value) for row in rows] - return BARUtils.success_exit(values) - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route("/samples/") -class SummarizationGeneExpressionSamples(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - def get(self, table_id=""): - """Returns the list of samples in the table with the given ID""" - con = db.get_engine(bind="summarization") - tbl = SummarizationGeneExpressionUtils.get_table_object(table_id) - values = [] - try: - rows = con.execute(db.select([tbl.c.data_bot_id]).distinct()) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [values.append(row.data_bot_id) for row in rows] - return BARUtils.success_exit(values) - - -@summarization_gene_expression.route("/genes/") -class SummarizationGeneExpressionGenes(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - def get(self, table_id=""): - """Returns the list of genes in the table with the given ID""" - key = request.headers.get("x-api-key") - if SummarizationGeneExpressionUtils.decrement_uses(key): - con = db.get_engine(bind="summarization") - tbl = SummarizationGeneExpressionUtils.get_table_object(table_id) - values = [] - try: - rows = con.execute(db.select([tbl.c.data_probeset_id]).distinct()) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [values.append(row.data_probeset_id) for row in rows] - return BARUtils.success_exit(values) - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route( - "/find_gene//" -) -class SummarizationGeneExpressionFindGene(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - @summarization_gene_expression.param("user_string", _in="path", default="AT1G") - def get(self, table_id="", user_string=""): - """Returns all genes that contain a given string as part of their name""" - con = db.get_engine(bind="summarization") - tbl = SummarizationGeneExpressionUtils.get_table_object(table_id) - values = [] - try: - rows = con.execute( - db.select([tbl.c.data_probeset_id]) - .where(tbl.c.data_probeset_id.contains(user_string)) - .distinct() - ) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - [values.append(row.data_probeset_id) for row in rows] - return BARUtils.success_exit(values) - - -@summarization_gene_expression.route("/table_exists/") -class SummarizationGeneExpressionTableExists(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - def get(self, table_id=""): - """Checks if a given table exists""" - con = db.get_engine(bind="summarization") - if inspect(con).has_table(table_id): - return BARUtils.success_exit(True) - else: - return BARUtils.success_exit(False) - - -@summarization_gene_expression.route("/drop_table/", doc=False) -class SummarizationGeneExpressionDropTable(Resource): - @summarization_gene_expression.param("table_id", _in="path", default="test") - def get(self, table_id=""): - """Drops the table with the given ID""" - if request.remote_addr != "127.0.0.1": - return BARUtils.error_exit("Forbidden"), 403 - tbl = SummarizationGeneExpressionUtils.get_table_object(table_id) - tbl.drop() - - -@summarization_gene_expression.route("/save", methods=["POST"], doc=False) -class SummarizationGeneExpressionSave(Resource): - def post(self): - """Saves the given file if the user has a valid API key""" - api_key = request.headers.get("x-api-key") - - validated_key = SummarizationGeneExpressionUtils.validated_api_key(api_key) - - if validated_key is None: - return BARUtils.error_exit("Invalid API key"), 403 - elif SummarizationGeneExpressionUtils.decrement_uses(validated_key): - if "file" in request.files: - file = request.files["file"] - if file.content_type == "text/json": - extension = ".json" - elif file.content_type == "image/svg+xml": - extension = ".svg" - else: - return BARUtils.error_exit("Invalid file type"), 400 - dir_name = os.path.join(DATA_FOLDER, secure_filename(validated_key)) - filename = os.path.join( - dir_name, secure_filename(file.filename), extension - ) - if not os.path.exists(dir_name): - os.makedirs(dir_name) - file.save(filename) - return BARUtils.success_exit(True) - else: - return BARUtils.error_exit("No file attached"), 400 - else: - return BARUtils.error_exit("Invalid API key"), 403 - - -@summarization_gene_expression.route("/get_file_list", methods=["POST"]) -class SummarizationGeneExpressionGetFileList(Resource): - def post(self): - """Returns a list of files stored in the user's folder""" - api_key = request.headers.get("x-api-key") - - # Check Key - validated_key = SummarizationGeneExpressionUtils.validated_api_key(api_key) - if validated_key is None: - return BARUtils.error_exit("Invalid API key"), 403 - - files = [] - sec_file = secure_filename(os.path.join(DATA_FOLDER, validated_key)) - - if os.path.exists(sec_file): - for file in os.walk(sec_file): - files.append(file[2]) - return BARUtils.success_exit(files) - else: - return BARUtils.error_exit("No folder found") - - -@summarization_gene_expression.route("/get_file/") -class SummarizationGeneExpressionGetFile(Resource): - @summarization_gene_expression.param("file_id", _in="path", default="test") - def get(self, file_id): - """Returns a specific file stored in the user's folder""" - api_key = request.headers.get("x-api-key") - - # Check key - validated_key = SummarizationGeneExpressionUtils.validated_api_key(api_key) - if validated_key is None: - return BARUtils.error_exit("Invalid API key"), 403 - - filename = os.path.join( - DATA_FOLDER, secure_filename(validated_key), secure_filename(file_id) - ) - if os.path.isfile(filename): - return send_file(filename) - else: - return BARUtils.error_exit("File not found"), 404 - - -@summarization_gene_expression.route("/clean_svg") -class SummarizationGeneExpressionCleanSvg(Resource): - def post(self): - api_key = request.headers.get("x-api-key") - - validated_key = SummarizationGeneExpressionUtils.validated_api_key(api_key) - - if validated_key is None: - return BARUtils.error_exit("Invalid API key"), 403 - elif SummarizationGeneExpressionUtils.decrement_uses(validated_key): - in_string = request.get_json()["svg"] - out_string = scourString(in_string, options={"remove_metadata": True}) - return BARUtils.success_exit(out_string) - else: - return BARUtils.error_exit("Invalid API key") - - -@summarization_gene_expression.route("/get_median/") -class SummarizationGeneExpressionGetMedian(Resource): - @summarization_gene_expression.param("gene", _in="path", default="AT1G01010") - def get(self, gene): - if request.method == "GET": - api_key = request.headers.get("x-api-key") - if api_key is None: - return BARUtils.error_exit("Invalid API key"), 403 - elif SummarizationGeneExpressionUtils.decrement_uses(api_key): - con = db.get_engine(bind="summarization") - tbl = SummarizationGeneExpressionUtils.get_table_object(api_key) - signals = [] - try: - rows = con.execute( - db.select([tbl.c.data_signal]).where( - tbl.c.data_probeset_id == gene - ) - ) - except SQLAlchemyError: - return BARUtils.error_exit("Internal server error"), 500 - # Get values for this gene - [signals.append(row.data_signal) for row in rows] - # Sort values - signals.sort() - # Get middle value(s) - if len(signals) % 2 == 0: - median = ( - float( - signals[int(len(signals) / 2) - 1] - + signals[int(len(signals) / 2)] - ) - / 2 - ) - else: - median = signals[floor(len(signals) / 2)] - # Insert as CTRL_Median - return BARUtils.success_exit(median) - return BARUtils.error_exit("Internal server error"), 500 - - -@summarization_gene_expression.route("/submit", methods=["POST"], doc=False) -class SummarizationGeneExpressionSubmit(Resource): - decorators = [limiter.limit("1/minute")] - - def post(self): - print(request.files) - svg_file = request.files.get("svg") - xml_file = request.files.get("xml") - user = request.get_json()["user"] - svg_filename = secure_filename(svg_file.filename) - xml_filename = secure_filename(xml_file.filename) - key = request.headers.get("X-Api-Key") - dir_name = os.path.join("/DATA/users/www-data/", secure_filename(key)) - if not os.path.exists(dir_name): - os.makedirs(dir_name) - svg_file.save(os.path.join(dir_name, secure_filename(svg_filename))) - xml_file.save(os.path.join(dir_name, secure_filename(xml_filename))) - if SummarizationGeneExpressionUtils.decrement_uses(key): - inputs = ( - """ - { - "finalSubmissionEmail.id": """ - + key - + """, - "finalSubmissionEmail.user": """ - + user - + """, - "finalSubmissionEmail.svg": """ - + os.path.join(dir_name, secure_filename(svg_filename)) - + """, - "finalSubmissionEmail.xml": """ - + os.path.join(dir_name, secure_filename(xml_filename)) - + """ - } - """ - ) - path = os.path.join(SUMMARIZATION_FILES_PATH, "finalSubmissionEmail.wdl") - files = { - "workflowSource": ("finalSubmissionEmail.wdl", open(path, "rb")), - "workflowInputs": ("inputs.json", inputs), - } - requests.post(CROMWELL_URL + "/api/workflows/v1", files=files) - return BARUtils.success_exit(key) - else: - return BARUtils.error_exit("Invalid API key") diff --git a/api/utils/api_manager_utils.py b/api/utils/api_manager_utils.py deleted file mode 100644 index db55a79b..00000000 --- a/api/utils/api_manager_utils.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -import requests -from cryptography.fernet import Fernet -from smtplib import SMTP_SSL -from ssl import create_default_context -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -CAPTCHA_KEY_FILE = "/home/bpereira/data/bar.summarization/key" - - -class ApiManagerUtils: - @staticmethod - def check_admin_pass(password): - # Replace below with key from script in /home/bpereira/dev/pw-key - key = os.environ.get("ADMIN_ENCRYPT_KEY") - cipher_suite = Fernet(key) - - with open(os.environ.get("ADMIN_PASSWORD_FILE"), "rb") as f: - for line in f: - encrypted_key = line - - decipher_text = cipher_suite.decrypt(encrypted_key) - plain_text_encrypted_password = bytes(decipher_text).decode("utf-8") - - if password == plain_text_encrypted_password: - return True - else: - return False - - @staticmethod - def validate_captcha(value): - """Validates a reCaptcha value using our secret token""" - if os.environ.get("BAR"): - with open(CAPTCHA_KEY_FILE, "rb") as f: - for line in f: - key = line - - if key: - ret = requests.post( - "https://www.google.com/recaptcha/api/siteverify", - data={"secret": key, "response": value}, - ) - return ret.json()["success"] - else: - return False - else: - return True - - @staticmethod - def send_email_notification(subject, msg): - if os.environ.get("BAR"): - with open(os.environ.get("ADMIN_EMAIL"), "r") as f: - for line in f: - recipient = line - - port = 465 - key = os.environ.get("EMAIL_PASS_KEY") - cipher_suite = Fernet(key) - - with open(os.environ.get("EMAIL_PASS_FILE"), "rb") as f: - for line in f: - encrypted_key = line - - decipher_text = cipher_suite.decrypt(encrypted_key) - password = bytes(decipher_text).decode("utf-8") - context = create_default_context() - smtp_server = "smtp.gmail.com" - sender_email = "bar.summarization@gmail.com" - text = msg - m_text = MIMEText(text, _subtype="plain", _charset="UTF-8") - msg = MIMEMultipart() - msg["From"] = sender_email - msg["To"] = recipient - msg["Subject"] = subject - msg.attach(m_text) - - with SMTP_SSL(smtp_server, port, context=context) as server: - server.login("bar.summarization@gmail.com", password) - server.sendmail(sender_email, recipient, msg.as_string()) diff --git a/api/utils/summarization_gene_expression_utils.py b/api/utils/summarization_gene_expression_utils.py deleted file mode 100644 index 06744222..00000000 --- a/api/utils/summarization_gene_expression_utils.py +++ /dev/null @@ -1,63 +0,0 @@ -from api import summarization_db as db -from sqlalchemy.exc import SQLAlchemyError - - -class SummarizationGeneExpressionUtils: - @staticmethod - def get_table_object(table_name): - metadata = db.MetaData() - table_object = db.Table( - table_name, - metadata, - autoload=True, - autoload_with=db.get_engine(bind="summarization"), - ) - return table_object - - @staticmethod - def validated_api_key(key): - """Checks if a given API key is in the Users database - :param key: The API key to be checked - """ - tbl = SummarizationGeneExpressionUtils.get_table_object("users") - - # The key is an alphanumeric string - key = str(key) - if not key.isalnum(): - return None - - con = db.get_engine(bind="summarization") - try: - row = con.execute( - db.select([tbl.c.uses_left]).where(tbl.c.api_key == key) - ).first() - except SQLAlchemyError: - return None - if row is None: - return None - else: - if row.uses_left > 0: - return key - else: - return None - - @staticmethod - def decrement_uses(key): - """Subtracts 1 from the uses_left column of the user whose key matches the given string - :param key: The user's API key - """ - - # The key will be valid here. - tbl = SummarizationGeneExpressionUtils.get_table_object("users") - con = db.get_engine(bind="summarization") - try: - con.execute( - db.update(tbl) - .where(tbl.c.api_key == key) - .values(uses_left=(tbl.c.uses_left - 1)) - ) - db.session.commit() - except SQLAlchemyError as e: - error = str(e.__dict__["orig"]) - return error - return True diff --git a/config/BAR_API.cfg b/config/BAR_API.cfg index 6e551e33..4ce68d01 100644 --- a/config/BAR_API.cfg +++ b/config/BAR_API.cfg @@ -13,7 +13,6 @@ SQLALCHEMY_BINDS = { 'annotations_lookup': 'mysql://root:root@localhost/annotations_lookup', 'single_cell': 'mysql://root:root@localhost/single_cell', 'eplant2': 'mysql://root:root@localhost/eplant2', - 'summarization': 'mysql://root:root@localhost/summarization', 'poplar_nssnp' : 'mysql://root:root@localhost/poplar_nssnp', 'tomato_nssnp' : 'mysql://root:root@localhost/tomato_nssnp', 'soybean_nssnp' : 'mysql://root:root@localhost/soybean_nssnp', @@ -24,24 +23,3 @@ SQLALCHEMY_BINDS = { 'tomato_sequence' : 'mysql://root:root@localhost/tomato_sequence', 'rice_interactions': 'mysql://root:root@localhost/rice_interactions' } - -## API Manager variables -ADMIN_EMAIL = '/home/bpereira/data/adminemail' -ADMIN_PASSWORD_FILE = '/home/bpereira/dev/pw-script/managerkey.bin' -ADMIN_ENCRYPT_KEY = 'fabUGnTJ3UQ4qeDJbnSMrb-tDdmt9kxLkuq3GHKdGTo=' - -EMAIL_PASS_KEY = 'DMKQpa7ndkE69rvuKiXk8P0NtyPID9puYbPRtyliHiY=' -EMAIL_PASS_FILE = '/home/bpereira/dev/pw-script/emailkey.bin' - -TEST_ADMIN_PASSWORD_FILE = './tests/data/test_key.bin' -TEST_ADMIN_ENCRYPT_KEY = 'fZ2zIiSoV3MOlwiyhjxPWomIArj6YwuDsDNP0J68ZT8=' - -# Google Captcha key -CAPTCHA_KEY_FILE = '/home/bpereira/data/bar.summarization/key' -DRIVE_LIST_KEY = 'APEFauPuOytij3c4MureR7FLQrc1I-3Dz7fO7ikJH3I=' -DRIVE_LIST_FILE = "/home/bpereira/dev/pw-script/gkey.bin" - -## Summarization Gene Expression variables -DATA_FOLDER = '/home/bpereira/dev/summarization-data' -SUMMARIZATION_FILES_PATH = '/home/bpereira/dev/gene-summarization-bar/summarization' -CROMWELL_URL = 'http://localhost:3020' diff --git a/config/databases/summarization.sql b/config/databases/summarization.sql deleted file mode 100644 index 53161344..00000000 --- a/config/databases/summarization.sql +++ /dev/null @@ -1,122 +0,0 @@ --- MySQL dump 10.17 Distrib 10.3.23-MariaDB, for debian-linux-gnu (x86_64) --- --- Host: localhost Database: summarization --- ------------------------------------------------------ --- Server version 10.3.23-MariaDB-0+deb10u1 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Current Database: `single_cell` --- - -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `summarization` /*!40100 DEFAULT CHARACTER SET latin1 */; - -USE `summarization`; - --- --- Table structure for table `bb5a52387069485486b2f4861c2826dd` --- - - -DROP TABLE IF EXISTS `bb5a52387069485486b2f4861c2826dd`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `bb5a52387069485486b2f4861c2826dd` ( - `proj_id` varchar(5) NOT NULL, - `sample_id` int(10) unsigned NOT NULL DEFAULT 0, - `data_probeset_id` varchar(24) NOT NULL, - `data_signal` float DEFAULT 0, - `data_bot_id` varchar(32) NOT NULL, - KEY `data_probeset_id` (`data_probeset_id`,`data_bot_id`,`data_signal`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `bb5a52387069485486b2f4861c2826dd` --- - -LOCK TABLES `bb5a52387069485486b2f4861c2826dd` WRITE; -/*!40000 ALTER TABLE `bb5a52387069485486b2f4861c2826dd` DISABLE KEYS */; -INSERT INTO `bb5a52387069485486b2f4861c2826dd` VALUES (1, 1, 'AT1G01010',32,'sample1'),(1, 2,'AT1G01020',546,'sample1'),(1, 3, 'AT1G01030',43,'sample1'),(1, 4, 'AT1G01010',54,'sample2'),(1, 5, 'AT1G01020',65,'sample2'),(1, 6, 'AT1G01030',123,'sample2'); -/*!40000 ALTER TABLE `bb5a52387069485486b2f4861c2826dd` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `requests` --- - -DROP TABLE IF EXISTS `requests`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `requests` ( - `first_name` text DEFAULT NULL, - `last_name` text DEFAULT NULL, - `email` text DEFAULT NULL, - `notes` text DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `requests` --- - -LOCK TABLES `requests` WRITE; -/*!40000 ALTER TABLE `requests` DISABLE KEYS */; -INSERT INTO `requests` VALUES ('Some','Request','request@gmail.com','Test notes'); -/*!40000 ALTER TABLE `requests` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users` ( - `first_name` varchar(32) DEFAULT NULL, - `last_name` varchar(32) DEFAULT NULL, - `email` varchar(120) DEFAULT NULL, - `api_key` varchar(120) NOT NULL, - `status` varchar(32) DEFAULT NULL, - `date_added` date NOT NULL, - `uses_left` int(11) DEFAULT NULL, - PRIMARY KEY (`api_key`), - UNIQUE KEY `ix_users_email` (`email`), - KEY `ix_users_status` (`status`), - KEY `ix_users_last_name` (`last_name`), - KEY `ix_users_uses_left` (`uses_left`), - KEY `ix_users_first_name` (`first_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `users` --- - -LOCK TABLES `users` WRITE; -/*!40000 ALTER TABLE `users` DISABLE KEYS */; -INSERT INTO `users` VALUES ('Test','User','test@gmail.com','bb5a52387069485486b2f4861c2826dd','user','2020-12-07',100); -/*!40000 ALTER TABLE `users` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2021-01-12 18:22:44 diff --git a/requirements.txt b/requirements.txt index 0d8bd36e..077fe6e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,47 +1,48 @@ aniso8601==9.0.1 appdirs==1.4.4 async-timeout==4.0.2 -attrs==22.1.0 -black==22.10.0 +attrs==22.2.0 +black==22.12.0 cachelib==0.9.0 -certifi==2022.9.24 +certifi==2022.12.7 cffi==1.15.1 -chardet==5.0.0 +chardet==5.1.0 charset-normalizer==2.1.1 click commonmark==0.9.1 -coverage==6.5.0 +coverage==7.0.1 cryptography==38.0.4 Deprecated==1.2.13 docopt==0.6.2 -exceptiongroup==1.0.4 +exceptiongroup==1.1.0 flake8 Flask==2.2.2 Flask-Caching==2.0.1 Flask-Cors==3.0.10 -Flask-Limiter==2.8.1 +Flask-Limiter==2.9.1 flask-marshmallow==0.14.0 flask-restx==1.0.3 -Flask-SQLAlchemy==2.5.1 +Flask-SQLAlchemy==3.0.2 greenlet==2.0.1 idna==3.4 -importlib-metadata +importlib-metadata==5.2.0 iniconfig==1.1.1 itsdangerous==2.1.2 Jinja2==3.1.2 -jsonschema==4.17.1 -limits==2.7.1 +jsonschema==4.17.3 +limits==2.8.0 MarkupSafe==2.1.1 marshmallow==3.19.0 mccabe==0.7.0 more-itertools==9.0.0 mypy-extensions==0.4.3 mysqlclient==2.1.1 -numpy==1.23.5 -packaging==21.3 +numpy==1.24.1 +ordered-set==4.1.0 +packaging==22.0 pandas==1.5.2 -pathspec==0.10.2 -platformdirs==2.5.4 +pathspec==0.10.3 +platformdirs==2.6.0 pluggy==1.0.0 py==1.11.0 pycodestyle==2.10.0 @@ -52,14 +53,14 @@ pyparsing==3.0.9 pyrsistent==0.19.2 pytest==7.2.0 python-dateutil==2.8.2 -pytz==2022.6 -redis==4.3.5 +pytz==2022.7 +redis==4.4.0 regex==2022.10.31 requests==2.28.1 rich==12.6.0 scour==0.38.2 six==1.16.0 -SQLAlchemy==1.4.44 +SQLAlchemy==1.4.45 toml==0.10.2 tomli==2.0.1 typed-ast==1.5.4 diff --git a/tests/data/test_key.bin b/tests/data/test_key.bin deleted file mode 100644 index 6673ea01..00000000 --- a/tests/data/test_key.bin +++ /dev/null @@ -1 +0,0 @@ -gAAAAABgWlCZg9FUp4CzBoXQputEo9gqlXc-ZXAEAoXMoALeZXnoPPMylXF-QJ2Njy2jsUS3-GotvHdR08BfiQhcPI7jV4ZpHA== \ No newline at end of file diff --git a/tests/resources/test_api_manager.py b/tests/resources/test_api_manager.py deleted file mode 100644 index e5ade5b6..00000000 --- a/tests/resources/test_api_manager.py +++ /dev/null @@ -1,111 +0,0 @@ -from api import app -from unittest import TestCase -import json - - -class TestIntegrations(TestCase): - # Currently, the test needs to run in order below - def setUp(self): - self.app_client = app.test_client() - - def test_0_validate_admin_password(self): - # Invalid password - response = self.app_client.post( - "/api_manager/validate_admin_password", json={"password": "abc"} - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": False} - self.assertEqual(data, expected) - - def test_1_validate_api_key(self): - # Valid API key - response = self.app_client.post( - "/api_manager/validate_api_key", - json={"key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": True} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - # Invalid API key - response = self.app_client.post( - "/api_manager/validate_api_key", - json={"key": "abc"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": False, "error": "API key not found"} - self.assertEqual(response.status_code, 404) - self.assertEqual(data, expected) - - def test_2_request(self): - # testPassword set in the test config - response = self.app_client.post( - "/api_manager/request", - json={ - "first_name": "Unit", - "last_name": "Test", - "email": "unittest@gmail.com", - "notes": "Unit test notes.", - }, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": "Data added"} - self.assertEqual(data, expected) - self.assertEqual(response.status_code, 200) - - def test_3_get_pending_requests(self): - # Password is correct - response = self.app_client.post( - "/api_manager/get_pending_requests", json={"password": "testPassword"} - ) - data = json.loads(response.get_data(as_text=True)) - expected = { - "wasSuccessful": True, - "data": [ - { - "first_name": "Some", - "last_name": "Request", - "email": "request@gmail.com", - "notes": "Test notes", - }, - { - "first_name": "Unit", - "last_name": "Test", - "email": "unittest@gmail.com", - "notes": "Unit test notes.", - }, - ], - } - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - # Password is not correct - response = self.app_client.post( - "/api_manager/get_pending_requests", json={"password": "abc"} - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": False, "error": "Forbidden"} - self.assertEqual(response.status_code, 403) - self.assertEqual(data, expected) - - def test_4_reject_request(self): - # Correct password - response = self.app_client.post( - "/api_manager/reject_request", - json={"password": "testPassword", "email": "unittest@gmail.com"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": True} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - # Incorrect password - response = self.app_client.post( - "/api_manager/reject_request", - json={"password": "abc", "email": "unittest@gmail.com"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": False, "error": "Forbidden"} - self.assertEqual(response.status_code, 403) - self.assertEqual(data, expected) diff --git a/tests/resources/test_summarization_gene_expression.py b/tests/resources/test_summarization_gene_expression.py deleted file mode 100644 index c5f9dc6b..00000000 --- a/tests/resources/test_summarization_gene_expression.py +++ /dev/null @@ -1,61 +0,0 @@ -from api import app -from unittest import TestCase -import json - - -class TestIntegrations(TestCase): - def setUp(self): - self.app_client = app.test_client() - - def test_get_gene_value(self): - response = self.app_client.get( - "/summarization_gene_expression/value/bb5a52387069485486b2f4861c2826dd/At1g01010", - headers={"x-api-key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": {"sample1": 32, "sample2": 54}} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - def test_get_samples(self): - response = self.app_client.get( - "/summarization_gene_expression/samples/bb5a52387069485486b2f4861c2826dd", - headers={"x-api-key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": ["sample1", "sample2"]} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - def test_get_genes(self): - response = self.app_client.get( - "/summarization_gene_expression/genes/bb5a52387069485486b2f4861c2826dd", - headers={"x-api-key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = { - "wasSuccessful": True, - "data": ["AT1G01010", "AT1G01020", "AT1G01030"], - } - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - def test_find_gene(self): - response = self.app_client.get( - "/summarization_gene_expression/find_gene/bb5a52387069485486b2f4861c2826dd/AT1G0101", - headers={"x-api-key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": ["AT1G01010"]} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) - - def DISABLED_test_table_exists(self): - response = self.app_client.get( - "/summarization_gene_expression/table_exists/bb5a52387069485486b2f4861c2826dd", - headers={"x-api-key": "bb5a52387069485486b2f4861c2826dd"}, - ) - data = json.loads(response.get_data(as_text=True)) - expected = {"wasSuccessful": True, "data": True} - self.assertEqual(response.status_code, 200) - self.assertEqual(data, expected) From cabcfceb8df097dd3b49f9c999b61609ea731938 Mon Sep 17 00:00:00 2001 From: asherpasha Date: Mon, 26 Dec 2022 14:40:47 -0500 Subject: [PATCH 2/2] Fixed a bug. --- api/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/__init__.py b/api/__init__.py index f0df4d68..97dd6da0 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -40,6 +40,7 @@ def create_app(): ) # Initialize the databases + annotations_lookup_db.init_app(bar_app) eplant2_db.init_app(bar_app) eplant_poplar_db.init_app(bar_app) eplant_soybean_db.init_app(bar_app)