main.py 8.02 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
Floreal Cabanettes's avatar
Floreal Cabanettes committed
16
from lib.mailer import Mailer
17
18

import sys
19

20
21
22
23
24
25
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")

26
27
28
29

# Init config reader:
config_reader = AppConfigReader()

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

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

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

40
# Init mail:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
41
mailer = Mailer(app)
42

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


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


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


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


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

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

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

    # Form pass
    if form_pass:
99
        # Get final job id:
100
        id_job = re.sub('[^A-Za-z0-9_\-]+', '', id_job.replace(" ", "_"))
101
102
103
104
105
106
107
108
        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:
109
110
111
        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
112
113
114
        query = Fasta(name=query_name, path=query_path, type_f=file_query_type)
        target = None
        if file_target != "":
115
116
117
            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
118
119
120
            target = Fasta(name=target_name, path=target_path, type_f=file_target_type)

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


# Status of a job
@app.route('/status/<id_job>', methods=['GET'])
def status(id_job):
    job = JobManager(id_job)
132
133
    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
134
                           menu="results")
135
136


Floreal Cabanettes's avatar
Floreal Cabanettes committed
137
# Results path
138
@app.route("/result/<id_res>", methods=['GET'])
139
def result(id_res):
Floreal Cabanettes's avatar
Floreal Cabanettes committed
140
141
142
143
144
145
146
147
    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
148
149


Floreal Cabanettes's avatar
Floreal Cabanettes committed
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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
173
# Get graph (ajax request)
174
175
176
@app.route('/get_graph', methods=['POST'])
def get_graph():
    id_f = request.form["id"]
177
    paf = os.path.join(app_data, id_f, "map.paf")
178
179
    idx1 = os.path.join(app_data, id_f, "query.idx")
    idx2 = os.path.join(app_data, id_f, "target.idx")
180

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

183
184
    if paf.parsed:
        res = paf.get_d3js_data()
185
        res["success"] = True
186
187
188
189
190
191
        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
192
193
194
    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")
195
    paf = Paf(paf, idx1, idx2, False)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
196
197
198
199
200
201
    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
202

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

204
205
@app.route("/upload", methods=['POST'])
def upload():
206
207
208
209
210
211
212
213
214
    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
215
            filename = Functions.get_valid_uploaded_filename(filename, folder_files)
216
217
            mime_type = files.content_type

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

            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
231
                result = UploadFile(name=filename, type_f=mime_type, size=size)
232
233
234
235
236

            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."})
237
238


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