diff options
| author | Alexander Kabui | 2024-12-04 09:21:52 +0300 |
|---|---|---|
| committer | GitHub | 2024-12-04 09:21:52 +0300 |
| commit | 5c6d3befdea924eb9f6df3d2d4199a5dd8be9522 (patch) | |
| tree | b24aa3e4058bffe23f208b883cb05c15f1a49832 | |
| parent | fb02b03a32a20572ea0057248fcfbc2a9ac21e6b (diff) | |
| parent | 09b6934461cc14d9c957baa2a51b031ea4c5c7bc (diff) | |
| download | genenetwork3-5c6d3befdea924eb9f6df3d2d4199a5dd8be9522.tar.gz | |
Merge pull request #203 from genenetwork/feature/rqtl2-endpoints-with-files
Feature/rqtl2 endpoints with files
| -rw-r--r-- | gn3/api/general.py | 29 | ||||
| -rw-r--r-- | gn3/api/rqtl2.py | 61 | ||||
| -rw-r--r-- | gn3/app.py | 2 |
3 files changed, 92 insertions, 0 deletions
diff --git a/gn3/api/general.py b/gn3/api/general.py index b984361..fcaf21b 100644 --- a/gn3/api/general.py +++ b/gn3/api/general.py @@ -1,5 +1,6 @@ """General API endpoints. Put endpoints that can't be grouped together nicely here.""" +import os from flask import Blueprint from flask import current_app from flask import jsonify @@ -68,3 +69,31 @@ def run_r_qtl(geno_filestr, pheno_filestr): cmd = (f"Rscript {rqtl_wrapper} " f"{geno_filestr} {pheno_filestr}") return jsonify(run_cmd(cmd)), 201 + + +@general.route("/stream ", methods=["GET"]) +def stream(): + """ + This endpoint streams the stdout content from a file. + It expects an identifier to be passed as a query parameter. + Example: `/stream?id=<identifier>` + The `id` will be used to locate the corresponding file. + You can also pass an optional `peak` parameter + to specify the file position to start reading from. + Query Parameters: + - `id` (required): The identifier used to locate the file. + - `peak` (optional): The position in the file to start reading from. + Returns: + - dict with data(stdout), run_id unique id for file, + pointer last read position for file + """ + run_id = request.args.get("id", "") + output_file = os.path.join(current_app.config.get("TMPDIR"), + f"{run_id}.txt") + seek_position = int(request.args.get("peak", 0)) + with open(output_file) as file_handler: + # read to the last position default to 0 + file_handler.seek(seek_position) + return jsonify({"data": file_handler.readlines(), + "run_id": run_id, + "pointer": file_handler.tell()}) diff --git a/gn3/api/rqtl2.py b/gn3/api/rqtl2.py new file mode 100644 index 0000000..ffa8755 --- /dev/null +++ b/gn3/api/rqtl2.py @@ -0,0 +1,61 @@ +""" File contains endpoints for rqlt2""" + +import subprocess +import uuid +import os +from flask import current_app +from flask import jsonify +from flask import Blueprint +from flask import request + +rqtl2 = Blueprint("rqtl2", __name__) + +@rqtl2.route("/compute", methods=["GET"]) +def compute(): + """Endpoint for computing QTL analysis using R/QTL2""" + # get the run id to act as file identifier default to output + run_id = request.args.get("id", "output") + output_file = os.path.join(current_app.config.get("TMPDIR"), + f"{run_id}.txt") + # this should be computed locally not via files + rscript_cmd = ( + f"Rscript ./scripts/rqtl2_wrapper.R " + f"-i /home/kabui/r_playground/meta_grav.json " + f"-d /home/kabui/r_playground " + f"-o /home/kabui/r_playground/rqtl_output.json " + f"--nperm 100 --threshold 1 --cores 0" + ) + process = subprocess.Popen( + rscript_cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + for line in iter(process.stdout.readline, b""): + # these allow endpoint stream to read the file since + # no read and write file same tiem + with open(output_file, "a+") as file_handler: + file_handler.write(line.decode("utf-8")) + process.stdout.close() + process.wait() + if process.returncode == 0: + return jsonify({"msg": "success", + "results": "file_here", + "run_id": run_id}) + return jsonify({"msg": "fail", + "error": "Process failed", + "run_id": run_id}) + + +@rqtl2.route("/stream/<identifier>", methods=["GET"]) +def stream(identifier="output"): + """ This endpoints streams stdout from a file expects + the indetifier to be the file """ + output_file = os.path.join(current_app.config.get("TMPDIR"), + f"{identifier}.txt") + seek_position = int(request.args.get("peak", 0)) + with open(output_file) as file_handler: + # read to the last position default to 0 + file_handler.seek(seek_position) + return jsonify({"data": file_handler.readlines(), + "run_id": identifier, + "pointer": file_handler.tell()}) diff --git a/gn3/app.py b/gn3/app.py index eb9e5d3..0bec32a 100644 --- a/gn3/app.py +++ b/gn3/app.py @@ -27,6 +27,7 @@ from gn3.api.search import search from gn3.api.metadata import metadata from gn3.api.sampledata import sampledata from gn3.api.llm import gnqa +from gn3.api.rqtl2 import rqtl2 from gn3.case_attributes import caseattr @@ -107,6 +108,7 @@ def create_app(config: Union[Dict, str, None] = None) -> Flask: app.register_blueprint(sampledata, url_prefix="/api/sampledata") app.register_blueprint(caseattr, url_prefix="/api/case-attribute") app.register_blueprint(gnqa, url_prefix="/api/llm") + app.register_blueprint(rqtl2, url_prefix="/api/rqtl2") register_error_handlers(app) return app |
