about summary refs log tree commit diff
path: root/gn3/api/rqtl.py
blob: 1881a4ec59f640f63ca509761bb84f2670a24a95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""Endpoints for running the rqtl cmd"""

import os
import uuid
import subprocess
from pathlib import Path

from flask import Blueprint
from flask import current_app
from flask import jsonify
from flask import request

from gn3.computations.rqtl import (
    generate_rqtl_cmd,
    process_rqtl_mapping,
    process_rqtl_pairscan,
    process_perm_output,
)
from gn3.fs_helpers import assert_path_exists, get_tmpdir

rqtl = Blueprint("rqtl", __name__)


@rqtl.route("/compute", methods=["POST"])
def compute():
    """Given at least a geno_file and pheno_file, generate and
    run the rqtl_wrapper script and return the results as JSON

    """
    genofile = request.form["geno_file"]
    phenofile = request.form["pheno_file"]
    assert_path_exists(genofile)
    assert_path_exists(phenofile)

    run_id = request.args.get("id")
    with open(
        os.path.join(current_app.config.get("TMPDIR"), f"{run_id}.txt"),
        "w+",
        encoding="utf-8",
    ):
        # TODO thos should  be refactored
        pass
    # Split kwargs by those with values and boolean ones
    # that just convert to True/False
    kwargs = ["covarstruct", "model", "method", "nperm", "scale", "control"]
    boolean_kwargs = ["addcovar", "interval", "pstrata", "pairscan"]
    all_kwargs = kwargs + boolean_kwargs

    rqtl_kwargs = {
        "geno": genofile,
        "pheno": phenofile,
        "outdir": current_app.config.get("TMPDIR"),
    }
    rqtl_bool_kwargs = []

    for kwarg in all_kwargs:
        if kwarg in request.form:
            if kwarg in kwargs:
                rqtl_kwargs[kwarg] = request.form[kwarg]
            if kwarg in boolean_kwargs:
                rqtl_bool_kwargs.append(kwarg)

    outdir = os.path.join(get_tmpdir(), "gn3")
    if not os.path.isdir(outdir):
        os.mkdir(outdir)

    rqtl_cmd = generate_rqtl_cmd(
        rqtl_wrapper_cmd=str(
            Path(__file__)
            .absolute()
            .parent.parent.parent.joinpath("scripts/rqtl_wrapper.R")
        ),
        rqtl_wrapper_kwargs=rqtl_kwargs,
        rqtl_wrapper_bool_kwargs=rqtl_bool_kwargs,
    )

    rqtl_output = {}
    #  get the stdout file
    run_id = request.args.get("id", str(uuid.uuid4()))
    if not os.path.isfile(
        os.path.join(
            current_app.config.get("TMPDIR"), "gn3", rqtl_cmd.get("output_file")
        )
    ):
        pass
    stream_ouput_file = os.path.join(current_app.config.get("TMPDIR"), f"{run_id}.txt")

    run_process(rqtl_cmd.get("rqtl_cmd").split(), stream_ouput_file, run_id)

    if "pairscan" in rqtl_bool_kwargs:
        rqtl_output["results"] = process_rqtl_pairscan(
            rqtl_cmd.get("output_file"), genofile
        )
    else:
        rqtl_output["results"] = process_rqtl_mapping(rqtl_cmd.get("output_file"))

    if int(rqtl_kwargs["nperm"]) > 0:
        # pylint: disable=C0301
        (
            rqtl_output["perm_results"],
            rqtl_output["suggestive"],
            rqtl_output["significant"],
        ) = process_perm_output(rqtl_cmd.get("output_file"))
    return jsonify(rqtl_output)


def run_process(cmd, output_file, run_id):
    """Function to execute an external process and
       capture the stdout in a file
      input:
           cmd: the command to execute as a list of args.
           output_file: abs file path to write the stdout.
           run_id: unique id to identify the process

      output:
          Dict with the results o either success or failure.
    """
    try:
        # phase: execute the  rscript cmd
        with subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        ) as process:
            for line in iter(process.stdout.readline, b""):
                # phase: capture the stdout for eaching line allowing read and write
                with open(output_file, "a+", encoding="utf-8") as file_handler:
                    file_handler.write(line.decode("utf-8"))
            process.wait()
        if process.returncode == 0:
            return {"msg": "success", "code": 0, "run_id": run_id}
        return {"msg": "Process failed",
                "code": process.returncode, "run_id": run_id}
    except subprocess.CalledProcessError as error:
        return {"msg": "Process failed",
                "error": str(error), "run_id": run_id}