main.py 7.99 KB
Newer Older
1
2
#!/usr/bin/env python3

Floreal Cabanettes's avatar
Floreal Cabanettes committed
3
import os
4
5
import time
import datetime
6
import shutil
7
import re
Floreal Cabanettes's avatar
Floreal Cabanettes committed
8
from flask import Flask, render_template, request, url_for, jsonify, session, Response, abort
9
from flask_mail import Mail
10
from lib.paf import Paf
11
from config_reader import AppConfigReader
12
from lib.job_manager import JobManager
13
from lib.functions import Functions, ALLOWED_EXTENSIONS
14
from lib.upload_file import UploadFile
15
from lib.Fasta import Fasta
16
17

import sys
18

19
20
21
22
23
24
app_folder = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, app_folder)
os.environ["PATH"] = os.path.join(app_folder, "bin") + ":" + os.environ["PATH"]

sqlite_file = os.path.join(app_folder, "database.sqlite")

25
26
27
28

# Init config reader:
config_reader = AppConfigReader()

29
UPLOAD_FOLDER = config_reader.get_upload_folder()
Floreal Cabanettes's avatar
Floreal Cabanettes committed
30
APP_DATA = config_reader.get_app_data()
31

Floreal Cabanettes's avatar
Floreal Cabanettes committed
32
app_title = "D-GENIES - Dotplot for Genomes Interactive, E-connected and Speedy"
Floreal Cabanettes's avatar
Floreal Cabanettes committed
33

34
# Init Flask:
35
app = Flask(__name__, static_url_path='/static')
36
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
37
app.config['SECRET_KEY'] = 'dsqdsq-255sdA-fHfg52-25Asd5'
38

39
40
41
# Init mail:
mail = Mail(app)

42
# Folder containing data:
43
44
45
app_data = config_reader.get_app_data()


Floreal Cabanettes's avatar
Floreal Cabanettes committed
46
47
48
49
50
51
@app.context_processor
def get_launched_results():
    cookie = request.cookies.get("results")
    return {"results": cookie.split("|") if cookie is not None else set()}


52
53
54
# Main
@app.route("/", methods=['GET'])
def main():
Floreal Cabanettes's avatar
Floreal Cabanettes committed
55
56
57
58
59
    return render_template("index.html", title=app_title, menu="index")


@app.route("/run", methods=['GET'])
def run():
Floreal Cabanettes's avatar
Floreal Cabanettes committed
60
    session["user_tmp_dir"] = Functions.random_string(5) + "_" + \
61
                              datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d%H%M%S')
Floreal Cabanettes's avatar
Floreal Cabanettes committed
62
    id_job = Functions.random_string(5) + "_" + datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d%H%M%S')
63
64
65
66
67
    if "id_job" in request.args:
        id_job = request.args["id_job"]
    email = ""
    if "email" in request.args:
        email = request.args["email"]
Floreal Cabanettes's avatar
Floreal Cabanettes committed
68
    return render_template("run.html", title=app_title, id_job=id_job, email=email,
69
                           menu="run", allowed_ext=ALLOWED_EXTENSIONS)
70
71
72
73
74
75
76


# Launch analysis
@app.route("/launch_analysis", methods=['POST'])
def launch_analysis():
    id_job = request.form["id_job"]
    email = request.form["email"]
77
78
79
80
    file_query = request.form["query"]
    file_query_type = request.form["query_type"]
    file_target = request.form["target"]
    file_target_type = request.form["target_type"]
81
82
83

    # Check form:
    form_pass = True
84
    errors = []
85
    if id_job == "":
86
        errors.append("Id of job not given")
87
88
89
        form_pass = False

    if email == "":
90
        errors.append("Email not given")
91
        form_pass = False
92
93
    if file_query == "":
        errors.append("No query fasta selected")
94
95
96
97
        form_pass = False

    # Form pass
    if form_pass:
98
        # Get final job id:
99
        id_job = re.sub('[^A-Za-z0-9_\-]+', '', id_job.replace(" ", "_"))
100
101
102
103
104
105
106
107
        id_job_orig = id_job
        while os.path.exists(os.path.join(app_data, id_job)):
            id_job = id_job_orig + "_2"

        folder_files = os.path.join(app_data, id_job)
        os.makedirs(folder_files)

        # Save files:
108
109
110
        query_name = os.path.splitext(file_query.replace(".gz", ""))[0] if file_query_type == "local" else None
        query_path = os.path.join(app.config["UPLOAD_FOLDER"], session["user_tmp_dir"], file_query) \
            if file_query_type == "local" else file_query
111
112
113
        query = Fasta(name=query_name, path=query_path, type_f=file_query_type)
        target = None
        if file_target != "":
114
115
116
            target_name = os.path.splitext(file_target.replace(".gz", ""))[0] if file_target_type == "local" else None
            target_path = os.path.join(app.config["UPLOAD_FOLDER"], session["user_tmp_dir"], file_target) \
                if file_target_type == "local" else file_target
117
118
119
            target = Fasta(name=target_name, path=target_path, type_f=file_target_type)

        # Launch job:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
120
        job = JobManager(id_job, email, query, target, app, mail)
121
122
        job.launch()
        return jsonify({"success": True, "redirect": url_for(".status", id_job=id_job)})
123
    else:
124
        return jsonify({"success": False, "errors": errors})
125
126
127
128
129
130


# Status of a job
@app.route('/status/<id_job>', methods=['GET'])
def status(id_job):
    job = JobManager(id_job)
131
132
    j_status, error = job.status()
    return render_template("status.html", title=app_title, status=j_status, error=error, id_job=id_job,
Floreal Cabanettes's avatar
Floreal Cabanettes committed
133
                           menu="results")
134
135


Floreal Cabanettes's avatar
Floreal Cabanettes committed
136
# Results path
137
@app.route("/result/<id_res>", methods=['GET'])
138
def result(id_res):
Floreal Cabanettes's avatar
Floreal Cabanettes committed
139
140
141
142
143
144
145
146
    my_render = render_template("results.html", title=app_title, id=id_res, menu="results", current_result=id_res)
    response = app.make_response(my_render)
    cookie = request.cookies.get("results")
    cookie = cookie.split("|") if cookie is not None else []
    if id_res not in cookie:
        cookie.insert(0, id_res)
    response.set_cookie(key="results", value="|".join(cookie), path="/")
    return response
147
148


Floreal Cabanettes's avatar
Floreal Cabanettes committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def get_file(file):  # pragma: no cover
    try:
        # Figure out how flask returns static files
        # Tried:
        # - render_template
        # - send_file
        # This should not be so non-obvious
        return open(file).read()
    except IOError as exc:
        return str(exc)


@app.route("/paf/<id_res>", methods=['GET'])
def download_paf(id_res):
    map_file = os.path.join(APP_DATA, id_res, "map.paf.sorted")
    if not os.path.exists(map_file):
        map_file = os.path.join(APP_DATA, id_res, "map.paf")
    if not os.path.exists(map_file):
        abort(404)
    content = get_file(map_file)
    return Response(content, mimetype="text/plain")


Floreal Cabanettes's avatar
Floreal Cabanettes committed
172
# Get graph (ajax request)
173
174
175
@app.route('/get_graph', methods=['POST'])
def get_graph():
    id_f = request.form["id"]
176
    paf = os.path.join(app_data, id_f, "map.paf")
177
178
    idx1 = os.path.join(app_data, id_f, "query.idx")
    idx2 = os.path.join(app_data, id_f, "target.idx")
179

180
    paf = Paf(paf, idx1, idx2)
181

182
183
    if paf.parsed:
        res = paf.get_d3js_data()
184
        res["success"] = True
185
186
187
188
189
190
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})


@app.route('/sort/<id_res>', methods=['POST'])
def sort_graph(id_res):
Floreal Cabanettes's avatar
Floreal Cabanettes committed
191
192
193
    paf = os.path.join(app_data, id_res, "map.paf")
    idx1 = os.path.join(app_data, id_res, "query.idx")
    idx2 = os.path.join(app_data, id_res, "target.idx")
194
    paf = Paf(paf, idx1, idx2, False)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
195
196
197
198
199
200
    paf.sort()
    if paf.parsed:
        res = paf.get_d3js_data()
        res["success"] = True
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})
Floreal Cabanettes's avatar
Floreal Cabanettes committed
201

Floréal Cabanettes's avatar
Floréal Cabanettes committed
202

203
204
@app.route("/upload", methods=['POST'])
def upload():
205
206
207
208
209
210
211
212
213
    if "user_tmp_dir" in session and session["user_tmp_dir"] != "":
        folder = session["user_tmp_dir"]
        files = request.files[list(request.files.keys())[0]]

        if files:
            filename = files.filename
            folder_files = os.path.join(app.config["UPLOAD_FOLDER"], folder)
            if not os.path.exists(folder_files):
                os.makedirs(folder_files)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
214
            filename = Functions.get_valid_uploaded_filename(filename, folder_files)
215
216
            mime_type = files.content_type

Floreal Cabanettes's avatar
Floreal Cabanettes committed
217
            if not Functions.allowed_file(files.filename):
218
                result = UploadFile(name=filename, type_f=mime_type, size=0, not_allowed_msg="File type not allowed")
219
                shutil.rmtree(folder_files)
220
221
222
223
224
225
226
227
228
229

            else:
                # save file to disk
                uploaded_file_path = os.path.join(folder_files, filename)
                files.save(uploaded_file_path)

                # get file size after saving
                size = os.path.getsize(uploaded_file_path)

                # return json for js call back
230
                result = UploadFile(name=filename, type_f=mime_type, size=size)
231
232
233
234
235

            return jsonify({"files": [result.get_file()], "success": "OK"})

        return jsonify({"files": [], "success": "404", "message": "No file provided"})
    return jsonify({"files": [], "success": "ERR", "message": "Session not initialized. Please refresh the page."})
236
237


Floréal Cabanettes's avatar
Floréal Cabanettes committed
238
if __name__ == '__main__':
Floreal Cabanettes's avatar
Floreal Cabanettes committed
239
    app.run()